nfs_nfsiod.c revision 128111
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 * 4. Neither the name of the University nor the names of its contributors 171541Srgrimes * may be used to endorse or promote products derived from this software 181541Srgrimes * without specific prior written permission. 191541Srgrimes * 201541Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 211541Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 221541Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 231541Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 241541Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 251541Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 261541Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 271541Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 281541Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 291541Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 301541Srgrimes * SUCH DAMAGE. 311541Srgrimes * 3222521Sdyson * @(#)nfs_syscalls.c 8.5 (Berkeley) 3/30/95 331541Srgrimes */ 341541Srgrimes 3583651Speter#include <sys/cdefs.h> 3683651Speter__FBSDID("$FreeBSD: head/sys/nfsclient/nfs_nfsiod.c 128111 2004-04-11 13:30:20Z peadar $"); 3783651Speter 381541Srgrimes#include <sys/param.h> 391541Srgrimes#include <sys/systm.h> 4012274Sbde#include <sys/sysproto.h> 411541Srgrimes#include <sys/kernel.h> 4219449Sdfr#include <sys/sysctl.h> 431541Srgrimes#include <sys/file.h> 4415480Sbde#include <sys/filedesc.h> 451541Srgrimes#include <sys/vnode.h> 4630354Sphk#include <sys/malloc.h> 471541Srgrimes#include <sys/mount.h> 481541Srgrimes#include <sys/proc.h> 4960041Sphk#include <sys/bio.h> 501541Srgrimes#include <sys/buf.h> 511541Srgrimes#include <sys/mbuf.h> 521541Srgrimes#include <sys/socket.h> 531541Srgrimes#include <sys/socketvar.h> 541541Srgrimes#include <sys/domain.h> 551541Srgrimes#include <sys/protosw.h> 561541Srgrimes#include <sys/namei.h> 5783651Speter#include <sys/unistd.h> 5883651Speter#include <sys/kthread.h> 5975631Salfred#include <sys/fcntl.h> 6075631Salfred#include <sys/lockf.h> 6183651Speter#include <sys/mutex.h> 621541Srgrimes 631541Srgrimes#include <netinet/in.h> 641541Srgrimes#include <netinet/tcp.h> 65122698Salfred 66122698Salfred#include <rpc/rpcclnt.h> 67122698Salfred 689336Sdfr#include <nfs/xdr_subs.h> 691541Srgrimes#include <nfs/rpcv2.h> 709336Sdfr#include <nfs/nfsproto.h> 7183651Speter#include <nfsclient/nfs.h> 7283651Speter#include <nfsclient/nfsm_subs.h> 7383651Speter#include <nfsclient/nfsmount.h> 7483651Speter#include <nfsclient/nfsnode.h> 7583651Speter#include <nfsclient/nfs_lock.h> 761541Srgrimes 7730354Sphkstatic MALLOC_DEFINE(M_NFSSVC, "NFS srvsock", "Nfs server structure"); 7830309Sphk 7983651Speterstatic void nfssvc_iod(void *); 801541Srgrimes 811541Srgrimesstatic int nfs_asyncdaemon[NFS_MAXASYNCDAEMON]; 8212457Sbde 8344112SdfrSYSCTL_DECL(_vfs_nfs); 8444112Sdfr 8589407Speter/* Maximum number of seconds a nfsiod kthread will sleep before exiting */ 8689407Speterstatic unsigned int nfs_iodmaxidle = 120; 8789407SpeterSYSCTL_UINT(_vfs_nfs, OID_AUTO, iodmaxidle, CTLFLAG_RW, &nfs_iodmaxidle, 0, ""); 8889407Speter 8989407Speter/* Maximum number of nfsiod kthreads */ 90128111Speadarunsigned int nfs_iodmax = 20; 9189407Speter 9289324Speter/* Minimum number of nfsiod kthreads to keep as spares */ 9389324Speterstatic unsigned int nfs_iodmin = 4; 9489324Speter 9589407Speterstatic int 9689407Spetersysctl_iodmin(SYSCTL_HANDLER_ARGS) 9789407Speter{ 9889407Speter int error, i; 9989407Speter int newmin; 10089324Speter 10189407Speter newmin = nfs_iodmin; 10289407Speter error = sysctl_handle_int(oidp, &newmin, 0, req); 10389407Speter if (error || (req->newptr == NULL)) 10489407Speter return (error); 10589407Speter if (newmin > nfs_iodmax) 10689407Speter return (EINVAL); 10789407Speter nfs_iodmin = newmin; 10889407Speter if (nfs_numasync >= nfs_iodmin) 10989407Speter return (0); 11089407Speter /* 11189407Speter * If the current number of nfsiod is lower 11289407Speter * than the new minimum, create some more. 11389407Speter */ 11489407Speter for (i = nfs_iodmin - nfs_numasync; i > 0; i--) 11589407Speter nfs_nfsiodnew(); 11689407Speter return (0); 11789407Speter} 11889407SpeterSYSCTL_PROC(_vfs_nfs, OID_AUTO, iodmin, CTLTYPE_UINT | CTLFLAG_RW, 0, 11989407Speter sizeof (nfs_iodmin), sysctl_iodmin, "IU", ""); 12089407Speter 12189407Speter 12289407Speterstatic int 12389407Spetersysctl_iodmax(SYSCTL_HANDLER_ARGS) 12489407Speter{ 12589407Speter int error, i; 12689407Speter int iod, newmax; 12789407Speter 12889407Speter newmax = nfs_iodmax; 12989407Speter error = sysctl_handle_int(oidp, &newmax, 0, req); 13089407Speter if (error || (req->newptr == NULL)) 13189407Speter return (error); 13289407Speter if (newmax > NFS_MAXASYNCDAEMON) 13389407Speter return (EINVAL); 13489407Speter nfs_iodmax = newmax; 13589407Speter if (nfs_numasync <= nfs_iodmax) 13689407Speter return (0); 13789407Speter /* 13889407Speter * If there are some asleep nfsiods that should 13989407Speter * exit, wakeup() them so that they check nfs_iodmax 14089407Speter * and exit. Those who are active will exit as 14189407Speter * soon as they finish I/O. 14289407Speter */ 14389407Speter iod = nfs_numasync - 1; 14489407Speter for (i = 0; i < nfs_numasync - nfs_iodmax; i++) { 14589407Speter if (nfs_iodwant[iod]) 146111748Sdes wakeup(&nfs_iodwant[iod]); 14789407Speter iod--; 14889407Speter } 14989407Speter return (0); 15089407Speter} 15189407SpeterSYSCTL_PROC(_vfs_nfs, OID_AUTO, iodmax, CTLTYPE_UINT | CTLFLAG_RW, 0, 15289407Speter sizeof (nfs_iodmax), sysctl_iodmax, "IU", ""); 15389407Speter 15489324Speterint 15589324Speternfs_nfsiodnew(void) 15689324Speter{ 15789324Speter int error, i; 15889324Speter int newiod; 15989324Speter 16089407Speter if (nfs_numasync >= nfs_iodmax) 16189407Speter return (-1); 16289324Speter newiod = -1; 16389407Speter for (i = 0; i < nfs_iodmax; i++) 16489324Speter if (nfs_asyncdaemon[i] == 0) { 16589324Speter nfs_asyncdaemon[i]++; 16689324Speter newiod = i; 16789324Speter break; 16889324Speter } 16989324Speter if (newiod == -1) 17089324Speter return (-1); 17189324Speter error = kthread_create(nfssvc_iod, nfs_asyncdaemon + i, NULL, RFHIGHPID, 172104354Sscottl 0, "nfsiod %d", newiod); 17389324Speter if (error) 17489324Speter return (-1); 17589324Speter nfs_numasync++; 17689324Speter return (newiod); 17789324Speter} 17889324Speter 17983651Speterstatic void 18083651Speternfsiod_setup(void *dummy) 1811541Srgrimes{ 18283651Speter int i; 1831541Srgrimes int error; 1841541Srgrimes 18589324Speter TUNABLE_INT_FETCH("vfs.nfs.iodmin", &nfs_iodmin); 18689324Speter /* Silently limit the start number of nfsiod's */ 18789324Speter if (nfs_iodmin > NFS_MAXASYNCDAEMON) 18889324Speter nfs_iodmin = NFS_MAXASYNCDAEMON; 18989324Speter 19089324Speter for (i = 0; i < nfs_iodmin; i++) { 19189324Speter error = nfs_nfsiodnew(); 19289324Speter if (error == -1) 19389324Speter panic("nfsiod_setup: nfs_nfsiodnew failed"); 1941541Srgrimes } 1951541Srgrimes} 19683651SpeterSYSINIT(nfsiod, SI_SUB_KTHREAD_IDLE, SI_ORDER_ANY, nfsiod_setup, NULL); 1971541Srgrimes 19883651Speterstatic int nfs_defect = 0; 19983651SpeterSYSCTL_INT(_vfs_nfs, OID_AUTO, defect, CTLFLAG_RW, &nfs_defect, 0, ""); 2001541Srgrimes 20144246Speterint 20283651Speternfsclnt(struct thread *td, struct nfsclnt_args *uap) 20344246Speter{ 20483651Speter struct lockd_ans la; 20583651Speter int error; 20644246Speter 20783651Speter if ((uap->flag & NFSCLNT_LOCKDANS) != 0) { 20883651Speter error = copyin(uap->argp, &la, sizeof(la)); 20986363Srwatson return (error != 0 ? error : nfslockdans(td, &la)); 21044246Speter } 21183651Speter return EINVAL; 21244246Speter} 21344246Speter 21444246Speter/* 2151541Srgrimes * Asynchronous I/O daemons for client nfs. 2161541Srgrimes * They do read-ahead and write-behind operations on the block I/O cache. 21789324Speter * Returns if we hit the timeout defined by the iodmaxidle sysctl. 2181541Srgrimes */ 21983651Speterstatic void 22089324Speternfssvc_iod(void *instance) 2211541Srgrimes{ 22283651Speter struct buf *bp; 22319449Sdfr struct nfsmount *nmp; 22489324Speter int myiod, timo; 22531016Sphk int error = 0; 2261541Srgrimes 22783651Speter mtx_lock(&Giant); 22889324Speter myiod = (int *)instance - nfs_asyncdaemon; 2291541Srgrimes /* 23089324Speter * Main loop 2311541Srgrimes */ 2321541Srgrimes for (;;) { 23319449Sdfr while (((nmp = nfs_iodmount[myiod]) == NULL 23489324Speter || !TAILQ_FIRST(&nmp->nm_bufq)) 23519449Sdfr && error == 0) { 23689407Speter if (myiod >= nfs_iodmax) 23789407Speter goto finish; 23819449Sdfr if (nmp) 23989324Speter nmp->nm_bufqiods--; 24083651Speter nfs_iodwant[myiod] = curthread->td_proc; 24119449Sdfr nfs_iodmount[myiod] = NULL; 24289324Speter /* 24389324Speter * Always keep at least nfs_iodmin kthreads. 24489324Speter */ 24589324Speter timo = (myiod < nfs_iodmin) ? 0 : nfs_iodmaxidle * hz; 246111748Sdes error = tsleep(&nfs_iodwant[myiod], PWAIT | PCATCH, 247117152Sphk "-", timo); 2489336Sdfr } 24989324Speter if (error) 25089324Speter break; 25183651Speter while ((bp = TAILQ_FIRST(&nmp->nm_bufq)) != NULL) { 2529336Sdfr /* Take one off the front of the list */ 25319449Sdfr TAILQ_REMOVE(&nmp->nm_bufq, bp, b_freelist); 25419449Sdfr nmp->nm_bufqlen--; 25555431Sdillon if (nmp->nm_bufqwant && nmp->nm_bufqlen <= nfs_numasync) { 25689324Speter nmp->nm_bufqwant = 0; 25719449Sdfr wakeup(&nmp->nm_bufq); 25819449Sdfr } 25958345Sphk if (bp->b_iocmd == BIO_READ) 26099797Sdillon (void) nfs_doio(bp, bp->b_rcred, NULL); 26119449Sdfr else 26299797Sdillon (void) nfs_doio(bp, bp->b_wcred, NULL); 26319449Sdfr /* 26419449Sdfr * If there are more than one iod on this mount, then defect 26519449Sdfr * so that the iods can be shared out fairly between the mounts 26619449Sdfr */ 26719449Sdfr if (nfs_defect && nmp->nm_bufqiods > 1) { 26819449Sdfr NFS_DPF(ASYNCIO, 26919449Sdfr ("nfssvc_iod: iod %d defecting from mount %p\n", 27019449Sdfr myiod, nmp)); 27119449Sdfr nfs_iodmount[myiod] = NULL; 27219449Sdfr nmp->nm_bufqiods--; 27319449Sdfr break; 27419449Sdfr } 2759336Sdfr } 2761541Srgrimes } 27789407Speterfinish: 27889324Speter nfs_asyncdaemon[myiod] = 0; 27989324Speter if (nmp) 28089324Speter nmp->nm_bufqiods--; 28189324Speter nfs_iodwant[myiod] = NULL; 28289324Speter nfs_iodmount[myiod] = NULL; 283128111Speadar /* Someone may be waiting for the last nfsiod to terminate. */ 284128111Speadar if (--nfs_numasync == 0) 285128111Speadar wakeup(&nfs_numasync); 28689407Speter if ((error == 0) || (error == EWOULDBLOCK)) 28789324Speter kthread_exit(0); 28889324Speter /* Abnormal termination */ 28989324Speter kthread_exit(1); 2901541Srgrimes} 291