nfs_nfsiod.c revision 122698
11541Srgrimes/* 21541Srgrimes * Copyright (c) 1989, 1993 31541Srgrimes * The Regents of the University of California. All rights reserved. 41541Srgrimes * 51541Srgrimes * This code is derived from software contributed to Berkeley by 61541Srgrimes * Rick Macklem at The University of Guelph. 71541Srgrimes * 81541Srgrimes * Redistribution and use in source and binary forms, with or without 91541Srgrimes * modification, are permitted provided that the following conditions 101541Srgrimes * are met: 111541Srgrimes * 1. Redistributions of source code must retain the above copyright 121541Srgrimes * notice, this list of conditions and the following disclaimer. 131541Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 141541Srgrimes * notice, this list of conditions and the following disclaimer in the 151541Srgrimes * documentation and/or other materials provided with the distribution. 161541Srgrimes * 3. All advertising materials mentioning features or use of this software 171541Srgrimes * must display the following acknowledgement: 181541Srgrimes * This product includes software developed by the University of 191541Srgrimes * California, Berkeley and its contributors. 201541Srgrimes * 4. Neither the name of the University nor the names of its contributors 211541Srgrimes * may be used to endorse or promote products derived from this software 221541Srgrimes * without specific prior written permission. 231541Srgrimes * 241541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 251541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 261541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 271541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 281541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 291541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 301541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 311541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 321541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 331541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 341541Srgrimes * SUCH DAMAGE. 351541Srgrimes * 3622521Sdyson * @(#)nfs_syscalls.c 8.5 (Berkeley) 3/30/95 371541Srgrimes */ 381541Srgrimes 3983651Speter#include <sys/cdefs.h> 4083651Speter__FBSDID("$FreeBSD: head/sys/nfsclient/nfs_nfsiod.c 122698 2003-11-14 20:54:10Z alfred $"); 4183651Speter 421541Srgrimes#include <sys/param.h> 431541Srgrimes#include <sys/systm.h> 4412274Sbde#include <sys/sysproto.h> 451541Srgrimes#include <sys/kernel.h> 4619449Sdfr#include <sys/sysctl.h> 471541Srgrimes#include <sys/file.h> 4815480Sbde#include <sys/filedesc.h> 491541Srgrimes#include <sys/vnode.h> 5030354Sphk#include <sys/malloc.h> 511541Srgrimes#include <sys/mount.h> 521541Srgrimes#include <sys/proc.h> 5360041Sphk#include <sys/bio.h> 541541Srgrimes#include <sys/buf.h> 551541Srgrimes#include <sys/mbuf.h> 561541Srgrimes#include <sys/socket.h> 571541Srgrimes#include <sys/socketvar.h> 581541Srgrimes#include <sys/domain.h> 591541Srgrimes#include <sys/protosw.h> 601541Srgrimes#include <sys/namei.h> 6183651Speter#include <sys/unistd.h> 6283651Speter#include <sys/kthread.h> 6375631Salfred#include <sys/fcntl.h> 6475631Salfred#include <sys/lockf.h> 6583651Speter#include <sys/mutex.h> 661541Srgrimes 671541Srgrimes#include <netinet/in.h> 681541Srgrimes#include <netinet/tcp.h> 69122698Salfred 70122698Salfred#include <rpc/rpcclnt.h> 71122698Salfred 729336Sdfr#include <nfs/xdr_subs.h> 731541Srgrimes#include <nfs/rpcv2.h> 749336Sdfr#include <nfs/nfsproto.h> 7583651Speter#include <nfsclient/nfs.h> 7683651Speter#include <nfsclient/nfsm_subs.h> 7783651Speter#include <nfsclient/nfsmount.h> 7883651Speter#include <nfsclient/nfsnode.h> 7983651Speter#include <nfsclient/nfs_lock.h> 801541Srgrimes 8130354Sphkstatic MALLOC_DEFINE(M_NFSSVC, "NFS srvsock", "Nfs server structure"); 8230309Sphk 8383651Speterstatic void nfssvc_iod(void *); 841541Srgrimes 851541Srgrimesstatic int nfs_asyncdaemon[NFS_MAXASYNCDAEMON]; 8612457Sbde 8744112SdfrSYSCTL_DECL(_vfs_nfs); 8844112Sdfr 8989407Speter/* Maximum number of seconds a nfsiod kthread will sleep before exiting */ 9089407Speterstatic unsigned int nfs_iodmaxidle = 120; 9189407SpeterSYSCTL_UINT(_vfs_nfs, OID_AUTO, iodmaxidle, CTLFLAG_RW, &nfs_iodmaxidle, 0, ""); 9289407Speter 9389407Speter/* Maximum number of nfsiod kthreads */ 9489407Speterstatic unsigned int nfs_iodmax = 20; 9589407Speter 9689324Speter/* Minimum number of nfsiod kthreads to keep as spares */ 9789324Speterstatic unsigned int nfs_iodmin = 4; 9889324Speter 9989407Speterstatic int 10089407Spetersysctl_iodmin(SYSCTL_HANDLER_ARGS) 10189407Speter{ 10289407Speter int error, i; 10389407Speter int newmin; 10489324Speter 10589407Speter newmin = nfs_iodmin; 10689407Speter error = sysctl_handle_int(oidp, &newmin, 0, req); 10789407Speter if (error || (req->newptr == NULL)) 10889407Speter return (error); 10989407Speter if (newmin > nfs_iodmax) 11089407Speter return (EINVAL); 11189407Speter nfs_iodmin = newmin; 11289407Speter if (nfs_numasync >= nfs_iodmin) 11389407Speter return (0); 11489407Speter /* 11589407Speter * If the current number of nfsiod is lower 11689407Speter * than the new minimum, create some more. 11789407Speter */ 11889407Speter for (i = nfs_iodmin - nfs_numasync; i > 0; i--) 11989407Speter nfs_nfsiodnew(); 12089407Speter return (0); 12189407Speter} 12289407SpeterSYSCTL_PROC(_vfs_nfs, OID_AUTO, iodmin, CTLTYPE_UINT | CTLFLAG_RW, 0, 12389407Speter sizeof (nfs_iodmin), sysctl_iodmin, "IU", ""); 12489407Speter 12589407Speter 12689407Speterstatic int 12789407Spetersysctl_iodmax(SYSCTL_HANDLER_ARGS) 12889407Speter{ 12989407Speter int error, i; 13089407Speter int iod, newmax; 13189407Speter 13289407Speter newmax = nfs_iodmax; 13389407Speter error = sysctl_handle_int(oidp, &newmax, 0, req); 13489407Speter if (error || (req->newptr == NULL)) 13589407Speter return (error); 13689407Speter if (newmax > NFS_MAXASYNCDAEMON) 13789407Speter return (EINVAL); 13889407Speter nfs_iodmax = newmax; 13989407Speter if (nfs_numasync <= nfs_iodmax) 14089407Speter return (0); 14189407Speter /* 14289407Speter * If there are some asleep nfsiods that should 14389407Speter * exit, wakeup() them so that they check nfs_iodmax 14489407Speter * and exit. Those who are active will exit as 14589407Speter * soon as they finish I/O. 14689407Speter */ 14789407Speter iod = nfs_numasync - 1; 14889407Speter for (i = 0; i < nfs_numasync - nfs_iodmax; i++) { 14989407Speter if (nfs_iodwant[iod]) 150111748Sdes wakeup(&nfs_iodwant[iod]); 15189407Speter iod--; 15289407Speter } 15389407Speter return (0); 15489407Speter} 15589407SpeterSYSCTL_PROC(_vfs_nfs, OID_AUTO, iodmax, CTLTYPE_UINT | CTLFLAG_RW, 0, 15689407Speter sizeof (nfs_iodmax), sysctl_iodmax, "IU", ""); 15789407Speter 15889324Speterint 15989324Speternfs_nfsiodnew(void) 16089324Speter{ 16189324Speter int error, i; 16289324Speter int newiod; 16389324Speter 16489407Speter if (nfs_numasync >= nfs_iodmax) 16589407Speter return (-1); 16689324Speter newiod = -1; 16789407Speter for (i = 0; i < nfs_iodmax; i++) 16889324Speter if (nfs_asyncdaemon[i] == 0) { 16989324Speter nfs_asyncdaemon[i]++; 17089324Speter newiod = i; 17189324Speter break; 17289324Speter } 17389324Speter if (newiod == -1) 17489324Speter return (-1); 17589324Speter error = kthread_create(nfssvc_iod, nfs_asyncdaemon + i, NULL, RFHIGHPID, 176104354Sscottl 0, "nfsiod %d", newiod); 17789324Speter if (error) 17889324Speter return (-1); 17989324Speter nfs_numasync++; 18089324Speter return (newiod); 18189324Speter} 18289324Speter 18383651Speterstatic void 18483651Speternfsiod_setup(void *dummy) 1851541Srgrimes{ 18683651Speter int i; 1871541Srgrimes int error; 1881541Srgrimes 18989324Speter TUNABLE_INT_FETCH("vfs.nfs.iodmin", &nfs_iodmin); 19089324Speter /* Silently limit the start number of nfsiod's */ 19189324Speter if (nfs_iodmin > NFS_MAXASYNCDAEMON) 19289324Speter nfs_iodmin = NFS_MAXASYNCDAEMON; 19389324Speter 19489324Speter for (i = 0; i < nfs_iodmin; i++) { 19589324Speter error = nfs_nfsiodnew(); 19689324Speter if (error == -1) 19789324Speter panic("nfsiod_setup: nfs_nfsiodnew failed"); 1981541Srgrimes } 1991541Srgrimes} 20083651SpeterSYSINIT(nfsiod, SI_SUB_KTHREAD_IDLE, SI_ORDER_ANY, nfsiod_setup, NULL); 2011541Srgrimes 20283651Speterstatic int nfs_defect = 0; 20383651SpeterSYSCTL_INT(_vfs_nfs, OID_AUTO, defect, CTLFLAG_RW, &nfs_defect, 0, ""); 2041541Srgrimes 20544246Speterint 20683651Speternfsclnt(struct thread *td, struct nfsclnt_args *uap) 20744246Speter{ 20883651Speter struct lockd_ans la; 20983651Speter int error; 21044246Speter 21183651Speter if ((uap->flag & NFSCLNT_LOCKDANS) != 0) { 21283651Speter error = copyin(uap->argp, &la, sizeof(la)); 21386363Srwatson return (error != 0 ? error : nfslockdans(td, &la)); 21444246Speter } 21583651Speter return EINVAL; 21644246Speter} 21744246Speter 21844246Speter/* 2191541Srgrimes * Asynchronous I/O daemons for client nfs. 2201541Srgrimes * They do read-ahead and write-behind operations on the block I/O cache. 22189324Speter * Returns if we hit the timeout defined by the iodmaxidle sysctl. 2221541Srgrimes */ 22383651Speterstatic void 22489324Speternfssvc_iod(void *instance) 2251541Srgrimes{ 22683651Speter struct buf *bp; 22719449Sdfr struct nfsmount *nmp; 22889324Speter int myiod, timo; 22931016Sphk int error = 0; 2301541Srgrimes 23183651Speter mtx_lock(&Giant); 23289324Speter myiod = (int *)instance - nfs_asyncdaemon; 2331541Srgrimes /* 23489324Speter * Main loop 2351541Srgrimes */ 2361541Srgrimes for (;;) { 23719449Sdfr while (((nmp = nfs_iodmount[myiod]) == NULL 23889324Speter || !TAILQ_FIRST(&nmp->nm_bufq)) 23919449Sdfr && error == 0) { 24089407Speter if (myiod >= nfs_iodmax) 24189407Speter goto finish; 24219449Sdfr if (nmp) 24389324Speter nmp->nm_bufqiods--; 24483651Speter nfs_iodwant[myiod] = curthread->td_proc; 24519449Sdfr nfs_iodmount[myiod] = NULL; 24689324Speter /* 24789324Speter * Always keep at least nfs_iodmin kthreads. 24889324Speter */ 24989324Speter timo = (myiod < nfs_iodmin) ? 0 : nfs_iodmaxidle * hz; 250111748Sdes error = tsleep(&nfs_iodwant[myiod], PWAIT | PCATCH, 251117152Sphk "-", timo); 2529336Sdfr } 25389324Speter if (error) 25489324Speter break; 25583651Speter while ((bp = TAILQ_FIRST(&nmp->nm_bufq)) != NULL) { 2569336Sdfr /* Take one off the front of the list */ 25719449Sdfr TAILQ_REMOVE(&nmp->nm_bufq, bp, b_freelist); 25819449Sdfr nmp->nm_bufqlen--; 25955431Sdillon if (nmp->nm_bufqwant && nmp->nm_bufqlen <= nfs_numasync) { 26089324Speter nmp->nm_bufqwant = 0; 26119449Sdfr wakeup(&nmp->nm_bufq); 26219449Sdfr } 26358345Sphk if (bp->b_iocmd == BIO_READ) 26499797Sdillon (void) nfs_doio(bp, bp->b_rcred, NULL); 26519449Sdfr else 26699797Sdillon (void) nfs_doio(bp, bp->b_wcred, NULL); 26719449Sdfr /* 26819449Sdfr * If there are more than one iod on this mount, then defect 26919449Sdfr * so that the iods can be shared out fairly between the mounts 27019449Sdfr */ 27119449Sdfr if (nfs_defect && nmp->nm_bufqiods > 1) { 27219449Sdfr NFS_DPF(ASYNCIO, 27319449Sdfr ("nfssvc_iod: iod %d defecting from mount %p\n", 27419449Sdfr myiod, nmp)); 27519449Sdfr nfs_iodmount[myiod] = NULL; 27619449Sdfr nmp->nm_bufqiods--; 27719449Sdfr break; 27819449Sdfr } 2799336Sdfr } 2801541Srgrimes } 28189407Speterfinish: 28289324Speter nfs_asyncdaemon[myiod] = 0; 28389324Speter if (nmp) 28489324Speter nmp->nm_bufqiods--; 28589324Speter nfs_iodwant[myiod] = NULL; 28689324Speter nfs_iodmount[myiod] = NULL; 28789324Speter nfs_numasync--; 28889407Speter if ((error == 0) || (error == EWOULDBLOCK)) 28989324Speter kthread_exit(0); 29089324Speter /* Abnormal termination */ 29189324Speter kthread_exit(1); 2921541Srgrimes} 293