summaryrefslogtreecommitdiffstats
path: root/sys
diff options
context:
space:
mode:
authorpeter <peter@FreeBSD.org>2002-01-14 02:13:46 +0000
committerpeter <peter@FreeBSD.org>2002-01-14 02:13:46 +0000
commit08d32da0a5c6bdbe31475c3af6a0a86d09560d03 (patch)
tree077cb662afe0f3570e4fd98b2cbdc9d58d25867c /sys
parent840adbd42c7eb9a9b6dad38f6479fc9781336d44 (diff)
downloadFreeBSD-src-08d32da0a5c6bdbe31475c3af6a0a86d09560d03.zip
FreeBSD-src-08d32da0a5c6bdbe31475c3af6a0a86d09560d03.tar.gz
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>
Diffstat (limited to 'sys')
-rw-r--r--sys/nfsclient/nfs.h1
-rw-r--r--sys/nfsclient/nfs_bio.c48
-rw-r--r--sys/nfsclient/nfs_nfsiod.c103
3 files changed, 92 insertions, 60 deletions
diff --git a/sys/nfsclient/nfs.h b/sys/nfsclient/nfs.h
index ec0e6ac..01901a4 100644
--- a/sys/nfsclient/nfs.h
+++ b/sys/nfsclient/nfs.h
@@ -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 *);
diff --git a/sys/nfsclient/nfs_bio.c b/sys/nfsclient/nfs_bio.c
index 2f3a44b..3905118 100644
--- a/sys/nfsclient/nfs_bio.c
+++ b/sys/nfsclient/nfs_bio.c
@@ -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 @@ again:
* (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,24 +1143,22 @@ again:
/*
* 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.
*/
@@ -1186,6 +1177,17 @@ again:
*/
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.
*/
diff --git a/sys/nfsclient/nfs_nfsiod.c b/sys/nfsclient/nfs_nfsiod.c
index 6fe1a55..d095792 100644
--- a/sys/nfsclient/nfs_nfsiod.c
+++ b/sys/nfsclient/nfs_nfsiod.c
@@ -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);
}
OpenPOWER on IntegriCloud