diff --git a/sbin/mount_nfs/mount_nfs.8 b/sbin/mount_nfs/mount_nfs.8 index bdb0707a46d5..b1b5c6e21cfc 100644 --- a/sbin/mount_nfs/mount_nfs.8 +++ b/sbin/mount_nfs/mount_nfs.8 @@ -157,6 +157,10 @@ Force the mount protocol to use UDP transport, even for TCP NFS mounts. (Necessary for some old .Bx servers.) +.It Cm nametimeo Ns = Ns Aq Ar value +Override the default of NFS_DEFAULT_NAMETIMEO for the timeout (in seconds) +for positive name cache entries. +If this is set to 0 it disables positive name caching for the mount point. .It Cm negnametimeo Ns = Ns Aq Ar value Override the default of NFS_DEFAULT_NEGNAMETIMEO for the timeout (in seconds) for negative name cache entries. If this is set to 0 it disables negative diff --git a/sys/fs/nfsclient/nfs_clvfsops.c b/sys/fs/nfsclient/nfs_clvfsops.c index 5ddfa2790e95..e6d146f84493 100644 --- a/sys/fs/nfsclient/nfs_clvfsops.c +++ b/sys/fs/nfsclient/nfs_clvfsops.c @@ -104,7 +104,7 @@ static void nfs_decode_args(struct mount *mp, struct nfsmount *nmp, static int mountnfs(struct nfs_args *, struct mount *, struct sockaddr *, char *, u_char *, int, u_char *, int, u_char *, int, struct vnode **, struct ucred *, - struct thread *, int); + struct thread *, int, int); static void nfs_getnlminfo(struct vnode *, uint8_t *, size_t *, struct sockaddr_storage *, int *, off_t *, struct timeval *); @@ -520,7 +520,8 @@ nfs_mountdiskless(char *path, dirlen = 0; nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK); if ((error = mountnfs(args, mp, nam, path, NULL, 0, dirpath, dirlen, - NULL, 0, vpp, td->td_ucred, td, NFS_DEFAULT_NEGNAMETIMEO)) != 0) { + NULL, 0, vpp, td->td_ucred, td, NFS_DEFAULT_NAMETIMEO, + NFS_DEFAULT_NEGNAMETIMEO)) != 0) { printf("nfs_mountroot: mount %s on /: %d\n", path, error); return (error); } @@ -715,7 +716,7 @@ static const char *nfs_opts[] = { "from", "nfs_args", "retrans", "acregmin", "acregmax", "acdirmin", "acdirmax", "resvport", "readahead", "hostname", "timeout", "addr", "fh", "nfsv3", "sec", "principal", "nfsv4", "gssname", "allgssname", "dirpath", - "negnametimeo", "nocto", "wcommitsize", + "nametimeo", "negnametimeo", "nocto", "wcommitsize", NULL }; /* @@ -760,6 +761,7 @@ nfs_mount(struct mount *mp) char hst[MNAMELEN]; u_char nfh[NFSX_FHMAX], krbname[100], dirpath[100], srvkrbname[100]; char *opt, *name, *secname; + int nametimeo = NFS_DEFAULT_NAMETIMEO; int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO; int dirlen, has_nfs_args_opt, krbnamelen, srvkrbnamelen; size_t hstlen; @@ -968,6 +970,14 @@ nfs_mount(struct mount *mp) } args.flags |= NFSMNT_TIMEO; } + if (vfs_getopt(mp->mnt_optnew, "nametimeo", (void **)&opt, NULL) == 0) { + ret = sscanf(opt, "%d", &nametimeo); + if (ret != 1 || nametimeo < 0) { + vfs_mount_error(mp, "illegal nametimeo: %s", opt); + error = EINVAL; + goto out; + } + } if (vfs_getopt(mp->mnt_optnew, "negnametimeo", (void **)&opt, NULL) == 0) { ret = sscanf(opt, "%d", &negnametimeo); @@ -1126,7 +1136,7 @@ nfs_mount(struct mount *mp) args.fh = nfh; error = mountnfs(&args, mp, nam, hst, krbname, krbnamelen, dirpath, dirlen, srvkrbname, srvkrbnamelen, &vp, td->td_ucred, td, - negnametimeo); + nametimeo, negnametimeo); out: if (!error) { MNT_ILOCK(mp); @@ -1170,7 +1180,7 @@ static int mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam, char *hst, u_char *krbname, int krbnamelen, u_char *dirpath, int dirlen, u_char *srvkrbname, int srvkrbnamelen, struct vnode **vpp, - struct ucred *cred, struct thread *td, int negnametimeo) + struct ucred *cred, struct thread *td, int nametimeo, int negnametimeo) { struct nfsmount *nmp; struct nfsnode *np; @@ -1237,13 +1247,14 @@ mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam, } vfs_getnewfsid(mp); nmp->nm_mountp = mp; - mtx_init(&nmp->nm_mtx, "NFSmount lock", NULL, MTX_DEF | MTX_DUPOK); + mtx_init(&nmp->nm_mtx, "NFSmount lock", NULL, MTX_DEF | MTX_DUPOK); /* - * Since nfs_decode_args() might optionally set them, these need to - * set to defaults before the call, so that the optional settings - * aren't overwritten. + * Since nfs_decode_args() might optionally set them, these + * need to be set to defaults before the call, so that the + * optional settings aren't overwritten. */ + nmp->nm_nametimeo = nametimeo; nmp->nm_negnametimeo = negnametimeo; nmp->nm_timeo = NFS_TIMEO; nmp->nm_retry = NFS_RETRANS; diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c index 2747191ea507..183491e99f45 100644 --- a/sys/fs/nfsclient/nfs_clvnops.c +++ b/sys/fs/nfsclient/nfs_clvnops.c @@ -1063,7 +1063,8 @@ nfs_lookup(struct vop_lookup_args *ap) * We only accept a positive hit in the cache if the * change time of the file matches our cached copy. * Otherwise, we discard the cache entry and fallback - * to doing a lookup RPC. + * to doing a lookup RPC. We also only trust cache + * entries for less than nm_nametimeo seconds. * * To better handle stale file handles and attributes, * clear the attribute cache of this node if it is a @@ -1085,7 +1086,8 @@ nfs_lookup(struct vop_lookup_args *ap) mtx_unlock(&newnp->n_mtx); } if (nfscl_nodeleg(newvp, 0) == 0 || - (VOP_GETATTR(newvp, &vattr, cnp->cn_cred) == 0 && + ((u_int)(ticks - ncticks) < (nmp->nm_nametimeo * hz) && + VOP_GETATTR(newvp, &vattr, cnp->cn_cred) == 0 && timespeccmp(&vattr.va_ctime, &nctime, ==))) { NFSINCRGLOBAL(newnfsstats.lookupcache_hits); if (cnp->cn_nameiop != LOOKUP && diff --git a/sys/fs/nfsclient/nfsmount.h b/sys/fs/nfsclient/nfsmount.h index b62a430e6683..8068c28ace2b 100644 --- a/sys/fs/nfsclient/nfsmount.h +++ b/sys/fs/nfsclient/nfsmount.h @@ -66,6 +66,7 @@ struct nfsmount { u_int64_t nm_maxfilesize; /* maximum file size */ int nm_tprintf_initial_delay; /* initial delay */ int nm_tprintf_delay; /* interval for messages */ + int nm_nametimeo; /* timeout for +ve entries (sec) */ int nm_negnametimeo; /* timeout for -ve entries (sec) */ /* Newnfs additions */ @@ -106,6 +107,10 @@ struct nfsmount { */ #define VFSTONFS(mp) ((struct nfsmount *)((mp)->mnt_data)) +#ifndef NFS_DEFAULT_NAMETIMEO +#define NFS_DEFAULT_NAMETIMEO 60 +#endif + #ifndef NFS_DEFAULT_NEGNAMETIMEO #define NFS_DEFAULT_NEGNAMETIMEO 60 #endif diff --git a/sys/nfsclient/nfs_vfsops.c b/sys/nfsclient/nfs_vfsops.c index e43c048f517b..5c257fcda492 100644 --- a/sys/nfsclient/nfs_vfsops.c +++ b/sys/nfsclient/nfs_vfsops.c @@ -117,7 +117,7 @@ static void nfs_decode_args(struct mount *mp, struct nfsmount *nmp, struct nfs_args *argp, const char *hostname); static int mountnfs(struct nfs_args *, struct mount *, struct sockaddr *, char *, struct vnode **, - struct ucred *cred, int); + struct ucred *cred, int, int); static void nfs_getnlminfo(struct vnode *, uint8_t *, size_t *, struct sockaddr_storage *, int *, off_t *, struct timeval *); @@ -559,8 +559,8 @@ nfs_mountdiskless(char *path, int error; nam = sodupsockaddr((struct sockaddr *)sin, M_WAITOK); - if ((error = mountnfs(args, mp, nam, path, vpp, - td->td_ucred, NFS_DEFAULT_NEGNAMETIMEO)) != 0) { + if ((error = mountnfs(args, mp, nam, path, vpp, td->td_ucred, + NFS_DEFAULT_NAMETIMEO, NFS_DEFAULT_NEGNAMETIMEO)) != 0) { printf("nfs_mountroot: mount %s on /: %d\n", path, error); return (error); } @@ -788,6 +788,7 @@ static const char *nfs_opts[] = { "from", "nfs_args", "wsize", "rsize", "retrans", "acregmin", "acregmax", "acdirmin", "acdirmax", "deadthresh", "hostname", "timeout", "addr", "fh", "nfsv3", "sec", "maxgroups", "principal", "negnametimeo", "nocto", "wcommitsize", + "nametimeo", NULL }; /* @@ -836,6 +837,7 @@ nfs_mount(struct mount *mp) size_t len; u_char nfh[NFSX_V3FHMAX]; char *opt; + int nametimeo = NFS_DEFAULT_NAMETIMEO; int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO; has_nfs_args_opt = 0; @@ -1058,6 +1060,14 @@ nfs_mount(struct mount *mp) } args.flags |= NFSMNT_MAXGRPS; } + if (vfs_getopt(mp->mnt_optnew, "nametimeo", (void **)&opt, NULL) == 0) { + ret = sscanf(opt, "%d", &nametimeo); + if (ret != 1 || nametimeo < 0) { + vfs_mount_error(mp, "illegal nametimeo: %s", opt); + error = EINVAL; + goto out; + } + } if (vfs_getopt(mp->mnt_optnew, "negnametimeo", (void **)&opt, NULL) == 0) { ret = sscanf(opt, "%d", &negnametimeo); @@ -1182,7 +1192,7 @@ nfs_mount(struct mount *mp) goto out; } error = mountnfs(&args, mp, nam, args.hostname, &vp, - curthread->td_ucred, negnametimeo); + curthread->td_ucred, nametimeo, negnametimeo); out: if (!error) { MNT_ILOCK(mp); @@ -1224,7 +1234,8 @@ nfs_cmount(struct mntarg *ma, void *data, uint64_t flags) */ static int mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam, - char *hst, struct vnode **vpp, struct ucred *cred, int negnametimeo) + char *hst, struct vnode **vpp, struct ucred *cred, int nametimeo, + int negnametimeo) { struct nfsmount *nmp; struct nfsnode *np; @@ -1274,6 +1285,7 @@ mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam, nmp->nm_numgrps = NFS_MAXGRPS; nmp->nm_readahead = NFS_DEFRAHEAD; nmp->nm_deadthresh = NFS_MAXDEADTHRESH; + nmp->nm_nametimeo = nametimeo; nmp->nm_negnametimeo = negnametimeo; nmp->nm_tprintf_delay = nfs_tprintf_delay; if (nmp->nm_tprintf_delay < 0) diff --git a/sys/nfsclient/nfs_vnops.c b/sys/nfsclient/nfs_vnops.c index a39b29b7374a..e03244898d4b 100644 --- a/sys/nfsclient/nfs_vnops.c +++ b/sys/nfsclient/nfs_vnops.c @@ -959,7 +959,8 @@ nfs_lookup(struct vop_lookup_args *ap) * We only accept a positive hit in the cache if the * change time of the file matches our cached copy. * Otherwise, we discard the cache entry and fallback - * to doing a lookup RPC. + * to doing a lookup RPC. We also only trust cache + * entries for less than nm_nametimeo seconds. * * To better handle stale file handles and attributes, * clear the attribute cache of this node if it is a @@ -980,7 +981,8 @@ nfs_lookup(struct vop_lookup_args *ap) KDTRACE_NFS_ATTRCACHE_FLUSH_DONE(newvp); mtx_unlock(&newnp->n_mtx); } - if (VOP_GETATTR(newvp, &vattr, cnp->cn_cred) == 0 && + if ((u_int)(ticks - ncticks) < (nmp->nm_nametimeo * hz) && + VOP_GETATTR(newvp, &vattr, cnp->cn_cred) == 0 && timespeccmp(&vattr.va_ctime, &nctime, ==)) { nfsstats.lookupcache_hits++; if (cnp->cn_nameiop != LOOKUP && diff --git a/sys/nfsclient/nfsmount.h b/sys/nfsclient/nfsmount.h index 6b84bdde5b75..77171a13ee39 100644 --- a/sys/nfsclient/nfsmount.h +++ b/sys/nfsclient/nfsmount.h @@ -83,6 +83,7 @@ struct nfsmount { struct rpc_timers nm_timers[NFS_MAX_TIMER]; /* RTT Timers for rpcs */ char nm_principal[MNAMELEN]; /* GSS-API principal of server */ gss_OID nm_mech_oid; /* OID of selected GSS-API mechanism */ + int nm_nametimeo; /* timeout for +ve entries (sec) */ int nm_negnametimeo; /* timeout for -ve entries (sec) */ /* NFSv4 */ @@ -116,6 +117,10 @@ struct nfsmount { #define NFS_TPRINTF_DELAY 30 #endif +#ifndef NFS_DEFAULT_NAMETIMEO +#define NFS_DEFAULT_NAMETIMEO 60 +#endif + #ifndef NFS_DEFAULT_NEGNAMETIMEO #define NFS_DEFAULT_NEGNAMETIMEO 60 #endif