summaryrefslogtreecommitdiffstats
path: root/sys/fs/nfsclient/nfs_clvfsops.c
diff options
context:
space:
mode:
authorrmacklem <rmacklem@FreeBSD.org>2012-12-08 22:52:39 +0000
committerrmacklem <rmacklem@FreeBSD.org>2012-12-08 22:52:39 +0000
commitc82d89183db93c8a4a4a1db712fa5464d28ff9a3 (patch)
tree1d0d5926e781fef2471af6865dd51ab8fbdb8620 /sys/fs/nfsclient/nfs_clvfsops.c
parent98601953890f2c4381eda07f2914bfc1f8b114ec (diff)
downloadFreeBSD-src-c82d89183db93c8a4a4a1db712fa5464d28ff9a3.zip
FreeBSD-src-c82d89183db93c8a4a4a1db712fa5464d28ff9a3.tar.gz
Move the NFSv4.1 client patches over from projects/nfsv4.1-client
to head. I don't think the NFS client behaviour will change unless the new "minorversion=1" mount option is used. It includes basic NFSv4.1 support plus support for pNFS using the Files Layout only. All problems detecting during an NFSv4.1 Bakeathon testing event in June 2012 have been resolved in this code and it has been tested against the NFSv4.1 server available to me. Although not reviewed, I believe that kib@ has looked at it.
Diffstat (limited to 'sys/fs/nfsclient/nfs_clvfsops.c')
-rw-r--r--sys/fs/nfsclient/nfs_clvfsops.c100
1 files changed, 84 insertions, 16 deletions
diff --git a/sys/fs/nfsclient/nfs_clvfsops.c b/sys/fs/nfsclient/nfs_clvfsops.c
index 41a6b78..00dbf90 100644
--- a/sys/fs/nfsclient/nfs_clvfsops.c
+++ b/sys/fs/nfsclient/nfs_clvfsops.c
@@ -80,6 +80,8 @@ extern int nfscl_ticks;
extern struct timeval nfsboottime;
extern struct nfsstats newnfsstats;
extern int nfsrv_useacl;
+extern int nfscl_debuglevel;
+NFSCLSTATEMUTEX;
MALLOC_DEFINE(M_NEWNFSREQ, "newnfsclient_req", "New NFS request header");
MALLOC_DEFINE(M_NEWNFSMNT, "newnfsmnt", "New NFS mount struct");
@@ -104,7 +106,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, int);
+ struct thread *, int, int, int);
static void nfs_getnlminfo(struct vnode *, uint8_t *, size_t *,
struct sockaddr_storage *, int *, off_t *,
struct timeval *);
@@ -296,9 +298,11 @@ nfs_statfs(struct mount *mp, struct statfs *sbp)
if (!error)
error = nfsrpc_statfs(vp, &sb, &fs, td->td_ucred, td, &nfsva,
&attrflag, NULL);
+ if (error != 0)
+ NFSCL_DEBUG(2, "statfs=%d\n", error);
if (attrflag == 0) {
ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
- td->td_ucred, td, &nfsva, NULL);
+ td->td_ucred, td, &nfsva, NULL, NULL);
if (ret) {
/*
* Just set default values to get things going.
@@ -521,7 +525,7 @@ nfs_mountdiskless(char *path,
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_NAMETIMEO,
- NFS_DEFAULT_NEGNAMETIMEO)) != 0) {
+ NFS_DEFAULT_NEGNAMETIMEO, 0)) != 0) {
printf("nfs_mountroot: mount %s on /: %d\n", path, error);
return (error);
}
@@ -715,8 +719,8 @@ static const char *nfs_opts[] = { "from", "nfs_args",
"readdirsize", "soft", "hard", "mntudp", "tcp", "udp", "wsize", "rsize",
"retrans", "acregmin", "acregmax", "acdirmin", "acdirmax", "resvport",
"readahead", "hostname", "timeout", "addr", "fh", "nfsv3", "sec",
- "principal", "nfsv4", "gssname", "allgssname", "dirpath",
- "nametimeo", "negnametimeo", "nocto", "wcommitsize",
+ "principal", "nfsv4", "gssname", "allgssname", "dirpath", "minorversion",
+ "nametimeo", "negnametimeo", "nocto", "pnfs", "wcommitsize",
NULL };
/*
@@ -763,6 +767,7 @@ nfs_mount(struct mount *mp)
char *opt, *name, *secname;
int nametimeo = NFS_DEFAULT_NAMETIMEO;
int negnametimeo = NFS_DEFAULT_NEGNAMETIMEO;
+ int minvers = 0;
int dirlen, has_nfs_args_opt, krbnamelen, srvkrbnamelen;
size_t hstlen;
@@ -836,6 +841,8 @@ nfs_mount(struct mount *mp)
args.flags |= NFSMNT_ALLGSSNAME;
if (vfs_getopt(mp->mnt_optnew, "nocto", NULL, NULL) == 0)
args.flags |= NFSMNT_NOCTO;
+ if (vfs_getopt(mp->mnt_optnew, "pnfs", NULL, NULL) == 0)
+ args.flags |= NFSMNT_PNFS;
if (vfs_getopt(mp->mnt_optnew, "readdirsize", (void **)&opt, NULL) == 0) {
if (opt == NULL) {
vfs_mount_error(mp, "illegal readdirsize");
@@ -988,6 +995,16 @@ nfs_mount(struct mount *mp)
goto out;
}
}
+ if (vfs_getopt(mp->mnt_optnew, "minorversion", (void **)&opt, NULL) ==
+ 0) {
+ ret = sscanf(opt, "%d", &minvers);
+ if (ret != 1 || minvers < 0 || minvers > 1 ||
+ (args.flags & NFSMNT_NFSV4) == 0) {
+ vfs_mount_error(mp, "illegal minorversion: %s", opt);
+ error = EINVAL;
+ goto out;
+ }
+ }
if (vfs_getopt(mp->mnt_optnew, "sec",
(void **) &secname, NULL) == 0)
nfs_sec_name(secname, &args.flags);
@@ -1132,7 +1149,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,
- nametimeo, negnametimeo);
+ nametimeo, negnametimeo, minvers);
out:
if (!error) {
MNT_ILOCK(mp);
@@ -1176,14 +1193,20 @@ 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 nametimeo, int negnametimeo)
+ struct ucred *cred, struct thread *td, int nametimeo, int negnametimeo,
+ int minvers)
{
struct nfsmount *nmp;
struct nfsnode *np;
int error, trycnt, ret;
struct nfsvattr nfsva;
+ struct nfsclclient *clp;
+ struct nfsclds *dsp, *tdsp;
+ uint32_t lease;
static u_int64_t clval = 0;
+ NFSCL_DEBUG(3, "in mnt\n");
+ clp = NULL;
if (mp->mnt_flag & MNT_UPDATE) {
nmp = VFSTONFS(mp);
printf("%s: MNT_UPDATE is no longer handled here\n", __func__);
@@ -1259,6 +1282,10 @@ mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
nmp->nm_wcommitsize = hibufspace / (desiredvnodes / 1000);
else
nmp->nm_wcommitsize = hibufspace / 10;
+ if ((argp->flags & NFSMNT_NFSV4) != 0)
+ nmp->nm_minorvers = minvers;
+ else
+ nmp->nm_minorvers = 0;
nfs_decode_args(mp, nmp, argp, hst, cred, td);
@@ -1306,17 +1333,18 @@ mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
if ((error = newnfs_connect(nmp, &nmp->nm_sockreq, cred, td, 0)))
goto bad;
+ /* For NFSv4.1, get the clientid now. */
+ if (nmp->nm_minorvers > 0) {
+ NFSCL_DEBUG(3, "at getcl\n");
+ error = nfscl_getcl(mp, cred, td, 0, &clp);
+ NFSCL_DEBUG(3, "aft getcl=%d\n", error);
+ if (error != 0)
+ goto bad;
+ }
- /*
- * A reference count is needed on the nfsnode representing the
- * remote root. If this object is not persistent, then backward
- * traversals of the mount point (i.e. "..") will not work if
- * the nfsnode gets flushed out of the cache. Ufs does not have
- * this problem, because one can identify root inodes by their
- * number == ROOTINO (2).
- */
if (nmp->nm_fhsize == 0 && (nmp->nm_flag & NFSMNT_NFSV4) &&
nmp->nm_dirpathlen > 0) {
+ NFSCL_DEBUG(3, "in dirp\n");
/*
* If the fhsize on the mount point == 0 for V4, the mount
* path needs to be looked up.
@@ -1325,6 +1353,7 @@ mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
do {
error = nfsrpc_getdirpath(nmp, NFSMNT_DIRPATH(nmp),
cred, td);
+ NFSCL_DEBUG(3, "aft dirp=%d\n", error);
if (error)
(void) nfs_catnap(PZERO, error, "nfsgetdirp");
} while (error && --trycnt > 0);
@@ -1333,6 +1362,15 @@ mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
goto bad;
}
}
+
+ /*
+ * A reference count is needed on the nfsnode representing the
+ * remote root. If this object is not persistent, then backward
+ * traversals of the mount point (i.e. "..") will not work if
+ * the nfsnode gets flushed out of the cache. Ufs does not have
+ * this problem, because one can identify root inodes by their
+ * number == ROOTINO (2).
+ */
if (nmp->nm_fhsize > 0) {
/*
* Set f_iosize to NFS_DIRBLKSIZ so that bo_bsize gets set
@@ -1352,7 +1390,7 @@ mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
* (*vpp)->v_type with the correct value.
*/
ret = nfsrpc_getattrnovp(nmp, nmp->nm_fh, nmp->nm_fhsize, 1,
- cred, td, &nfsva, NULL);
+ cred, td, &nfsva, NULL, &lease);
if (ret) {
/*
* Just set default values to get things going.
@@ -1367,8 +1405,25 @@ mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
nfsva.na_vattr.va_gen = 1;
nfsva.na_vattr.va_blocksize = NFS_FABLKSIZE;
nfsva.na_vattr.va_size = 512 * 1024;
+ lease = 60;
}
(void) nfscl_loadattrcache(vpp, &nfsva, NULL, NULL, 0, 1);
+ if (nmp->nm_minorvers > 0) {
+ NFSCL_DEBUG(3, "lease=%d\n", (int)lease);
+ NFSLOCKCLSTATE();
+ clp->nfsc_renew = NFSCL_RENEW(lease);
+ clp->nfsc_expire = NFSD_MONOSEC + clp->nfsc_renew;
+ clp->nfsc_clientidrev++;
+ if (clp->nfsc_clientidrev == 0)
+ clp->nfsc_clientidrev++;
+ NFSUNLOCKCLSTATE();
+ /*
+ * Mount will succeed, so the renew thread can be
+ * started now.
+ */
+ nfscl_start_renewthread(clp);
+ nfscl_clientrelease(clp);
+ }
if (argp->flags & NFSMNT_NFSV3)
ncl_fsinfo(nmp, *vpp, cred, td);
@@ -1390,10 +1445,20 @@ mountnfs(struct nfs_args *argp, struct mount *mp, struct sockaddr *nam,
error = EIO;
bad:
+ if (clp != NULL)
+ nfscl_clientrelease(clp);
newnfs_disconnect(&nmp->nm_sockreq);
crfree(nmp->nm_sockreq.nr_cred);
mtx_destroy(&nmp->nm_sockreq.nr_mtx);
mtx_destroy(&nmp->nm_mtx);
+ if (nmp->nm_clp != NULL) {
+ NFSLOCKCLSTATE();
+ LIST_REMOVE(nmp->nm_clp, nfsc_list);
+ NFSUNLOCKCLSTATE();
+ free(nmp->nm_clp, M_NFSCLCLIENT);
+ }
+ TAILQ_FOREACH_SAFE(dsp, &nmp->nm_sess, nfsclds_list, tdsp)
+ nfscl_freenfsclds(dsp);
FREE(nmp, M_NEWNFSMNT);
FREE(nam, M_SONAME);
return (error);
@@ -1408,6 +1473,7 @@ nfs_unmount(struct mount *mp, int mntflags)
struct thread *td;
struct nfsmount *nmp;
int error, flags = 0, trycnt = 0;
+ struct nfsclds *dsp, *tdsp;
td = curthread;
@@ -1448,6 +1514,8 @@ nfs_unmount(struct mount *mp, int mntflags)
mtx_destroy(&nmp->nm_sockreq.nr_mtx);
mtx_destroy(&nmp->nm_mtx);
+ TAILQ_FOREACH_SAFE(dsp, &nmp->nm_sess, nfsclds_list, tdsp)
+ nfscl_freenfsclds(dsp);
FREE(nmp, M_NEWNFSMNT);
out:
return (error);
OpenPOWER on IntegriCloud