1*0d297f47Sjsg /* $OpenBSD: nfs_aiod.c,v 1.9 2022/01/11 03:13:59 jsg Exp $ */
203240483Sthib /*
303240483Sthib * Copyright (c) 1989, 1993
403240483Sthib * The Regents of the University of California. All rights reserved.
503240483Sthib *
603240483Sthib * This code is derived from software contributed to Berkeley by
703240483Sthib * Rick Macklem at The University of Guelph.
803240483Sthib *
903240483Sthib * Redistribution and use in source and binary forms, with or without
1003240483Sthib * modification, are permitted provided that the following conditions
1103240483Sthib * are met:
1203240483Sthib * 1. Redistributions of source code must retain the above copyright
1303240483Sthib * notice, this list of conditions and the following disclaimer.
1403240483Sthib * 2. Redistributions in binary form must reproduce the above copyright
1503240483Sthib * notice, this list of conditions and the following disclaimer in the
1603240483Sthib * documentation and/or other materials provided with the distribution.
1703240483Sthib * 3. Neither the name of the University nor the names of its contributors
1803240483Sthib * may be used to endorse or promote products derived from this software
1903240483Sthib * without specific prior written permission.
2003240483Sthib *
2103240483Sthib * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2203240483Sthib * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2303240483Sthib * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
2403240483Sthib * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
2503240483Sthib * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2603240483Sthib * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2703240483Sthib * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2803240483Sthib * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2903240483Sthib * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3003240483Sthib * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3103240483Sthib * SUCH DAMAGE.
3203240483Sthib */
3303240483Sthib
3403240483Sthib #include <sys/param.h>
3503240483Sthib #include <sys/systm.h>
3603240483Sthib #include <sys/kernel.h>
3703240483Sthib #include <sys/proc.h>
3803240483Sthib #include <sys/malloc.h>
3903240483Sthib #include <sys/mount.h>
4003240483Sthib #include <sys/vnode.h>
4103240483Sthib #include <sys/kthread.h>
4203240483Sthib #include <sys/rwlock.h>
4303240483Sthib #include <sys/signalvar.h>
4403240483Sthib #include <sys/queue.h>
4503240483Sthib #include <sys/mutex.h>
4603240483Sthib
4703240483Sthib #include <nfs/rpcv2.h>
4803240483Sthib #include <nfs/nfsproto.h>
4903240483Sthib #include <nfs/nfs.h>
5003240483Sthib #include <nfs/nfsnode.h>
5103240483Sthib #include <nfs/nfs_var.h>
5203240483Sthib #include <nfs/nfsmount.h>
5303240483Sthib
5403240483Sthib /* The nfs_aiodl_mtx mutex protects the two lists. */
5503240483Sthib struct mutex nfs_aiodl_mtx;
5603240483Sthib struct nfs_aiodhead nfs_aiods_all;
5703240483Sthib struct nfs_aiodhead nfs_aiods_idle;
5803240483Sthib
5903240483Sthib /* Current number of "running" aiods. Defaults to NFS_DEFASYNCDAEMON (4). */
6003240483Sthib int nfs_numaiods = -1;
6103240483Sthib
6203240483Sthib /* Maximum # of buf to queue on an aiod. */
6303240483Sthib int nfs_aiodbufqmax;
6403240483Sthib
6503240483Sthib /*
6603240483Sthib * Asynchronous I/O threads for client nfs.
6703240483Sthib * They do read-ahead and write-behind operations on the block I/O cache.
6803240483Sthib * Never returns unless it fails or gets killed.
6903240483Sthib */
7003240483Sthib void
nfs_aiod(void * arg)7103240483Sthib nfs_aiod(void *arg)
7203240483Sthib {
7303240483Sthib struct nfs_aiod *aiod;
7403240483Sthib struct nfsmount *nmp;
7503240483Sthib struct buf *bp;
7603240483Sthib
7703240483Sthib aiod = malloc(sizeof(*aiod), M_TEMP, M_WAITOK|M_ZERO);
7803240483Sthib mtx_enter(&nfs_aiodl_mtx);
7903240483Sthib LIST_INSERT_HEAD(&nfs_aiods_all, aiod, nad_all);
807ae83476Sthib LIST_INSERT_HEAD(&nfs_aiods_idle, aiod, nad_idle);
8103240483Sthib mtx_leave(&nfs_aiodl_mtx);
8203240483Sthib nfs_numaiods++;
8303240483Sthib
8403240483Sthib /*
8503240483Sthib * Enforce an upper limit on how many bufs we'll queue up for
8603240483Sthib * a given aiod. This is arbitrarily chosen to be a quarter of
8703240483Sthib * the number of bufs in the system, divided evenly between
8803240483Sthib * the running aiods.
8903240483Sthib *
9003240483Sthib * Since the number of bufs in the system is dynamic, and the
9103240483Sthib * aiods are usually started up very early (during boot), the
9203240483Sthib * number of buffers available is pretty low, so the limit we
9303240483Sthib * enforce is way to low: So, always allow a minimum of 64 bufs.
9403240483Sthib * XXX: Footshooting.
9503240483Sthib */
9603240483Sthib nfs_aiodbufqmax = max((bcstats.numbufs / 4) / nfs_numaiods, 64);
9703240483Sthib
9803240483Sthib
9903240483Sthib loop: /* Loop around until SIGKILL */
10000cb4738Sthib if (aiod->nad_flags & NFSAIOD_WAKEUP) {
10103240483Sthib mtx_enter(&nfs_aiodl_mtx);
10203240483Sthib LIST_INSERT_HEAD(&nfs_aiods_idle, aiod, nad_idle);
10303240483Sthib mtx_leave(&nfs_aiodl_mtx);
10400cb4738Sthib aiod->nad_flags &= ~NFSAIOD_WAKEUP;
10500cb4738Sthib }
10603240483Sthib
10703240483Sthib while (1) {
10803240483Sthib nmp = aiod->nad_mnt;
10903240483Sthib if (nmp) {
11003240483Sthib aiod->nad_mnt = NULL;
11103240483Sthib break;
11203240483Sthib }
11303240483Sthib
11400cb4738Sthib while (!(aiod->nad_flags & NFSAIOD_WAKEUP))
115ae991a59Smpi tsleep_nsec(aiod, PWAIT, "aiodidle", INFSLP);
11603240483Sthib
11703240483Sthib /*
11803240483Sthib * Wakeup for this aiod happens in one of the following
11903240483Sthib * situations:
12003240483Sthib * - The thread is being asked to exit by nfs_set_naiod(), or
12103240483Sthib * - nfs_asyncio() has found work for this thread on a mount.
12203240483Sthib *
12303240483Sthib * In the former case, check to see if nfs_asyncio() has just
12403240483Sthib * found some work for this thread, and if so, ignore it until
12503240483Sthib * later.
12603240483Sthib */
12700cb4738Sthib if (aiod->nad_flags & NFSAIOD_EXIT) {
12803240483Sthib if (aiod->nad_mnt == NULL)
12903240483Sthib goto out1;
13003240483Sthib else
13103240483Sthib break;
13203240483Sthib }
13303240483Sthib }
13403240483Sthib
13503240483Sthib while ((bp = TAILQ_FIRST(&nmp->nm_bufq)) != NULL) {
13603240483Sthib /* Take one off the front of the list */
13703240483Sthib TAILQ_REMOVE(&nmp->nm_bufq, bp, b_freelist);
13803240483Sthib nmp->nm_bufqlen--;
13903240483Sthib nfs_doio(bp, NULL);
14003240483Sthib }
14103240483Sthib
14203240483Sthib KASSERT(nmp->nm_naiods > 0);
14303240483Sthib nmp->nm_naiods--;
14400cb4738Sthib if (aiod->nad_flags & NFSAIOD_EXIT)
14503240483Sthib goto out1;
14603240483Sthib
14703240483Sthib goto loop;
14803240483Sthib
14903240483Sthib out1:
150bae2bd50Sderaadt free(aiod, M_TEMP, sizeof(*aiod));
15103240483Sthib nfs_numaiods--;
15200cb4738Sthib KASSERT(nfs_numaiods >= 0);
153*0d297f47Sjsg /* Readjust the limit of bufs to queue. See comment above. */
15400cb4738Sthib if (nfs_numaiods > 0)
15503240483Sthib nfs_aiodbufqmax = max((bcstats.numbufs / 4) / nfs_numaiods, 64);
15600cb4738Sthib else
15700cb4738Sthib nfs_aiodbufqmax = 0;
158469c0eb2Sthib kthread_exit(0);
15903240483Sthib }
16003240483Sthib
16103240483Sthib int
nfs_set_naiod(int howmany)16203240483Sthib nfs_set_naiod(int howmany)
16303240483Sthib {
16403240483Sthib struct nfs_aiod *aiod;
165469c0eb2Sthib int want, error;
16603240483Sthib
16703240483Sthib KASSERT(howmany >= 0);
16803240483Sthib
16903240483Sthib error = 0;
17003240483Sthib
17103240483Sthib if (nfs_numaiods == -1)
17203240483Sthib nfs_numaiods = 0;
17303240483Sthib
17403240483Sthib want = howmany - nfs_numaiods;
17503240483Sthib
17603240483Sthib if (want > 0) {
17703240483Sthib /* Add more. */
17803240483Sthib want = min(want, NFS_MAXASYNCDAEMON);
17903240483Sthib while (want > 0) {
18096a748bbSmiod error = kthread_create(nfs_aiod, NULL, NULL, "nfsaio");
18103240483Sthib if (error)
18203240483Sthib return (error);
18303240483Sthib want--;
18403240483Sthib }
18503240483Sthib } else if (want < 0) {
18603240483Sthib /* Get rid of some. */
18703240483Sthib want = -want;
18803240483Sthib want = min(want, nfs_numaiods);
18903240483Sthib
19003240483Sthib /* Favour idle aiod's. */
19103240483Sthib mtx_enter(&nfs_aiodl_mtx);
19203240483Sthib while (!LIST_EMPTY(&nfs_aiods_idle) && want > 0) {
19303240483Sthib aiod = LIST_FIRST(&nfs_aiods_idle);
19403240483Sthib LIST_REMOVE(aiod, nad_idle);
19503240483Sthib LIST_REMOVE(aiod, nad_all); /* Yuck. */
19600cb4738Sthib aiod->nad_flags |= NFSAIOD_QUIT;
19700cb4738Sthib wakeup_one(aiod);
19803240483Sthib want--;
19903240483Sthib }
20003240483Sthib
20103240483Sthib while (!LIST_EMPTY(&nfs_aiods_all) && want > 0) {
20203240483Sthib aiod = LIST_FIRST(&nfs_aiods_all);
20303240483Sthib LIST_REMOVE(aiod, nad_all);
20400cb4738Sthib aiod->nad_flags |= NFSAIOD_QUIT;
20500cb4738Sthib wakeup_one(aiod);
20603240483Sthib want--;
20703240483Sthib }
20803240483Sthib mtx_leave(&nfs_aiodl_mtx);
20903240483Sthib }
21003240483Sthib /* ignore the want == nfs_numaiods case, since it means no work */
21103240483Sthib
21203240483Sthib return (error);
21303240483Sthib }
214