Change NFS readdir() to only ignore cookies preceding the given offset for

UFS rather than for all but ZFS.  This code was assuming that offsets were
monotonically increasing for all file systems except ZFS and that the
cookies from a previous call may have been rewound to a block boundary.
According to mckusick@ only UFS is known to do this, so only requests against
UFS file systems should remove cookies smaller than the given offset.  This
fixes serving TMPFS over NFS as it too does not have monotonically increasing
offsets.  The comment around the code also indicated it was specific to UFS.

Some of the code using 'not_zfs' is specific to ZFS snapshot handling, so
add a 'is_zfs' variable for those cases.

It's possible that 'is_zfs' check for VFS_VGET() support may not be
specific to ZFS.  This needs more research and testing.

After this fix TMPFS and other file systems can be served over NFS.

To test I compared the results of syncing a /usr/src tree into a tmpfs and
serving that over NFS.  Before the fix 3589 files were missing on the remote
view.  After the fix all files were successfully found.

Reviewed by:	rmacklem
Discussed with:	mckusick, rmacklem via fs@
Discussed at:	http://lists.freebsd.org/pipermail/freebsd-fs/2014-April/019264.html
MFC after:	2 weeks
Sponsored by:	EMC / Isilon Storage Division
This commit is contained in:
bdrewery 2014-07-01 20:00:35 +00:00
parent 32bde54848
commit be0e28fbe8
2 changed files with 15 additions and 22 deletions

View File

