nfs_nfsiod.c revision 139823
1139823Simp/*- 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 139823 2005-01-07 01:45:51Z imp $"); 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 20144246Speter/* 2021541Srgrimes * Asynchronous I/O daemons for client nfs. 2031541Srgrimes * They do read-ahead and write-behind operations on the block I/O cache. 20489324Speter * Returns if we hit the timeout defined by the iodmaxidle sysctl. 2051541Srgrimes */ 20683651Speterstatic void 20789324Speternfssvc_iod(void *instance) 2081541Srgrimes{ 20983651Speter struct buf *bp; 21019449Sdfr struct nfsmount *nmp; 21189324Speter int myiod, timo; 21231016Sphk int error = 0; 2131541Srgrimes 21483651Speter mtx_lock(&Giant); 21589324Speter myiod = (int *)instance - nfs_asyncdaemon; 2161541Srgrimes /* 21789324Speter * Main loop 2181541Srgrimes */ 2191541Srgrimes for (;;) { 22019449Sdfr while (((nmp = nfs_iodmount[myiod]) == NULL 22189324Speter || !TAILQ_FIRST(&nmp->nm_bufq)) 22219449Sdfr && error == 0) { 22389407Speter if (myiod >= nfs_iodmax) 22489407Speter goto finish; 22519449Sdfr if (nmp) 22689324Speter nmp->nm_bufqiods--; 22783651Speter nfs_iodwant[myiod] = curthread->td_proc; 22819449Sdfr nfs_iodmount[myiod] = NULL; 22989324Speter /* 23089324Speter * Always keep at least nfs_iodmin kthreads. 23189324Speter */ 23289324Speter timo = (myiod < nfs_iodmin) ? 0 : nfs_iodmaxidle * hz; 233111748Sdes error = tsleep(&nfs_iodwant[myiod], PWAIT | PCATCH, 234117152Sphk "-", timo); 2359336Sdfr } 23689324Speter if (error) 23789324Speter break; 23883651Speter while ((bp = TAILQ_FIRST(&nmp->nm_bufq)) != NULL) { 2399336Sdfr /* Take one off the front of the list */ 24019449Sdfr TAILQ_REMOVE(&nmp->nm_bufq, bp, b_freelist); 24119449Sdfr nmp->nm_bufqlen--; 24255431Sdillon if (nmp->nm_bufqwant && nmp->nm_bufqlen <= nfs_numasync) { 24389324Speter nmp->nm_bufqwant = 0; 24419449Sdfr wakeup(&nmp->nm_bufq); 24519449Sdfr } 246138899Sps if (bp->b_flags & B_DIRECT) { 247138899Sps KASSERT((bp->b_iocmd == BIO_WRITE), ("nfscvs_iod: BIO_WRITE not set")); 248138899Sps (void)nfs_doio_directwrite(bp); 249138899Sps } else { 250138899Sps if (bp->b_iocmd == BIO_READ) 251138899Sps (void) nfs_doio(bp->b_vp, bp, bp->b_rcred, NULL); 252138899Sps else 253138899Sps (void) nfs_doio(bp->b_vp, bp, bp->b_wcred, NULL); 254138899Sps } 255138899Sps 25619449Sdfr /* 25719449Sdfr * If there are more than one iod on this mount, then defect 25819449Sdfr * so that the iods can be shared out fairly between the mounts 25919449Sdfr */ 26019449Sdfr if (nfs_defect && nmp->nm_bufqiods > 1) { 26119449Sdfr NFS_DPF(ASYNCIO, 26219449Sdfr ("nfssvc_iod: iod %d defecting from mount %p\n", 26319449Sdfr myiod, nmp)); 26419449Sdfr nfs_iodmount[myiod] = NULL; 26519449Sdfr nmp->nm_bufqiods--; 26619449Sdfr break; 26719449Sdfr } 2689336Sdfr } 2691541Srgrimes } 27089407Speterfinish: 27189324Speter nfs_asyncdaemon[myiod] = 0; 27289324Speter if (nmp) 27389324Speter nmp->nm_bufqiods--; 27489324Speter nfs_iodwant[myiod] = NULL; 27589324Speter nfs_iodmount[myiod] = NULL; 276128111Speadar /* Someone may be waiting for the last nfsiod to terminate. */ 277128111Speadar if (--nfs_numasync == 0) 278128111Speadar wakeup(&nfs_numasync); 27989407Speter if ((error == 0) || (error == EWOULDBLOCK)) 28089324Speter kthread_exit(0); 28189324Speter /* Abnormal termination */ 28289324Speter kthread_exit(1); 2831541Srgrimes} 284