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:
Peter Wemm 2002-01-14 02:13:46 +00:00
parent 971730fc67
commit 117f61374c
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=89324
3 changed files with 92 additions and 60 deletions

View File

@ -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 *);

View File

@ -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.

View File

@ -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);
}