@ -1551,7 +1551,7 @@ nfsrvd_readdir(struct nfsrv_descript *nd, int isdgram,
u_long *cookies = NULL, *cookiep; u_long *cookies = NULL, *cookiep;
struct uio io; struct uio io;
struct iovec iv; struct iovec iv;
int not_zfs; int is_ufs;
if (nd->nd_repstat) { if (nd->nd_repstat) {
nfsrv_postopattr(nd, getret, &at); nfsrv_postopattr(nd, getret, &at);
@ -1606,7 +1606,7 @@ nfsrvd_readdir(struct nfsrv_descript *nd, int isdgram,
nfsrv_postopattr(nd, getret, &at); nfsrv_postopattr(nd, getret, &at);
goto out; goto out;
} }
not_zfs = strcmp(vp->v_mount->mnt_vfc->vfc_name, "zfs"); is_ufs = strcmp(vp->v_mount->mnt_vfc->vfc_name, "ufs") == 0;
MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK); MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
again: again:
eofflag = 0; eofflag = 0;
@ -1686,12 +1686,10 @@ nfsrvd_readdir(struct nfsrv_descript *nd, int isdgram,
* skip over the records that precede the requested offset. This * skip over the records that precede the requested offset. This
* requires the assumption that file offset cookies monotonically * requires the assumption that file offset cookies monotonically
* increase. * increase.
* Since the offset cookies don't monotonically increase for ZFS,
* this is not done when ZFS is the file system.
*/ */
while (cpos < cend && ncookies > 0 && while (cpos < cend && ncookies > 0 &&
(dp->d_fileno == 0 || dp->d_type == DT_WHT || (dp->d_fileno == 0 || dp->d_type == DT_WHT ||
(not_zfs != 0 && ((u_quad_t)(*cookiep)) <= toff))) { (is_ufs == 1 && ((u_quad_t)(*cookiep)) <= toff))) {
cpos += dp->d_reclen; cpos += dp->d_reclen;
dp = (struct dirent *)cpos; dp = (struct dirent *)cpos;
cookiep++; cookiep++;
@ -1804,7 +1802,7 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
struct uio io; struct uio io;
struct iovec iv; struct iovec iv;
struct componentname cn; struct componentname cn;
int at_root, needs_unbusy, not_zfs, supports_nfsv4acls; int at_root, is_ufs, is_zfs, needs_unbusy, supports_nfsv4acls;
struct mount *mp, *new_mp; struct mount *mp, *new_mp;
uint64_t mounted_on_fileno; uint64_t mounted_on_fileno;
@ -1884,7 +1882,8 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
nfsrv_postopattr(nd, getret, &at); nfsrv_postopattr(nd, getret, &at);
goto out; goto out;
} }
not_zfs = strcmp(vp->v_mount->mnt_vfc->vfc_name, "zfs"); is_ufs = strcmp(vp->v_mount->mnt_vfc->vfc_name, "ufs") == 0;
is_zfs = strcmp(vp->v_mount->mnt_vfc->vfc_name, "zfs") == 0;
MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK); MALLOC(rbuf, caddr_t, siz, M_TEMP, M_WAITOK);
again: again:
@ -1957,12 +1956,10 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
* skip over the records that precede the requested offset. This * skip over the records that precede the requested offset. This
* requires the assumption that file offset cookies monotonically * requires the assumption that file offset cookies monotonically
* increase. * increase.
* Since the offset cookies don't monotonically increase for ZFS,
* this is not done when ZFS is the file system.
*/ */
while (cpos < cend && ncookies > 0 && while (cpos < cend && ncookies > 0 &&
(dp->d_fileno == 0 || dp->d_type == DT_WHT || (dp->d_fileno == 0 || dp->d_type == DT_WHT ||
(not_zfs != 0 && ((u_quad_t)(*cookiep)) <= toff) || (is_ufs == 1 && ((u_quad_t)(*cookiep)) <= toff) ||
((nd->nd_flag & ND_NFSV4) && ((nd->nd_flag & ND_NFSV4) &&
((dp->d_namlen == 1 && dp->d_name[0] == '.') || ((dp->d_namlen == 1 && dp->d_name[0] == '.') ||
(dp->d_namlen==2 && dp->d_name[0]=='.' && dp->d_name[1]=='.'))))) { (dp->d_namlen==2 && dp->d_name[0]=='.' && dp->d_name[1]=='.'))))) {
@ -2004,7 +2001,7 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
* This needs to be done here for NFSv4, since NFSv4 never does * This needs to be done here for NFSv4, since NFSv4 never does
* a VFS_VGET() for "." or "..". * a VFS_VGET() for "." or "..".
*/ */
if (not_zfs == 0) { if (is_zfs == 1) {
r = VFS_VGET(mp, at.na_fileid, LK_SHARED, &nvp); r = VFS_VGET(mp, at.na_fileid, LK_SHARED, &nvp);
if (r == EOPNOTSUPP) { if (r == EOPNOTSUPP) {
usevget = 0; usevget = 0;
@ -2153,7 +2150,7 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram,
if (!r) if (!r)
r = nfsvno_getattr(nvp, nvap, r = nfsvno_getattr(nvp, nvap,
nd->nd_cred, p, 1); nd->nd_cred, p, 1);
if (r == 0 && not_zfs == 0 && if (r == 0 && is_zfs == 1 &&
nfsrv_enable_crossmntpt != 0 && nfsrv_enable_crossmntpt != 0 &&
(nd->nd_flag & ND_NFSV4) != 0 && (nd->nd_flag & ND_NFSV4) != 0 &&
nvp->v_type == VDIR && nvp->v_type == VDIR &&

View File

@ -2627,7 +2627,7 @@ nfsrv_readdir(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
int v3 = (nfsd->nd_flag & ND_NFSV3); int v3 = (nfsd->nd_flag & ND_NFSV3);
u_quad_t off, toff, verf; u_quad_t off, toff, verf;
u_long *cookies = NULL, *cookiep; /* needs to be int64_t or off_t */ u_long *cookies = NULL, *cookiep; /* needs to be int64_t or off_t */
int not_zfs; int is_ufs;
nfsdbprintf(("%s %d\n", __FILE__, __LINE__)); nfsdbprintf(("%s %d\n", __FILE__, __LINE__));
fhp = &nfh.fh_generic; fhp = &nfh.fh_generic;
@ -2690,7 +2690,7 @@ nfsrv_readdir(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
error = 0; error = 0;
goto nfsmout; goto nfsmout;
} }
not_zfs = strcmp(vp->v_mount->mnt_vfc->vfc_name, "zfs") != 0; is_ufs = strcmp(vp->v_mount->mnt_vfc->vfc_name, "ufs") == 0;
VOP_UNLOCK(vp, 0); VOP_UNLOCK(vp, 0);
/* /*
@ -2777,12 +2777,10 @@ nfsrv_readdir(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
* skip over the records that precede the requested offset. This * skip over the records that precede the requested offset. This
* requires the assumption that file offset cookies monotonically * requires the assumption that file offset cookies monotonically
* increase. * increase.
* Since the offset cookies don't monotonically increase for ZFS,
* this is not done when ZFS is the file system.
*/ */
while (cpos < cend && ncookies > 0 && while (cpos < cend && ncookies > 0 &&
(dp->d_fileno == 0 || dp->d_type == DT_WHT || (dp->d_fileno == 0 || dp->d_type == DT_WHT ||
(not_zfs != 0 && ((u_quad_t)(*cookiep)) <= toff))) { (is_ufs == 1 && ((u_quad_t)(*cookiep)) <= toff))) {
cpos += dp->d_reclen; cpos += dp->d_reclen;
dp = (struct dirent *)cpos; dp = (struct dirent *)cpos;
cookiep++; cookiep++;
@ -2928,7 +2926,7 @@ nfsrv_readdirplus(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
int usevget = 1; int usevget = 1;
struct componentname cn; struct componentname cn;
struct mount *mntp = NULL; struct mount *mntp = NULL;
int not_zfs; int is_ufs;
nfsdbprintf(("%s %d\n", __FILE__, __LINE__)); nfsdbprintf(("%s %d\n", __FILE__, __LINE__));
vp_locked = 0; vp_locked = 0;
@ -2988,7 +2986,7 @@ nfsrv_readdirplus(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
error = 0; error = 0;
goto nfsmout; goto nfsmout;
} }
not_zfs = strcmp(vp->v_mount->mnt_vfc->vfc_name, "zfs") != 0; is_ufs = strcmp(vp->v_mount->mnt_vfc->vfc_name, "ufs") == 0;
VOP_UNLOCK(vp, 0); VOP_UNLOCK(vp, 0);
vp_locked = 0; vp_locked = 0;
rbuf = malloc(siz, M_TEMP, M_WAITOK); rbuf = malloc(siz, M_TEMP, M_WAITOK);
@ -3068,12 +3066,10 @@ nfsrv_readdirplus(struct nfsrv_descript *nfsd, struct nfssvc_sock *slp,
* skip over the records that precede the requested offset. This * skip over the records that precede the requested offset. This
* requires the assumption that file offset cookies monotonically * requires the assumption that file offset cookies monotonically
* increase. * increase.
* Since the offset cookies don't monotonically increase for ZFS,
* this is not done when ZFS is the file system.
*/ */
while (cpos < cend && ncookies > 0 && while (cpos < cend && ncookies > 0 &&
(dp->d_fileno == 0 || dp->d_type == DT_WHT || (dp->d_fileno == 0 || dp->d_type == DT_WHT ||
(not_zfs != 0 && ((u_quad_t)(*cookiep)) <= toff))) { (is_ufs == 1 && ((u_quad_t)(*cookiep)) <= toff))) {
cpos += dp->d_reclen; cpos += dp->d_reclen;
dp = (struct dirent *)cpos; dp = (struct dirent *)cpos;
cookiep++; cookiep++;