--- usr.sbin/mountd/mountd.c.sav 2012-06-30 19:57:21.000000000 -0400 +++ usr.sbin/mountd/mountd.c 2012-09-23 09:31:57.000000000 -0400 @@ -241,6 +241,7 @@ static int mallocd_svcport = 0; static int *sock_fd; static int sock_fdcnt; static int sock_fdpos; +static int suspend_nfsd = 0; int opt_flags; static int have_v6 = 1; @@ -306,7 +307,7 @@ main(int argc, char **argv) else close(s); - while ((c = getopt(argc, argv, "2deh:lnop:r")) != -1) + while ((c = getopt(argc, argv, "2deh:lnop:rS")) != -1) switch (c) { case '2': force_v2 = 1; @@ -358,6 +359,9 @@ main(int argc, char **argv) out_of_mem(); } break; + case 'S': + suspend_nfsd = 1; + break; default: usage(); }; @@ -1648,6 +1652,8 @@ get_exportlist(void) int done; struct nfsex_args eargs; + if (suspend_nfsd != 0) + (void)nfssvc(NFSSVC_SUSPENDNFSD, NULL); v4root_dirpath[0] = '\0'; bzero(&export, sizeof(export)); export.ex_flags = MNT_DELEXPORT; @@ -1776,6 +1782,9 @@ get_exportlist(void) */ if (run_v4server > 0 && has_publicfh == 0) (void) nfssvc(NFSSVC_NOPUBLICFH, NULL); + + /* Resume the nfsd. If they weren't suspended, this is harmless. */ + (void)nfssvc(NFSSVC_RESUMENFSD, NULL); } /* --- usr.sbin/mountd/mountd.8.sav 2012-06-30 20:24:30.000000000 -0400 +++ usr.sbin/mountd/mountd.8 2012-06-30 20:29:55.000000000 -0400 @@ -38,7 +38,7 @@ mount requests .Sh SYNOPSIS .Nm -.Op Fl 2delnor +.Op Fl 2delnorS .Op Fl h Ar bindip .Op Fl p Ar port .Op Ar exportsfile ... @@ -131,6 +131,19 @@ flag. Specify an alternate location for the exports file. More than one exports file can be specified. +.It Fl S +Tell mountd to suspend/resume execution of the nfsd threads whenever +exports list is being reloaded. +This avoids intermittent access +errors for clients that do NFS RPCs while the exports are being +reloaded, but introduces a delay in RPC response while the reload +is in progress. +If +.Nm +crashes while an exports load is in progress, +.Nm +must be restarted to get the nfsd threads running again, if this +option is used. .El .Pp When --- sys/fs/nfsserver/nfs_nfsdkrpc.c.sav 2012-09-23 09:25:02.000000000 -0400 +++ sys/fs/nfsserver/nfs_nfsdkrpc.c 2012-09-23 09:25:02.000000000 -0400 @@ -45,6 +45,8 @@ __FBSDID("$FreeBSD: head/sys/fs/nfsserve #include NFSDLOCKMUTEX; +NFSV4ROOTLOCKMUTEX; +struct nfsv4lock nfsd_suspend_lock; /* * Mapping of old NFS Version 2 RPC numbers to generic numbers. @@ -221,9 +223,24 @@ nfssvc_program(struct svc_req *rqst, SVC #ifdef MAC mac_cred_associate_nfsd(nd.nd_cred); #endif + /* + * Get a refcnt (shared lock) on nfsd_suspend_lock. + * NFSSVC_SUSPENDNFSD will take an exclusive lock on + * nfsd_suspend_lock to suspend these threads. + * This must be done here, before the check of + * nfsv4root exports by nfsvno_v4rootexport(). + */ + NFSLOCKV4ROOTMUTEX(); + nfsv4_getref(&nfsd_suspend_lock, NULL, NFSV4ROOTLOCKMUTEXPTR, + NULL); + NFSUNLOCKV4ROOTMUTEX(); + if ((nd.nd_flag & ND_NFSV4) != 0) { nd.nd_repstat = nfsvno_v4rootexport(&nd); if (nd.nd_repstat != 0) { + NFSLOCKV4ROOTMUTEX(); + nfsv4_relref(&nfsd_suspend_lock); + NFSUNLOCKV4ROOTMUTEX(); svcerr_weakauth(rqst); svc_freereq(rqst); m_freem(nd.nd_mrep); @@ -233,6 +250,9 @@ nfssvc_program(struct svc_req *rqst, SVC cacherep = nfs_proc(&nd, rqst->rq_xid, xprt->xp_socket, xprt->xp_sockref, &rp); + NFSLOCKV4ROOTMUTEX(); + nfsv4_relref(&nfsd_suspend_lock); + NFSUNLOCKV4ROOTMUTEX(); } else { NFSMGET(nd.nd_mreq); nd.nd_mreq->m_len = 0; --- sys/fs/nfsserver/nfs_nfsdport.c.sav 2012-09-23 09:25:56.000000000 -0400 +++ sys/fs/nfsserver/nfs_nfsdport.c 2012-09-23 09:29:58.000000000 -0400 @@ -57,6 +57,7 @@ extern struct mount nfsv4root_mnt; extern struct nfsrv_stablefirst nfsrv_stablefirst; extern void (*nfsd_call_servertimer)(void); extern SVCPOOL *nfsrvd_pool; +extern struct nfsv4lock nfsd_suspend_lock; struct vfsoptlist nfsv4root_opt, nfsv4root_newopt; NFSDLOCKMUTEX; struct mtx nfs_cache_mutex; @@ -3095,8 +3096,9 @@ nfssvc_srvcall(struct thread *p, struct struct nfsd_dumplocks *dumplocks; struct nameidata nd; vnode_t vp; - int error = EINVAL; + int error = EINVAL, igotlock; struct proc *procp; + static int suspend_nfsd = 0; if (uap->flag & NFSSVC_PUBLICFH) { NFSBZERO((caddr_t)&nfs_pubfh.nfsrvfh_data, @@ -3175,6 +3177,26 @@ nfssvc_srvcall(struct thread *p, struct nfsd_master_start = procp->p_stats->p_start; nfsd_master_proc = procp; PROC_UNLOCK(procp); + } else if ((uap->flag & NFSSVC_SUSPENDNFSD) != 0) { + NFSLOCKV4ROOTMUTEX(); + if (suspend_nfsd == 0) { + /* Lock out all nfsd threads */ + do { + igotlock = nfsv4_lock(&nfsd_suspend_lock, 1, + NULL, NFSV4ROOTLOCKMUTEXPTR, NULL); + } while (igotlock == 0 && suspend_nfsd == 0); + suspend_nfsd = 1; + } + NFSUNLOCKV4ROOTMUTEX(); + error = 0; + } else if ((uap->flag & NFSSVC_RESUMENFSD) != 0) { + NFSLOCKV4ROOTMUTEX(); + if (suspend_nfsd != 0) { + nfsv4_unlock(&nfsd_suspend_lock, 0); + suspend_nfsd = 0; + } + NFSUNLOCKV4ROOTMUTEX(); + error = 0; } NFSEXITCODE(error); --- sys/nfs/nfssvc.h.sav 2012-09-15 20:35:11.000000000 -0400 +++ sys/nfs/nfssvc.h 2012-09-15 20:35:11.000000000 -0400 @@ -66,5 +66,7 @@ #define NFSSVC_BACKUPSTABLE 0x00800000 #define NFSSVC_ZEROCLTSTATS 0x01000000 /* modifier for GETSTATS */ #define NFSSVC_ZEROSRVSTATS 0x02000000 /* modifier for GETSTATS */ +#define NFSSVC_SUSPENDNFSD 0x04000000 +#define NFSSVC_RESUMENFSD 0x08000000 #endif /* _NFS_NFSSVC_H */ --- sys/nfs/nfs_nfssvc.c.sav 2012-09-15 20:35:11.000000000 -0400 +++ sys/nfs/nfs_nfssvc.c 2012-09-15 20:35:11.000000000 -0400 @@ -102,7 +102,8 @@ sys_nfssvc(struct thread *td, struct nfs else if ((uap->flag & (NFSSVC_NFSDNFSD | NFSSVC_NFSDADDSOCK | NFSSVC_PUBLICFH | NFSSVC_V4ROOTEXPORT | NFSSVC_NOPUBLICFH | NFSSVC_STABLERESTART | NFSSVC_ADMINREVOKE | - NFSSVC_DUMPCLIENTS | NFSSVC_DUMPLOCKS | NFSSVC_BACKUPSTABLE)) && + NFSSVC_DUMPCLIENTS | NFSSVC_DUMPLOCKS | NFSSVC_BACKUPSTABLE | + NFSSVC_SUSPENDNFSD | NFSSVC_RESUMENFSD)) && nfsd_call_nfsd != NULL) error = (*nfsd_call_nfsd)(td, uap); if (error == EINTR || error == ERESTART)