mirror of
https://git.FreeBSD.org/src.git
synced 2025-01-27 16:39:08 +00:00
Implement vfs.nfs.iodmin (minimum number of nfsiod's) and
vfs.nfs.iodmaxidle (idle time before nfsiod's exit). Make it adaptive so that we create nfsiod's on demand and they go away after not being used for a while. The upper limit is NFS_MAXASYNCDAEMON (currently 20). More will be done here, but this is a useful checkpoint. Submitted by: Maxime Henrion <mux@qualys.com>
This commit is contained in:
parent
971730fc67
commit
117f61374c
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=89324
@ -265,6 +265,7 @@ int nfs_writerpc(struct vnode *, struct uio *, struct ucred *, int *,
|
||||
int nfs_commit(struct vnode *vp, u_quad_t offset, int cnt,
|
||||
struct ucred *cred, struct thread *td);
|
||||
int nfs_readdirrpc(struct vnode *, struct uio *, struct ucred *);
|
||||
int nfs_nfsiodnew(void);
|
||||
int nfs_asyncio(struct buf *, struct ucred *, struct thread *);
|
||||
int nfs_doio(struct buf *, struct ucred *, struct thread *);
|
||||
int nfs_readlinkrpc(struct vnode *, struct uio *, struct ucred *);
|
||||
|
@ -424,7 +424,7 @@ nfs_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred)
|
||||
/*
|
||||
* Start the read ahead(s), as required.
|
||||
*/
|
||||
if (nfs_numasync > 0 && nmp->nm_readahead > 0) {
|
||||
if (nmp->nm_readahead > 0) {
|
||||
for (nra = 0; nra < nmp->nm_readahead && nra < seqcount &&
|
||||
(off_t)(lbn + 1 + nra) * biosize < np->n_size; nra++) {
|
||||
rabn = lbn + 1 + nra;
|
||||
@ -609,7 +609,7 @@ nfs_bioread(struct vnode *vp, struct uio *uio, int ioflag, struct ucred *cred)
|
||||
* (You need the current block first, so that you have the
|
||||
* directory offset cookie of the next block.)
|
||||
*/
|
||||
if (nfs_numasync > 0 && nmp->nm_readahead > 0 &&
|
||||
if (nmp->nm_readahead > 0 &&
|
||||
(bp->b_flags & B_INVAL) == 0 &&
|
||||
(np->n_direofoffset == 0 ||
|
||||
(lbn + 1) * NFS_DIRBLKSIZ < np->n_direofoffset) &&
|
||||
@ -1117,19 +1117,12 @@ int
|
||||
nfs_asyncio(struct buf *bp, struct ucred *cred, struct thread *td)
|
||||
{
|
||||
struct nfsmount *nmp;
|
||||
int i;
|
||||
int iod;
|
||||
int gotiod;
|
||||
int slpflag = 0;
|
||||
int slptimeo = 0;
|
||||
int error;
|
||||
|
||||
/*
|
||||
* If no async daemons then return EIO to force caller to run the rpc
|
||||
* synchronously.
|
||||
*/
|
||||
if (nfs_numasync == 0)
|
||||
return (EIO);
|
||||
|
||||
nmp = VFSTONFS(bp->b_vp->v_mount);
|
||||
|
||||
/*
|
||||
@ -1150,23 +1143,21 @@ nfs_asyncio(struct buf *bp, struct ucred *cred, struct thread *td)
|
||||
/*
|
||||
* Find a free iod to process this request.
|
||||
*/
|
||||
for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
|
||||
if (nfs_iodwant[i]) {
|
||||
/*
|
||||
* Found one, so wake it up and tell it which
|
||||
* mount to process.
|
||||
*/
|
||||
NFS_DPF(ASYNCIO,
|
||||
("nfs_asyncio: waking iod %d for mount %p\n",
|
||||
i, nmp));
|
||||
nfs_iodwant[i] = (struct proc *)0;
|
||||
nfs_iodmount[i] = nmp;
|
||||
nmp->nm_bufqiods++;
|
||||
wakeup((caddr_t)&nfs_iodwant[i]);
|
||||
for (iod = 0; iod < NFS_MAXASYNCDAEMON; iod++)
|
||||
if (nfs_iodwant[iod]) {
|
||||
gotiod = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
/*
|
||||
* Try to create one if none are free.
|
||||
*/
|
||||
if (!gotiod) {
|
||||
iod = nfs_nfsiodnew();
|
||||
if (iod != -1)
|
||||
gotiod = TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* If none are free, we may already have an iod working on this mount
|
||||
* point. If so, it will process our request.
|
||||
@ -1185,6 +1176,17 @@ nfs_asyncio(struct buf *bp, struct ucred *cred, struct thread *td)
|
||||
* the buffer.
|
||||
*/
|
||||
if (gotiod) {
|
||||
/*
|
||||
* Found one, so wake it up and tell it which
|
||||
* mount to process.
|
||||
*/
|
||||
NFS_DPF(ASYNCIO, ("nfs_asyncio: waking iod %d for mount %p\n",
|
||||
iod, nmp));
|
||||
nfs_iodwant[iod] = (struct proc *)0;
|
||||
nfs_iodmount[iod] = nmp;
|
||||
nmp->nm_bufqiods++;
|
||||
wakeup((caddr_t)&nfs_iodwant[iod]);
|
||||
|
||||
/*
|
||||
* Ensure that the queue never grows too large. We still want
|
||||
* to asynchronize so we block rather then return EIO.
|
||||
|
@ -79,25 +79,56 @@ static MALLOC_DEFINE(M_NFSSVC, "NFS srvsock", "Nfs server structure");
|
||||
|
||||
static void nfssvc_iod(void *);
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
static int nfs_asyncdaemon[NFS_MAXASYNCDAEMON];
|
||||
|
||||
SYSCTL_DECL(_vfs_nfs);
|
||||
|
||||
/* Minimum number of nfsiod kthreads to keep as spares */
|
||||
static unsigned int nfs_iodmin = 4;
|
||||
SYSCTL_INT(_vfs_nfs, OID_AUTO, iodmin, CTLFLAG_RW, &nfs_iodmin, 0, "");
|
||||
|
||||
/* Maximum number of seconds a nfsiod kthread will sleep before exiting */
|
||||
static int nfs_iodmaxidle = 120;
|
||||
SYSCTL_INT(_vfs_nfs, OID_AUTO, iodmaxidle, CTLFLAG_RW, &nfs_iodmaxidle, 0, "");
|
||||
|
||||
int
|
||||
nfs_nfsiodnew(void)
|
||||
{
|
||||
int error, i;
|
||||
int newiod;
|
||||
|
||||
newiod = -1;
|
||||
for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
|
||||
if (nfs_asyncdaemon[i] == 0) {
|
||||
nfs_asyncdaemon[i]++;
|
||||
newiod = i;
|
||||
break;
|
||||
}
|
||||
if (newiod == -1)
|
||||
return (-1);
|
||||
error = kthread_create(nfssvc_iod, nfs_asyncdaemon + i, NULL, RFHIGHPID,
|
||||
"nfsiod %d", newiod);
|
||||
if (error)
|
||||
return (-1);
|
||||
nfs_numasync++;
|
||||
return (newiod);
|
||||
}
|
||||
|
||||
static void
|
||||
nfsiod_setup(void *dummy)
|
||||
{
|
||||
int i;
|
||||
int error;
|
||||
struct proc *p;
|
||||
|
||||
for (i = 0; i < 4; i++) {
|
||||
error = kthread_create(nfssvc_iod, NULL, &p, RFHIGHPID,
|
||||
"nfsiod %d", i);
|
||||
if (error)
|
||||
panic("nfsiod_setup: kthread_create error %d", error);
|
||||
TUNABLE_INT_FETCH("vfs.nfs.iodmin", &nfs_iodmin);
|
||||
/* Silently limit the start number of nfsiod's */
|
||||
if (nfs_iodmin > NFS_MAXASYNCDAEMON)
|
||||
nfs_iodmin = NFS_MAXASYNCDAEMON;
|
||||
|
||||
for (i = 0; i < nfs_iodmin; i++) {
|
||||
error = nfs_nfsiodnew();
|
||||
if (error == -1)
|
||||
panic("nfsiod_setup: nfs_nfsiodnew failed");
|
||||
}
|
||||
}
|
||||
SYSINIT(nfsiod, SI_SUB_KTHREAD_IDLE, SI_ORDER_ANY, nfsiod_setup, NULL);
|
||||
@ -121,59 +152,47 @@ nfsclnt(struct thread *td, struct nfsclnt_args *uap)
|
||||
/*
|
||||
* Asynchronous I/O daemons for client nfs.
|
||||
* They do read-ahead and write-behind operations on the block I/O cache.
|
||||
* Never returns unless it fails or gets killed.
|
||||
* Returns if we hit the timeout defined by the iodmaxidle sysctl.
|
||||
*/
|
||||
static void
|
||||
nfssvc_iod(void *dummy)
|
||||
nfssvc_iod(void *instance)
|
||||
{
|
||||
struct buf *bp;
|
||||
int i, myiod;
|
||||
struct nfsmount *nmp;
|
||||
int myiod, timo;
|
||||
int error = 0;
|
||||
|
||||
mtx_lock(&Giant);
|
||||
/*
|
||||
* Assign my position or return error if too many already running
|
||||
*/
|
||||
myiod = -1;
|
||||
for (i = 0; i < NFS_MAXASYNCDAEMON; i++)
|
||||
if (nfs_asyncdaemon[i] == 0) {
|
||||
nfs_asyncdaemon[i]++;
|
||||
myiod = i;
|
||||
break;
|
||||
}
|
||||
if (myiod == -1)
|
||||
return /* XXX (EBUSY) */;
|
||||
nfs_numasync++;
|
||||
myiod = (int *)instance - nfs_asyncdaemon;
|
||||
/*
|
||||
* Just loop around doin our stuff until SIGKILL
|
||||
* Main loop
|
||||
*/
|
||||
for (;;) {
|
||||
while (((nmp = nfs_iodmount[myiod]) == NULL
|
||||
|| !TAILQ_FIRST(&nmp->nm_bufq))
|
||||
|| !TAILQ_FIRST(&nmp->nm_bufq))
|
||||
&& error == 0) {
|
||||
if (nmp)
|
||||
nmp->nm_bufqiods--;
|
||||
nmp->nm_bufqiods--;
|
||||
nfs_iodwant[myiod] = curthread->td_proc;
|
||||
nfs_iodmount[myiod] = NULL;
|
||||
error = tsleep((caddr_t)&nfs_iodwant[myiod],
|
||||
PWAIT | PCATCH, "nfsidl", 0);
|
||||
}
|
||||
if (error) {
|
||||
nfs_asyncdaemon[myiod] = 0;
|
||||
if (nmp)
|
||||
nmp->nm_bufqiods--;
|
||||
nfs_iodwant[myiod] = NULL;
|
||||
nfs_iodmount[myiod] = NULL;
|
||||
nfs_numasync--;
|
||||
return /* XXX (error) */;
|
||||
/*
|
||||
* Always keep at least nfs_iodmin kthreads.
|
||||
*/
|
||||
timo = (myiod < nfs_iodmin) ? 0 : nfs_iodmaxidle * hz;
|
||||
error = tsleep((caddr_t)&nfs_iodwant[myiod], PWAIT | PCATCH,
|
||||
"nfsidl", timo);
|
||||
}
|
||||
if (error)
|
||||
break;
|
||||
while ((bp = TAILQ_FIRST(&nmp->nm_bufq)) != NULL) {
|
||||
/* Take one off the front of the list */
|
||||
TAILQ_REMOVE(&nmp->nm_bufq, bp, b_freelist);
|
||||
nmp->nm_bufqlen--;
|
||||
if (nmp->nm_bufqwant && nmp->nm_bufqlen <= nfs_numasync) {
|
||||
nmp->nm_bufqwant = FALSE;
|
||||
nmp->nm_bufqwant = 0;
|
||||
wakeup(&nmp->nm_bufq);
|
||||
}
|
||||
if (bp->b_iocmd == BIO_READ)
|
||||
@ -194,4 +213,14 @@ nfssvc_iod(void *dummy)
|
||||
}
|
||||
}
|
||||
}
|
||||
nfs_asyncdaemon[myiod] = 0;
|
||||
if (nmp)
|
||||
nmp->nm_bufqiods--;
|
||||
nfs_iodwant[myiod] = NULL;
|
||||
nfs_iodmount[myiod] = NULL;
|
||||
nfs_numasync--;
|
||||
if (error == EWOULDBLOCK)
|
||||
kthread_exit(0);
|
||||
/* Abnormal termination */
|
||||
kthread_exit(1);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user