Merge commit 'v2.6.30' into for-2.6.31
authorJ. Bruce Fields <bfields@citi.umich.edu>
Tue, 16 Jun 2009 01:08:07 +0000 (18:08 -0700)
committerJ. Bruce Fields <bfields@citi.umich.edu>
Tue, 16 Jun 2009 01:08:07 +0000 (18:08 -0700)
23 files changed:
Documentation/filesystems/00-INDEX
fs/Kconfig
fs/lockd/svclock.c
fs/locks.c
fs/nfs/Kconfig
fs/nfsd/export.c
fs/nfsd/nfs3xdr.c
fs/nfsd/nfs4callback.c
fs/nfsd/nfs4proc.c
fs/nfsd/nfs4state.c
fs/nfsd/nfs4xdr.c
fs/nfsd/nfscache.c
fs/nfsd/nfsctl.c
fs/nfsd/nfsfh.c
include/linux/fs.h
include/linux/nfsd/cache.h
include/linux/nfsd/nfsfh.h
include/linux/nfsd/state.h
include/linux/nfsd/xdr4.h
include/linux/sunrpc/svc_xprt.h
include/linux/sunrpc/svcsock.h
net/sunrpc/svc_xprt.c
net/sunrpc/svcsock.c

index 8dd6db76171deb5c24fca888676162e5d9c10676..f15621ee55996e0a0928f08783ddcaa43ddfee21 100644 (file)
@@ -66,6 +66,10 @@ mandatory-locking.txt
        - info on the Linux implementation of Sys V mandatory file locking.
 ncpfs.txt
        - info on Novell Netware(tm) filesystem using NCP protocol.
+nfs41-server.txt
+       - info on the Linux server implementation of NFSv4 minor version 1.
+nfs-rdma.txt
+       - how to install and setup the Linux NFS/RDMA client and server software.
 nfsroot.txt
        - short guide on setting up a diskless box with NFS root filesystem.
 nilfs2.txt
index 9f7270f36b2af4e207b26ff52f9b98ad502d8c00..44ab328ceb2a03e08e2834145b85db951efeb705 100644 (file)
@@ -226,10 +226,12 @@ source "fs/nfsd/Kconfig"
 
 config LOCKD
        tristate
+       depends on FILE_LOCKING
 
 config LOCKD_V4
        bool
        depends on NFSD_V3 || NFS_V3
+       depends on FILE_LOCKING
        default y
 
 config EXPORTFS
index 83ee34203bd7748ffa138a93c21ea964b140a1b8..e577a78d7bacf9e07d3cb50f4bb657d30d5e9446 100644 (file)
@@ -326,6 +326,8 @@ static void nlmsvc_freegrantargs(struct nlm_rqst *call)
 {
        if (call->a_args.lock.oh.data != call->a_owner)
                kfree(call->a_args.lock.oh.data);
+
+       locks_release_private(&call->a_args.lock.fl);
 }
 
 /*
index ec3deea29e37cbd581488087e94053708dd100b1..b6440f52178fad125f3d0101d7a97f82ad2cf3ee 100644 (file)
@@ -151,7 +151,7 @@ static struct file_lock *locks_alloc_lock(void)
        return kmem_cache_alloc(filelock_cache, GFP_KERNEL);
 }
 
-static void locks_release_private(struct file_lock *fl)
+void locks_release_private(struct file_lock *fl)
 {
        if (fl->fl_ops) {
                if (fl->fl_ops->fl_release_private)
@@ -165,6 +165,7 @@ static void locks_release_private(struct file_lock *fl)
        }
 
 }
+EXPORT_SYMBOL_GPL(locks_release_private);
 
 /* Free a lock which is not in use. */
 static void locks_free_lock(struct file_lock *fl)
index e67f3ec0773681c17dcf906e0cddfbd12d9ea1bc..7dbb8f27b9d6c914fa9b5147372071abe61f53c8 100644 (file)
@@ -1,6 +1,6 @@
 config NFS_FS
        tristate "NFS client support"
-       depends on INET
+       depends on INET && FILE_LOCKING
        select LOCKD
        select SUNRPC
        select NFS_ACL_SUPPORT if NFS_V3_ACL
index 5839b229cd0ea9bbd631441b89a07e639be0a98f..6eb918153fd41d49529d350c424619df23bc0c3a 100644 (file)
@@ -464,16 +464,11 @@ static int secinfo_parse(char **mesg, char *buf, struct svc_export *exp)
                if (err)
                        return err;
                /*
-                * Just a quick sanity check; we could also try to check
-                * whether this pseudoflavor is supported, but at worst
-                * an unsupported pseudoflavor on the export would just
-                * be a pseudoflavor that won't match the flavor of any
-                * authenticated request.  The administrator will
-                * probably discover the problem when someone fails to
-                * authenticate.
+                * XXX: It would be nice to also check whether this
+                * pseudoflavor is supported, so we can discover the
+                * problem at export time instead of when a client fails
+                * to authenticate.
                 */
-               if (f->pseudoflavor < 0)
-                       return -EINVAL;
                err = get_int(mesg, &f->flags);
                if (err)
                        return err;
index 17d0dd997204a8442efc3514c98045c81363730c..01d4ec1c88e0ff064ed2b5de697257766945c412 100644 (file)
@@ -272,6 +272,7 @@ void fill_post_wcc(struct svc_fh *fhp)
 
        err = vfs_getattr(fhp->fh_export->ex_path.mnt, fhp->fh_dentry,
                        &fhp->fh_post_attr);
+       fhp->fh_post_change = fhp->fh_dentry->d_inode->i_version;
        if (err)
                fhp->fh_post_saved = 0;
        else
index 290289bd44f76ccadb54614df6f5bb4775be1f35..f4fab69a8c30c7fc5f733cde9dd961c2875b349a 100644 (file)
@@ -215,18 +215,18 @@ encode_cb_compound_hdr(struct xdr_stream *xdr, struct nfs4_cb_compound_hdr *hdr)
 }
 
 static int
-encode_cb_recall(struct xdr_stream *xdr, struct nfs4_cb_recall *cb_rec)
+encode_cb_recall(struct xdr_stream *xdr, struct nfs4_delegation *dp)
 {
        __be32 *p;
-       int len = cb_rec->cbr_fh.fh_size;
+       int len = dp->dl_fh.fh_size;
 
-       RESERVE_SPACE(12+sizeof(cb_rec->cbr_stateid) + len);
+       RESERVE_SPACE(12+sizeof(dp->dl_stateid) + len);
        WRITE32(OP_CB_RECALL);
-       WRITE32(cb_rec->cbr_stateid.si_generation);
-       WRITEMEM(&cb_rec->cbr_stateid.si_opaque, sizeof(stateid_opaque_t));
-       WRITE32(cb_rec->cbr_trunc);
+       WRITE32(dp->dl_stateid.si_generation);
+       WRITEMEM(&dp->dl_stateid.si_opaque, sizeof(stateid_opaque_t));
+       WRITE32(0); /* truncate optimization not implemented */
        WRITE32(len);
-       WRITEMEM(&cb_rec->cbr_fh.fh_base, len);
+       WRITEMEM(&dp->dl_fh.fh_base, len);
        return 0;
 }
 
@@ -241,11 +241,11 @@ nfs4_xdr_enc_cb_null(struct rpc_rqst *req, __be32 *p)
 }
 
 static int
-nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p, struct nfs4_cb_recall *args)
+nfs4_xdr_enc_cb_recall(struct rpc_rqst *req, __be32 *p, struct nfs4_delegation *args)
 {
        struct xdr_stream xdr;
        struct nfs4_cb_compound_hdr hdr = {
-               .ident = args->cbr_ident,
+               .ident = args->dl_ident,
                .nops   = 1,
        };
 
@@ -358,18 +358,21 @@ static struct rpc_program cb_program = {
                .pipe_dir_name  = "/nfsd4_cb",
 };
 
+static int max_cb_time(void)
+{
+       return max(NFSD_LEASE_TIME/10, (time_t)1) * HZ;
+}
+
 /* Reference counting, callback cleanup, etc., all look racy as heck.
  * And why is cb_set an atomic? */
 
-static struct rpc_clnt *setup_callback_client(struct nfs4_client *clp)
+int setup_callback_client(struct nfs4_client *clp)
 {
        struct sockaddr_in      addr;
-       struct nfs4_callback    *cb = &clp->cl_callback;
+       struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
        struct rpc_timeout      timeparms = {
-               .to_initval     = (NFSD_LEASE_TIME/4) * HZ,
-               .to_retries     = 5,
-               .to_maxval      = (NFSD_LEASE_TIME/2) * HZ,
-               .to_exponential = 1,
+               .to_initval     = max_cb_time(),
+               .to_retries     = 0,
        };
        struct rpc_create_args args = {
                .protocol       = IPPROTO_TCP,
@@ -386,7 +389,7 @@ static struct rpc_clnt *setup_callback_client(struct nfs4_client *clp)
        struct rpc_clnt *client;
 
        if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5))
-               return ERR_PTR(-EINVAL);
+               return -EINVAL;
 
        /* Initialize address */
        memset(&addr, 0, sizeof(addr));
@@ -396,48 +399,77 @@ static struct rpc_clnt *setup_callback_client(struct nfs4_client *clp)
 
        /* Create RPC client */
        client = rpc_create(&args);
-       if (IS_ERR(client))
+       if (IS_ERR(client)) {
                dprintk("NFSD: couldn't create callback client: %ld\n",
                        PTR_ERR(client));
-       return client;
+               return PTR_ERR(client);
+       }
+       cb->cb_client = client;
+       return 0;
+
+}
+
+static void warn_no_callback_path(struct nfs4_client *clp, int reason)
+{
+       dprintk("NFSD: warning: no callback path to client %.*s: error %d\n",
+               (int)clp->cl_name.len, clp->cl_name.data, reason);
+}
+
+static void nfsd4_cb_probe_done(struct rpc_task *task, void *calldata)
+{
+       struct nfs4_client *clp = calldata;
+
+       if (task->tk_status)
+               warn_no_callback_path(clp, task->tk_status);
+       else
+               atomic_set(&clp->cl_cb_conn.cb_set, 1);
+       put_nfs4_client(clp);
+}
+
+static const struct rpc_call_ops nfsd4_cb_probe_ops = {
+       .rpc_call_done = nfsd4_cb_probe_done,
+};
 
+static struct rpc_cred *lookup_cb_cred(struct nfs4_cb_conn *cb)
+{
+       struct auth_cred acred = {
+               .machine_cred = 1
+       };
+
+       /*
+        * Note in the gss case this doesn't actually have to wait for a
+        * gss upcall (or any calls to the client); this just creates a
+        * non-uptodate cred which the rpc state machine will fill in with
+        * a refresh_upcall later.
+        */
+       return rpcauth_lookup_credcache(cb->cb_client->cl_auth, &acred,
+                                                       RPCAUTH_LOOKUP_NEW);
 }
 
-static int do_probe_callback(void *data)
+void do_probe_callback(struct nfs4_client *clp)
 {
-       struct nfs4_client *clp = data;
-       struct nfs4_callback    *cb = &clp->cl_callback;
+       struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
        struct rpc_message msg = {
                .rpc_proc       = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
                .rpc_argp       = clp,
        };
-       struct rpc_clnt *client;
+       struct rpc_cred *cred;
        int status;
 
-       client = setup_callback_client(clp);
-       if (IS_ERR(client)) {
-               status = PTR_ERR(client);
-               dprintk("NFSD: couldn't create callback client: %d\n",
-                                                               status);
-               goto out_err;
+       cred = lookup_cb_cred(cb);
+       if (IS_ERR(cred)) {
+               status = PTR_ERR(cred);
+               goto out;
+       }
+       cb->cb_cred = cred;
+       msg.rpc_cred = cb->cb_cred;
+       status = rpc_call_async(cb->cb_client, &msg, RPC_TASK_SOFT,
+                               &nfsd4_cb_probe_ops, (void *)clp);
+out:
+       if (status) {
+               warn_no_callback_path(clp, status);
+               put_nfs4_client(clp);
        }
-
-       status = rpc_call_sync(client, &msg, RPC_TASK_SOFT);
-
-       if (status)
-               goto out_release_client;
-
-       cb->cb_client = client;
-       atomic_set(&cb->cb_set, 1);
-       put_nfs4_client(clp);
-       return 0;
-out_release_client:
-       rpc_shutdown_client(client);
-out_err:
-       dprintk("NFSD: warning: no callback path to client %.*s: error %d\n",
-               (int)clp->cl_name.len, clp->cl_name.data, status);
-       put_nfs4_client(clp);
-       return 0;
 }
 
 /*
@@ -446,21 +478,65 @@ out_err:
 void
 nfsd4_probe_callback(struct nfs4_client *clp)
 {
-       struct task_struct *t;
+       int status;
+
+       BUG_ON(atomic_read(&clp->cl_cb_conn.cb_set));
 
-       BUG_ON(atomic_read(&clp->cl_callback.cb_set));
+       status = setup_callback_client(clp);
+       if (status) {
+               warn_no_callback_path(clp, status);
+               return;
+       }
 
        /* the task holds a reference to the nfs4_client struct */
        atomic_inc(&clp->cl_count);
 
-       t = kthread_run(do_probe_callback, clp, "nfs4_cb_probe");
+       do_probe_callback(clp);
+}
 
-       if (IS_ERR(t))
-               atomic_dec(&clp->cl_count);
+static void nfsd4_cb_recall_done(struct rpc_task *task, void *calldata)
+{
+       struct nfs4_delegation *dp = calldata;
+       struct nfs4_client *clp = dp->dl_client;
 
-       return;
+       switch (task->tk_status) {
+       case -EIO:
+               /* Network partition? */
+               atomic_set(&clp->cl_cb_conn.cb_set, 0);
+               warn_no_callback_path(clp, task->tk_status);
+       case -EBADHANDLE:
+       case -NFS4ERR_BAD_STATEID:
+               /* Race: client probably got cb_recall
+                * before open reply granting delegation */
+               break;
+       default:
+               /* success, or error we can't handle */
+               return;
+       }
+       if (dp->dl_retries--) {
+               rpc_delay(task, 2*HZ);
+               task->tk_status = 0;
+               rpc_restart_call(task);
+       } else {
+               atomic_set(&clp->cl_cb_conn.cb_set, 0);
+               warn_no_callback_path(clp, task->tk_status);
+       }
 }
 
+static void nfsd4_cb_recall_release(void *calldata)
+{
+       struct nfs4_delegation *dp = calldata;
+       struct nfs4_client *clp = dp->dl_client;
+
+       nfs4_put_delegation(dp);
+       put_nfs4_client(clp);
+}
+
+static const struct rpc_call_ops nfsd4_cb_recall_ops = {
+       .rpc_call_done = nfsd4_cb_recall_done,
+       .rpc_release = nfsd4_cb_recall_release,
+};
+
 /*
  * called with dp->dl_count inc'ed.
  */
@@ -468,41 +544,19 @@ void
 nfsd4_cb_recall(struct nfs4_delegation *dp)
 {
        struct nfs4_client *clp = dp->dl_client;
-       struct rpc_clnt *clnt = clp->cl_callback.cb_client;
-       struct nfs4_cb_recall *cbr = &dp->dl_recall;
+       struct rpc_clnt *clnt = clp->cl_cb_conn.cb_client;
        struct rpc_message msg = {
                .rpc_proc = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_RECALL],
-               .rpc_argp = cbr,
+               .rpc_argp = dp,
+               .rpc_cred = clp->cl_cb_conn.cb_cred
        };
-       int retries = 1;
-       int status = 0;
-
-       cbr->cbr_trunc = 0; /* XXX need to implement truncate optimization */
-       cbr->cbr_dp = dp;
-
-       status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFT);
-       while (retries--) {
-               switch (status) {
-                       case -EIO:
-                               /* Network partition? */
-                               atomic_set(&clp->cl_callback.cb_set, 0);
-                       case -EBADHANDLE:
-                       case -NFS4ERR_BAD_STATEID:
-                               /* Race: client probably got cb_recall
-                                * before open reply granting delegation */
-                               break;
-                       default:
-                               goto out_put_cred;
-               }
-               ssleep(2);
-               status = rpc_call_sync(clnt, &msg, RPC_TASK_SOFT);
+       int status;
+
+       dp->dl_retries = 1;
+       status = rpc_call_async(clnt, &msg, RPC_TASK_SOFT,
+                               &nfsd4_cb_recall_ops, dp);
+       if (status) {
+               put_nfs4_client(clp);
+               nfs4_put_delegation(dp);
        }
-out_put_cred:
-       /*
-        * Success or failure, now we're either waiting for lease expiration
-        * or deleg_return.
-        */
-       put_nfs4_client(clp);
-       nfs4_put_delegation(dp);
-       return;
 }
index b2883e9c6381f22b51bc788174d149a20a5e2e54..7c8801769a3cf8279dc74b980775e1437f98ccd6 100644 (file)
 
 #define NFSDDBG_FACILITY               NFSDDBG_PROC
 
+static u32 nfsd_attrmask[] = {
+       NFSD_WRITEABLE_ATTRS_WORD0,
+       NFSD_WRITEABLE_ATTRS_WORD1,
+       NFSD_WRITEABLE_ATTRS_WORD2
+};
+
+static u32 nfsd41_ex_attrmask[] = {
+       NFSD_SUPPATTR_EXCLCREAT_WORD0,
+       NFSD_SUPPATTR_EXCLCREAT_WORD1,
+       NFSD_SUPPATTR_EXCLCREAT_WORD2
+};
+
+static __be32
+check_attr_support(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
+                  u32 *bmval, u32 *writable)
+{
+       struct dentry *dentry = cstate->current_fh.fh_dentry;
+       struct svc_export *exp = cstate->current_fh.fh_export;
+
+       /*
+        * Check about attributes are supported by the NFSv4 server or not.
+        * According to spec, unsupported attributes return ERR_ATTRNOTSUPP.
+        */
+       if ((bmval[0] & ~nfsd_suppattrs0(cstate->minorversion)) ||
+           (bmval[1] & ~nfsd_suppattrs1(cstate->minorversion)) ||
+           (bmval[2] & ~nfsd_suppattrs2(cstate->minorversion)))
+               return nfserr_attrnotsupp;
+
+       /*
+        * Check FATTR4_WORD0_ACL & FATTR4_WORD0_FS_LOCATIONS can be supported
+        * in current environment or not.
+        */
+       if (bmval[0] & FATTR4_WORD0_ACL) {
+               if (!IS_POSIXACL(dentry->d_inode))
+                       return nfserr_attrnotsupp;
+       }
+       if (bmval[0] & FATTR4_WORD0_FS_LOCATIONS) {
+               if (exp->ex_fslocs.locations == NULL)
+                       return nfserr_attrnotsupp;
+       }
+
+       /*
+        * According to spec, read-only attributes return ERR_INVAL.
+        */
+       if (writable) {
+               if ((bmval[0] & ~writable[0]) || (bmval[1] & ~writable[1]) ||
+                   (bmval[2] & ~writable[2]))
+                       return nfserr_inval;
+       }
+
+       return nfs_ok;
+}
+
+static __be32
+nfsd4_check_open_attributes(struct svc_rqst *rqstp,
+       struct nfsd4_compound_state *cstate, struct nfsd4_open *open)
+{
+       __be32 status = nfs_ok;
+
+       if (open->op_create == NFS4_OPEN_CREATE) {
+               if (open->op_createmode == NFS4_CREATE_UNCHECKED
+                   || open->op_createmode == NFS4_CREATE_GUARDED)
+                       status = check_attr_support(rqstp, cstate,
+                                       open->op_bmval, nfsd_attrmask);
+               else if (open->op_createmode == NFS4_CREATE_EXCLUSIVE4_1)
+                       status = check_attr_support(rqstp, cstate,
+                                       open->op_bmval, nfsd41_ex_attrmask);
+       }
+
+       return status;
+}
+
 static inline void
 fh_dup2(struct svc_fh *dst, struct svc_fh *src)
 {
@@ -225,6 +297,10 @@ nfsd4_open(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (status)
                goto out;
 
+       status = nfsd4_check_open_attributes(rqstp, cstate, open);
+       if (status)
+               goto out;
+
        /* Openowner is now set, so sequence id will get bumped.  Now we need
         * these checks before we do any creates: */
        status = nfserr_grace;
@@ -395,6 +471,11 @@ nfsd4_create(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (status)
                return status;
 
+       status = check_attr_support(rqstp, cstate, create->cr_bmval,
+                                   nfsd_attrmask);
+       if (status)
+               return status;
+
        switch (create->cr_type) {
        case NF4LNK:
                /* ugh! we have to null-terminate the linktext, or
@@ -689,6 +770,12 @@ nfsd4_setattr(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (status)
                return status;
        status = nfs_ok;
+
+       status = check_attr_support(rqstp, cstate, setattr->sa_bmval,
+                                   nfsd_attrmask);
+       if (status)
+               goto out;
+
        if (setattr->sa_acl != NULL)
                status = nfsd4_set_nfs4_acl(rqstp, &cstate->current_fh,
                                            setattr->sa_acl);
@@ -763,10 +850,10 @@ _nfsd4_verify(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (status)
                return status;
 
-       if ((verify->ve_bmval[0] & ~nfsd_suppattrs0(cstate->minorversion))
-           || (verify->ve_bmval[1] & ~nfsd_suppattrs1(cstate->minorversion))
-           || (verify->ve_bmval[2] & ~nfsd_suppattrs2(cstate->minorversion)))
-               return nfserr_attrnotsupp;
+       status = check_attr_support(rqstp, cstate, verify->ve_bmval, NULL);
+       if (status)
+               return status;
+
        if ((verify->ve_bmval[0] & FATTR4_WORD0_RDATTR_ERROR)
            || (verify->ve_bmval[1] & NFSD_WRITEONLY_ATTRS_WORD1))
                return nfserr_inval;
@@ -1226,24 +1313,9 @@ static const char *nfsd4_op_name(unsigned opnum)
        return "unknown_operation";
 }
 
-#define nfs4svc_decode_voidargs                NULL
-#define nfs4svc_release_void           NULL
 #define nfsd4_voidres                  nfsd4_voidargs
-#define nfs4svc_release_compound       NULL
 struct nfsd4_voidargs { int dummy; };
 
-#define PROC(name, argt, rest, relt, cache, respsize)  \
- { (svc_procfunc) nfsd4_proc_##name,           \
-   (kxdrproc_t) nfs4svc_decode_##argt##args,   \
-   (kxdrproc_t) nfs4svc_encode_##rest##res,    \
-   (kxdrproc_t) nfs4svc_release_##relt,                \
-   sizeof(struct nfsd4_##argt##args),          \
-   sizeof(struct nfsd4_##rest##res),           \
-   0,                                          \
-   cache,                                      \
-   respsize,                                   \
- }
-
 /*
  * TODO: At the present time, the NFSv4 server does not do XID caching
  * of requests.  Implementing XID caching would not be a serious problem,
@@ -1255,8 +1327,23 @@ struct nfsd4_voidargs { int dummy; };
  * better XID's.
  */
 static struct svc_procedure            nfsd_procedures4[2] = {
-  PROC(null,    void,          void,           void,     RC_NOCACHE, 1),
-  PROC(compound, compound,     compound,       compound, RC_NOCACHE, NFSD_BUFSIZE/4)
+       [NFSPROC4_NULL] = {
+               .pc_func = (svc_procfunc) nfsd4_proc_null,
+               .pc_encode = (kxdrproc_t) nfs4svc_encode_voidres,
+               .pc_argsize = sizeof(struct nfsd4_voidargs),
+               .pc_ressize = sizeof(struct nfsd4_voidres),
+               .pc_cachetype = RC_NOCACHE,
+               .pc_xdrressize = 1,
+       },
+       [NFSPROC4_COMPOUND] = {
+               .pc_func = (svc_procfunc) nfsd4_proc_compound,
+               .pc_decode = (kxdrproc_t) nfs4svc_decode_compoundargs,
+               .pc_encode = (kxdrproc_t) nfs4svc_encode_compoundres,
+               .pc_argsize = sizeof(struct nfsd4_compoundargs),
+               .pc_ressize = sizeof(struct nfsd4_compoundres),
+               .pc_cachetype = RC_NOCACHE,
+               .pc_xdrressize = NFSD_BUFSIZE/4,
+       },
 };
 
 struct svc_version     nfsd_version4 = {
index 3b711f5147a75c5fb8bb04be8477fe5c7d866ff4..89d9ac55c0349838c541b468919d0447e5f5a5fa 100644 (file)
@@ -182,7 +182,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
 {
        struct nfs4_delegation *dp;
        struct nfs4_file *fp = stp->st_file;
-       struct nfs4_callback *cb = &stp->st_stateowner->so_client->cl_callback;
+       struct nfs4_cb_conn *cb = &stp->st_stateowner->so_client->cl_cb_conn;
 
        dprintk("NFSD alloc_init_deleg\n");
        if (fp->fi_had_conflict)
@@ -203,10 +203,8 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
        get_file(stp->st_vfs_file);
        dp->dl_vfs_file = stp->st_vfs_file;
        dp->dl_type = type;
-       dp->dl_recall.cbr_dp = NULL;
-       dp->dl_recall.cbr_ident = cb->cb_ident;
-       dp->dl_recall.cbr_trunc = 0;
-       dp->dl_stateid.si_boot = boot_time;
+       dp->dl_ident = cb->cb_ident;
+       dp->dl_stateid.si_boot = get_seconds();
        dp->dl_stateid.si_stateownerid = current_delegid++;
        dp->dl_stateid.si_fileid = 0;
        dp->dl_stateid.si_generation = 0;
@@ -632,16 +630,20 @@ static struct nfs4_client *alloc_client(struct xdr_netobj name)
 static void
 shutdown_callback_client(struct nfs4_client *clp)
 {
-       struct rpc_clnt *clnt = clp->cl_callback.cb_client;
+       struct rpc_clnt *clnt = clp->cl_cb_conn.cb_client;
 
        if (clnt) {
                /*
                 * Callback threads take a reference on the client, so there
                 * should be no outstanding callbacks at this point.
                 */
-               clp->cl_callback.cb_client = NULL;
+               clp->cl_cb_conn.cb_client = NULL;
                rpc_shutdown_client(clnt);
        }
+       if (clp->cl_cb_conn.cb_cred) {
+               put_rpccred(clp->cl_cb_conn.cb_cred);
+               clp->cl_cb_conn.cb_cred = NULL;
+       }
 }
 
 static inline void
@@ -714,7 +716,7 @@ static struct nfs4_client *create_client(struct xdr_netobj name, char *recdir)
                return NULL;
        memcpy(clp->cl_recdir, recdir, HEXDIR_LEN);
        atomic_set(&clp->cl_count, 1);
-       atomic_set(&clp->cl_callback.cb_set, 0);
+       atomic_set(&clp->cl_cb_conn.cb_set, 0);
        INIT_LIST_HEAD(&clp->cl_idhash);
        INIT_LIST_HEAD(&clp->cl_strhash);
        INIT_LIST_HEAD(&clp->cl_openowners);
@@ -966,7 +968,7 @@ parse_ipv4(unsigned int addr_len, char *addr_val, unsigned int *cbaddrp, unsigne
 static void
 gen_callback(struct nfs4_client *clp, struct nfsd4_setclientid *se)
 {
-       struct nfs4_callback *cb = &clp->cl_callback;
+       struct nfs4_cb_conn *cb = &clp->cl_cb_conn;
 
        /* Currently, we only support tcp for the callback channel */
        if ((se->se_callback_netid_len != 3) || memcmp((char *)se->se_callback_netid_val, "tcp", 3))
@@ -1686,9 +1688,7 @@ nfsd4_setclientid_confirm(struct svc_rqst *rqstp,
                else {
                        /* XXX: We just turn off callbacks until we can handle
                          * change request correctly. */
-                       atomic_set(&conf->cl_callback.cb_set, 0);
-                       gen_confirm(conf);
-                       nfsd4_remove_clid_dir(unconf);
+                       atomic_set(&conf->cl_cb_conn.cb_set, 0);
                        expire_client(unconf);
                        status = nfs_ok;
 
@@ -1882,7 +1882,7 @@ init_stateid(struct nfs4_stateid *stp, struct nfs4_file *fp, struct nfsd4_open *
        stp->st_stateowner = sop;
        get_nfs4_file(fp);
        stp->st_file = fp;
-       stp->st_stateid.si_boot = boot_time;
+       stp->st_stateid.si_boot = get_seconds();
        stp->st_stateid.si_stateownerid = sop->so_id;
        stp->st_stateid.si_fileid = fp->fi_id;
        stp->st_stateid.si_generation = 0;
@@ -2058,19 +2058,6 @@ nfs4_file_downgrade(struct file *filp, unsigned int share_access)
        }
 }
 
-/*
- * Recall a delegation
- */
-static int
-do_recall(void *__dp)
-{
-       struct nfs4_delegation *dp = __dp;
-
-       dp->dl_file->fi_had_conflict = true;
-       nfsd4_cb_recall(dp);
-       return 0;
-}
-
 /*
  * Spawn a thread to perform a recall on the delegation represented
  * by the lease (file_lock)
@@ -2082,8 +2069,7 @@ do_recall(void *__dp)
 static
 void nfsd_break_deleg_cb(struct file_lock *fl)
 {
-       struct nfs4_delegation *dp=  (struct nfs4_delegation *)fl->fl_owner;
-       struct task_struct *t;
+       struct nfs4_delegation *dp = (struct nfs4_delegation *)fl->fl_owner;
 
        dprintk("NFSD nfsd_break_deleg_cb: dp %p fl %p\n",dp,fl);
        if (!dp)
@@ -2111,16 +2097,8 @@ void nfsd_break_deleg_cb(struct file_lock *fl)
         */
        fl->fl_break_time = 0;
 
-       t = kthread_run(do_recall, dp, "%s", "nfs4_cb_recall");
-       if (IS_ERR(t)) {
-               struct nfs4_client *clp = dp->dl_client;
-
-               printk(KERN_INFO "NFSD: Callback thread failed for "
-                       "for client (clientid %08x/%08x)\n",
-                       clp->cl_clientid.cl_boot, clp->cl_clientid.cl_id);
-               put_nfs4_client(dp->dl_client);
-               nfs4_put_delegation(dp);
-       }
+       dp->dl_file->fi_had_conflict = true;
+       nfsd4_cb_recall(dp);
 }
 
 /*
@@ -2422,7 +2400,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta
 {
        struct nfs4_delegation *dp;
        struct nfs4_stateowner *sop = stp->st_stateowner;
-       struct nfs4_callback *cb = &sop->so_client->cl_callback;
+       struct nfs4_cb_conn *cb = &sop->so_client->cl_cb_conn;
        struct file_lock fl, *flp = &fl;
        int status, flag = 0;
 
@@ -2614,7 +2592,7 @@ nfsd4_renew(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        renew_client(clp);
        status = nfserr_cb_path_down;
        if (!list_empty(&clp->cl_delegations)
-                       && !atomic_read(&clp->cl_callback.cb_set))
+                       && !atomic_read(&clp->cl_cb_conn.cb_set))
                goto out;
        status = nfs_ok;
 out:
@@ -2738,12 +2716,42 @@ nfs4_check_fh(struct svc_fh *fhp, struct nfs4_stateid *stp)
 static int
 STALE_STATEID(stateid_t *stateid)
 {
-       if (stateid->si_boot == boot_time)
-               return 0;
-       dprintk("NFSD: stale stateid (%08x/%08x/%08x/%08x)!\n",
-               stateid->si_boot, stateid->si_stateownerid, stateid->si_fileid,
-               stateid->si_generation);
-       return 1;
+       if (time_after((unsigned long)boot_time,
+                       (unsigned long)stateid->si_boot)) {
+               dprintk("NFSD: stale stateid (%08x/%08x/%08x/%08x)!\n",
+                       stateid->si_boot, stateid->si_stateownerid,
+                       stateid->si_fileid, stateid->si_generation);
+               return 1;
+       }
+       return 0;
+}
+
+static int
+EXPIRED_STATEID(stateid_t *stateid)
+{
+       if (time_before((unsigned long)boot_time,
+                       ((unsigned long)stateid->si_boot)) &&
+           time_before((unsigned long)(stateid->si_boot + lease_time), get_seconds())) {
+               dprintk("NFSD: expired stateid (%08x/%08x/%08x/%08x)!\n",
+                       stateid->si_boot, stateid->si_stateownerid,
+                       stateid->si_fileid, stateid->si_generation);
+               return 1;
+       }
+       return 0;
+}
+
+static __be32
+stateid_error_map(stateid_t *stateid)
+{
+       if (STALE_STATEID(stateid))
+               return nfserr_stale_stateid;
+       if (EXPIRED_STATEID(stateid))
+               return nfserr_expired;
+
+       dprintk("NFSD: bad stateid (%08x/%08x/%08x/%08x)!\n",
+               stateid->si_boot, stateid->si_stateownerid,
+               stateid->si_fileid, stateid->si_generation);
+       return nfserr_bad_stateid;
 }
 
 static inline int
@@ -2867,8 +2875,10 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
        status = nfserr_bad_stateid;
        if (is_delegation_stateid(stateid)) {
                dp = find_delegation_stateid(ino, stateid);
-               if (!dp)
+               if (!dp) {
+                       status = stateid_error_map(stateid);
                        goto out;
+               }
                status = check_stateid_generation(stateid, &dp->dl_stateid,
                                                  flags);
                if (status)
@@ -2881,8 +2891,10 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
                        *filpp = dp->dl_vfs_file;
        } else { /* open or lock stateid */
                stp = find_stateid(stateid, flags);
-               if (!stp)
+               if (!stp) {
+                       status = stateid_error_map(stateid);
                        goto out;
+               }
                if (nfs4_check_fh(current_fh, stp))
                        goto out;
                if (!stp->st_stateowner->so_confirmed)
@@ -2956,7 +2968,7 @@ nfs4_preprocess_seqid_op(struct nfsd4_compound_state *cstate, u32 seqid,
                 */
                sop = search_close_lru(stateid->si_stateownerid, flags);
                if (sop == NULL)
-                       return nfserr_bad_stateid;
+                       return stateid_error_map(stateid);
                *sopp = sop;
                goto check_replay;
        }
@@ -3227,8 +3239,10 @@ nfsd4_delegreturn(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        if (!is_delegation_stateid(stateid))
                goto out;
        dp = find_delegation_stateid(inode, stateid);
-       if (!dp)
+       if (!dp) {
+               status = stateid_error_map(stateid);
                goto out;
+       }
        status = check_stateid_generation(stateid, &dp->dl_stateid, flags);
        if (status)
                goto out;
@@ -3455,7 +3469,7 @@ alloc_init_lock_stateid(struct nfs4_stateowner *sop, struct nfs4_file *fp, struc
        stp->st_stateowner = sop;
        get_nfs4_file(fp);
        stp->st_file = fp;
-       stp->st_stateid.si_boot = boot_time;
+       stp->st_stateid.si_boot = get_seconds();
        stp->st_stateid.si_stateownerid = sop->so_id;
        stp->st_stateid.si_fileid = fp->fi_id;
        stp->st_stateid.si_generation = 0;
@@ -3987,6 +4001,7 @@ nfs4_state_init(void)
                INIT_LIST_HEAD(&conf_str_hashtbl[i]);
                INIT_LIST_HEAD(&unconf_str_hashtbl[i]);
                INIT_LIST_HEAD(&unconf_id_hashtbl[i]);
+               INIT_LIST_HEAD(&reclaim_str_hashtbl[i]);
        }
        for (i = 0; i < SESSION_HASH_SIZE; i++)
                INIT_LIST_HEAD(&sessionid_hashtbl[i]);
@@ -4009,8 +4024,6 @@ nfs4_state_init(void)
        INIT_LIST_HEAD(&close_lru);
        INIT_LIST_HEAD(&client_lru);
        INIT_LIST_HEAD(&del_recall_lru);
-       for (i = 0; i < CLIENT_HASH_SIZE; i++)
-               INIT_LIST_HEAD(&reclaim_str_hashtbl[i]);
        reclaim_str_hashtbl_size = 0;
        return 0;
 }
index b73549d293bef81e7f82bbbde7ef175ed419e137..d07f704a2ac9b381428fe7830d4e78d21e628974 100644 (file)
@@ -83,16 +83,6 @@ check_filename(char *str, int len, __be32 err)
        return 0;
 }
 
-/*
- * START OF "GENERIC" DECODE ROUTINES.
- *   These may look a little ugly since they are imported from a "generic"
- * set of XDR encode/decode routines which are intended to be shared by
- * all of our NFSv4 implementations (OpenBSD, MacOS X...).
- *
- * If the pain of reading these is too great, it should be a straightforward
- * task to translate them into Linux-specific versions which are more
- * consistent with the style used in NFSv2/v3...
- */
 #define DECODE_HEAD                            \
        __be32 *p;                              \
        __be32 status
@@ -254,20 +244,8 @@ nfsd4_decode_bitmap(struct nfsd4_compoundargs *argp, u32 *bmval)
        DECODE_TAIL;
 }
 
-static u32 nfsd_attrmask[] = {
-       NFSD_WRITEABLE_ATTRS_WORD0,
-       NFSD_WRITEABLE_ATTRS_WORD1,
-       NFSD_WRITEABLE_ATTRS_WORD2
-};
-
-static u32 nfsd41_ex_attrmask[] = {
-       NFSD_SUPPATTR_EXCLCREAT_WORD0,
-       NFSD_SUPPATTR_EXCLCREAT_WORD1,
-       NFSD_SUPPATTR_EXCLCREAT_WORD2
-};
-
 static __be32
-nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, u32 *writable,
+nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval,
                   struct iattr *iattr, struct nfs4_acl **acl)
 {
        int expected_len, len = 0;
@@ -280,18 +258,6 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, u32 *writable,
        if ((status = nfsd4_decode_bitmap(argp, bmval)))
                return status;
 
-       /*
-        * According to spec, unsupported attributes return ERR_ATTRNOTSUPP;
-        * read-only attributes return ERR_INVAL.
-        */
-       if ((bmval[0] & ~nfsd_suppattrs0(argp->minorversion)) ||
-           (bmval[1] & ~nfsd_suppattrs1(argp->minorversion)) ||
-           (bmval[2] & ~nfsd_suppattrs2(argp->minorversion)))
-               return nfserr_attrnotsupp;
-       if ((bmval[0] & ~writable[0]) || (bmval[1] & ~writable[1]) ||
-           (bmval[2] & ~writable[2]))
-               return nfserr_inval;
-
        READ_BUF(4);
        READ32(expected_len);
 
@@ -424,8 +390,11 @@ nfsd4_decode_fattr(struct nfsd4_compoundargs *argp, u32 *bmval, u32 *writable,
                        goto xdr_error;
                }
        }
-       BUG_ON(bmval[2]);       /* no such writeable attr supported yet */
-       if (len != expected_len)
+       if (bmval[0] & ~NFSD_WRITEABLE_ATTRS_WORD0
+           || bmval[1] & ~NFSD_WRITEABLE_ATTRS_WORD1
+           || bmval[2] & ~NFSD_WRITEABLE_ATTRS_WORD2)
+               READ_BUF(expected_len - len);
+       else if (len != expected_len)
                goto xdr_error;
 
        DECODE_TAIL;
@@ -518,8 +487,8 @@ nfsd4_decode_create(struct nfsd4_compoundargs *argp, struct nfsd4_create *create
        if ((status = check_filename(create->cr_name, create->cr_namelen, nfserr_inval)))
                return status;
 
-       status = nfsd4_decode_fattr(argp, create->cr_bmval, nfsd_attrmask,
-                                   &create->cr_iattr, &create->cr_acl);
+       status = nfsd4_decode_fattr(argp, create->cr_bmval, &create->cr_iattr,
+                                   &create->cr_acl);
        if (status)
                goto out;
 
@@ -682,7 +651,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
                case NFS4_CREATE_UNCHECKED:
                case NFS4_CREATE_GUARDED:
                        status = nfsd4_decode_fattr(argp, open->op_bmval,
-                               nfsd_attrmask, &open->op_iattr, &open->op_acl);
+                               &open->op_iattr, &open->op_acl);
                        if (status)
                                goto out;
                        break;
@@ -696,8 +665,7 @@ nfsd4_decode_open(struct nfsd4_compoundargs *argp, struct nfsd4_open *open)
                        READ_BUF(8);
                        COPYMEM(open->op_verf.data, 8);
                        status = nfsd4_decode_fattr(argp, open->op_bmval,
-                               nfsd41_ex_attrmask, &open->op_iattr,
-                               &open->op_acl);
+                               &open->op_iattr, &open->op_acl);
                        if (status)
                                goto out;
                        break;
@@ -893,8 +861,8 @@ nfsd4_decode_setattr(struct nfsd4_compoundargs *argp, struct nfsd4_setattr *seta
        status = nfsd4_decode_stateid(argp, &setattr->sa_stateid);
        if (status)
                return status;
-       return nfsd4_decode_fattr(argp, setattr->sa_bmval, nfsd_attrmask,
-                                 &setattr->sa_iattr, &setattr->sa_acl);
+       return nfsd4_decode_fattr(argp, setattr->sa_bmval, &setattr->sa_iattr,
+                                 &setattr->sa_acl);
 }
 
 static __be32
@@ -1328,64 +1296,64 @@ static nfsd4_dec nfsd4_dec_ops[] = {
 };
 
 static nfsd4_dec nfsd41_dec_ops[] = {
-       [OP_ACCESS]             (nfsd4_dec)nfsd4_decode_access,
-       [OP_CLOSE]              (nfsd4_dec)nfsd4_decode_close,
-       [OP_COMMIT]             (nfsd4_dec)nfsd4_decode_commit,
-       [OP_CREATE]             (nfsd4_dec)nfsd4_decode_create,
-       [OP_DELEGPURGE]         (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_DELEGRETURN]        (nfsd4_dec)nfsd4_decode_delegreturn,
-       [OP_GETATTR]            (nfsd4_dec)nfsd4_decode_getattr,
-       [OP_GETFH]              (nfsd4_dec)nfsd4_decode_noop,
-       [OP_LINK]               (nfsd4_dec)nfsd4_decode_link,
-       [OP_LOCK]               (nfsd4_dec)nfsd4_decode_lock,
-       [OP_LOCKT]              (nfsd4_dec)nfsd4_decode_lockt,
-       [OP_LOCKU]              (nfsd4_dec)nfsd4_decode_locku,
-       [OP_LOOKUP]             (nfsd4_dec)nfsd4_decode_lookup,
-       [OP_LOOKUPP]            (nfsd4_dec)nfsd4_decode_noop,
-       [OP_NVERIFY]            (nfsd4_dec)nfsd4_decode_verify,
-       [OP_OPEN]               (nfsd4_dec)nfsd4_decode_open,
-       [OP_OPENATTR]           (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_OPEN_CONFIRM]       (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_OPEN_DOWNGRADE]     (nfsd4_dec)nfsd4_decode_open_downgrade,
-       [OP_PUTFH]              (nfsd4_dec)nfsd4_decode_putfh,
-       [OP_PUTPUBFH]           (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_PUTROOTFH]          (nfsd4_dec)nfsd4_decode_noop,
-       [OP_READ]               (nfsd4_dec)nfsd4_decode_read,
-       [OP_READDIR]            (nfsd4_dec)nfsd4_decode_readdir,
-       [OP_READLINK]           (nfsd4_dec)nfsd4_decode_noop,
-       [OP_REMOVE]             (nfsd4_dec)nfsd4_decode_remove,
-       [OP_RENAME]             (nfsd4_dec)nfsd4_decode_rename,
-       [OP_RENEW]              (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_RESTOREFH]          (nfsd4_dec)nfsd4_decode_noop,
-       [OP_SAVEFH]             (nfsd4_dec)nfsd4_decode_noop,
-       [OP_SECINFO]            (nfsd4_dec)nfsd4_decode_secinfo,
-       [OP_SETATTR]            (nfsd4_dec)nfsd4_decode_setattr,
-       [OP_SETCLIENTID]        (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_SETCLIENTID_CONFIRM](nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_VERIFY]             (nfsd4_dec)nfsd4_decode_verify,
-       [OP_WRITE]              (nfsd4_dec)nfsd4_decode_write,
-       [OP_RELEASE_LOCKOWNER]  (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_ACCESS]             (nfsd4_dec)nfsd4_decode_access,
+       [OP_CLOSE]              (nfsd4_dec)nfsd4_decode_close,
+       [OP_COMMIT]             (nfsd4_dec)nfsd4_decode_commit,
+       [OP_CREATE]             (nfsd4_dec)nfsd4_decode_create,
+       [OP_DELEGPURGE]         (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_DELEGRETURN]        (nfsd4_dec)nfsd4_decode_delegreturn,
+       [OP_GETATTR]            (nfsd4_dec)nfsd4_decode_getattr,
+       [OP_GETFH]              (nfsd4_dec)nfsd4_decode_noop,
+       [OP_LINK]               (nfsd4_dec)nfsd4_decode_link,
+       [OP_LOCK]               (nfsd4_dec)nfsd4_decode_lock,
+       [OP_LOCKT]              (nfsd4_dec)nfsd4_decode_lockt,
+       [OP_LOCKU]              (nfsd4_dec)nfsd4_decode_locku,
+       [OP_LOOKUP]             (nfsd4_dec)nfsd4_decode_lookup,
+       [OP_LOOKUPP]            (nfsd4_dec)nfsd4_decode_noop,
+       [OP_NVERIFY]            (nfsd4_dec)nfsd4_decode_verify,
+       [OP_OPEN]               (nfsd4_dec)nfsd4_decode_open,
+       [OP_OPENATTR]           (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_OPEN_CONFIRM]       (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_OPEN_DOWNGRADE]     (nfsd4_dec)nfsd4_decode_open_downgrade,
+       [OP_PUTFH]              (nfsd4_dec)nfsd4_decode_putfh,
+       [OP_PUTPUBFH]           (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_PUTROOTFH]          (nfsd4_dec)nfsd4_decode_noop,
+       [OP_READ]               (nfsd4_dec)nfsd4_decode_read,
+       [OP_READDIR]            (nfsd4_dec)nfsd4_decode_readdir,
+       [OP_READLINK]           (nfsd4_dec)nfsd4_decode_noop,
+       [OP_REMOVE]             (nfsd4_dec)nfsd4_decode_remove,
+       [OP_RENAME]             (nfsd4_dec)nfsd4_decode_rename,
+       [OP_RENEW]              (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_RESTOREFH]          (nfsd4_dec)nfsd4_decode_noop,
+       [OP_SAVEFH]             (nfsd4_dec)nfsd4_decode_noop,
+       [OP_SECINFO]            (nfsd4_dec)nfsd4_decode_secinfo,
+       [OP_SETATTR]            (nfsd4_dec)nfsd4_decode_setattr,
+       [OP_SETCLIENTID]        (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_SETCLIENTID_CONFIRM](nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_VERIFY]             (nfsd4_dec)nfsd4_decode_verify,
+       [OP_WRITE]              (nfsd4_dec)nfsd4_decode_write,
+       [OP_RELEASE_LOCKOWNER]  (nfsd4_dec)nfsd4_decode_notsupp,
 
        /* new operations for NFSv4.1 */
-       [OP_BACKCHANNEL_CTL]    (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_BIND_CONN_TO_SESSION](nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_EXCHANGE_ID]        (nfsd4_dec)nfsd4_decode_exchange_id,
-       [OP_CREATE_SESSION]     (nfsd4_dec)nfsd4_decode_create_session,
-       [OP_DESTROY_SESSION]    (nfsd4_dec)nfsd4_decode_destroy_session,
-       [OP_FREE_STATEID]       (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_GET_DIR_DELEGATION] (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_GETDEVICEINFO]      (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_GETDEVICELIST]      (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_LAYOUTCOMMIT]       (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_LAYOUTGET]          (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_LAYOUTRETURN]       (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_SECINFO_NO_NAME]    (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_SEQUENCE]           (nfsd4_dec)nfsd4_decode_sequence,
-       [OP_SET_SSV]            (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_TEST_STATEID]       (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_WANT_DELEGATION]    (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_DESTROY_CLIENTID]   (nfsd4_dec)nfsd4_decode_notsupp,
-       [OP_RECLAIM_COMPLETE]   (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_BACKCHANNEL_CTL]    (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_BIND_CONN_TO_SESSION](nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_EXCHANGE_ID]        (nfsd4_dec)nfsd4_decode_exchange_id,
+       [OP_CREATE_SESSION]     (nfsd4_dec)nfsd4_decode_create_session,
+       [OP_DESTROY_SESSION]    (nfsd4_dec)nfsd4_decode_destroy_session,
+       [OP_FREE_STATEID]       (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_GET_DIR_DELEGATION] (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_GETDEVICEINFO]      (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_GETDEVICELIST]      (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_LAYOUTCOMMIT]       (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_LAYOUTGET]          (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_LAYOUTRETURN]       (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_SECINFO_NO_NAME]    (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_SEQUENCE]           (nfsd4_dec)nfsd4_decode_sequence,
+       [OP_SET_SSV]            (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_TEST_STATEID]       (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_WANT_DELEGATION]    (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_DESTROY_CLIENTID]   (nfsd4_dec)nfsd4_decode_notsupp,
+       [OP_RECLAIM_COMPLETE]   (nfsd4_dec)nfsd4_decode_notsupp,
 };
 
 struct nfsd4_minorversion_ops {
@@ -1489,21 +1457,6 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
 
        DECODE_TAIL;
 }
-/*
- * END OF "GENERIC" DECODE ROUTINES.
- */
-
-/*
- * START OF "GENERIC" ENCODE ROUTINES.
- *   These may look a little ugly since they are imported from a "generic"
- * set of XDR encode/decode routines which are intended to be shared by
- * all of our NFSv4 implementations (OpenBSD, MacOS X...).
- *
- * If the pain of reading these is too great, it should be a straightforward
- * task to translate them into Linux-specific versions which are more
- * consistent with the style used in NFSv2/v3...
- */
-#define ENCODE_HEAD              __be32 *p
 
 #define WRITE32(n)               *p++ = htonl(n)
 #define WRITE64(n)               do {                          \
@@ -1515,13 +1468,41 @@ nfsd4_decode_compound(struct nfsd4_compoundargs *argp)
        memcpy(p, ptr, nbytes);                                 \
        p += XDR_QUADLEN(nbytes);                               \
 }} while (0)
-#define WRITECINFO(c)          do {                            \
-       *p++ = htonl(c.atomic);                                 \
-       *p++ = htonl(c.before_ctime_sec);                               \
-       *p++ = htonl(c.before_ctime_nsec);                              \
-       *p++ = htonl(c.after_ctime_sec);                                \
-       *p++ = htonl(c.after_ctime_nsec);                               \
-} while (0)
+
+static void write32(__be32 **p, u32 n)
+{
+       *(*p)++ = n;
+}
+
+static void write64(__be32 **p, u64 n)
+{
+       write32(p, (u32)(n >> 32));
+       write32(p, (u32)n);
+}
+
+static void write_change(__be32 **p, struct kstat *stat, struct inode *inode)
+{
+       if (IS_I_VERSION(inode)) {
+               write64(p, inode->i_version);
+       } else {
+               write32(p, stat->ctime.tv_sec);
+               write32(p, stat->ctime.tv_nsec);
+       }
+}
+
+static void write_cinfo(__be32 **p, struct nfsd4_change_info *c)
+{
+       write32(p, c->atomic);
+       if (c->change_supported) {
+               write64(p, c->before_change);
+               write64(p, c->after_change);
+       } else {
+               write32(p, c->before_ctime_sec);
+               write32(p, c->before_ctime_nsec);
+               write32(p, c->after_ctime_sec);
+               write32(p, c->after_ctime_nsec);
+       }
+}
 
 #define RESERVE_SPACE(nbytes)  do {                            \
        p = resp->p;                                            \
@@ -1874,16 +1855,9 @@ nfsd4_encode_fattr(struct svc_fh *fhp, struct svc_export *exp,
                        WRITE32(NFS4_FH_PERSISTENT|NFS4_FH_VOL_RENAME);
        }
        if (bmval0 & FATTR4_WORD0_CHANGE) {
-               /*
-                * Note: This _must_ be consistent with the scheme for writing
-                * change_info, so any changes made here must be reflected there
-                * as well.  (See xdr4.h:set_change_info() and the WRITECINFO()
-                * macro above.)
-                */
                if ((buflen -= 8) < 0)
                        goto out_resource;
-               WRITE32(stat.ctime.tv_sec);
-               WRITE32(stat.ctime.tv_nsec);
+               write_change(&p, &stat, dentry->d_inode);
        }
        if (bmval0 & FATTR4_WORD0_SIZE) {
                if ((buflen -= 8) < 0)
@@ -2348,7 +2322,7 @@ fail:
 static void
 nfsd4_encode_stateid(struct nfsd4_compoundres *resp, stateid_t *sid)
 {
-       ENCODE_HEAD;
+       __be32 *p;
 
        RESERVE_SPACE(sizeof(stateid_t));
        WRITE32(sid->si_generation);
@@ -2359,7 +2333,7 @@ nfsd4_encode_stateid(struct nfsd4_compoundres *resp, stateid_t *sid)
 static __be32
 nfsd4_encode_access(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_access *access)
 {
-       ENCODE_HEAD;
+       __be32 *p;
 
        if (!nfserr) {
                RESERVE_SPACE(8);
@@ -2386,7 +2360,7 @@ nfsd4_encode_close(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_c
 static __be32
 nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_commit *commit)
 {
-       ENCODE_HEAD;
+       __be32 *p;
 
        if (!nfserr) {
                RESERVE_SPACE(8);
@@ -2399,11 +2373,11 @@ nfsd4_encode_commit(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_
 static __be32
 nfsd4_encode_create(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_create *create)
 {
-       ENCODE_HEAD;
+       __be32 *p;
 
        if (!nfserr) {
                RESERVE_SPACE(32);
-               WRITECINFO(create->cr_cinfo);
+               write_cinfo(&p, &create->cr_cinfo);
                WRITE32(2);
                WRITE32(create->cr_bmval[0]);
                WRITE32(create->cr_bmval[1]);
@@ -2435,7 +2409,7 @@ nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh
 {
        struct svc_fh *fhp = *fhpp;
        unsigned int len;
-       ENCODE_HEAD;
+       __be32 *p;
 
        if (!nfserr) {
                len = fhp->fh_handle.fh_size;
@@ -2454,7 +2428,7 @@ nfsd4_encode_getfh(struct nfsd4_compoundres *resp, __be32 nfserr, struct svc_fh
 static void
 nfsd4_encode_lock_denied(struct nfsd4_compoundres *resp, struct nfsd4_lock_denied *ld)
 {
-       ENCODE_HEAD;
+       __be32 *p;
 
        RESERVE_SPACE(32 + XDR_LEN(ld->ld_sop ? ld->ld_sop->so_owner.len : 0));
        WRITE64(ld->ld_start);
@@ -2510,11 +2484,11 @@ nfsd4_encode_locku(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_l
 static __be32
 nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_link *link)
 {
-       ENCODE_HEAD;
+       __be32 *p;
 
        if (!nfserr) {
                RESERVE_SPACE(20);
-               WRITECINFO(link->li_cinfo);
+               write_cinfo(&p, &link->li_cinfo);
                ADJUST_ARGS();
        }
        return nfserr;
@@ -2524,7 +2498,7 @@ nfsd4_encode_link(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_li
 static __be32
 nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_open *open)
 {
-       ENCODE_HEAD;
+       __be32 *p;
        ENCODE_SEQID_OP_HEAD;
 
        if (nfserr)
@@ -2532,7 +2506,7 @@ nfsd4_encode_open(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_op
 
        nfsd4_encode_stateid(resp, &open->op_stateid);
        RESERVE_SPACE(40);
-       WRITECINFO(open->op_cinfo);
+       write_cinfo(&p, &open->op_cinfo);
        WRITE32(open->op_rflags);
        WRITE32(2);
        WRITE32(open->op_bmval[0]);
@@ -2619,7 +2593,7 @@ nfsd4_encode_read(struct nfsd4_compoundres *resp, __be32 nfserr,
        int v, pn;
        unsigned long maxcount; 
        long len;
-       ENCODE_HEAD;
+       __be32 *p;
 
        if (nfserr)
                return nfserr;
@@ -2681,7 +2655,7 @@ nfsd4_encode_readlink(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd
 {
        int maxcount;
        char *page;
-       ENCODE_HEAD;
+       __be32 *p;
 
        if (nfserr)
                return nfserr;
@@ -2730,7 +2704,7 @@ nfsd4_encode_readdir(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
        int maxcount;
        loff_t offset;
        __be32 *page, *savep, *tailbase;
-       ENCODE_HEAD;
+       __be32 *p;
 
        if (nfserr)
                return nfserr;
@@ -2806,11 +2780,11 @@ err_no_verf:
 static __be32
 nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_remove *remove)
 {
-       ENCODE_HEAD;
+       __be32 *p;
 
        if (!nfserr) {
                RESERVE_SPACE(20);
-               WRITECINFO(remove->rm_cinfo);
+               write_cinfo(&p, &remove->rm_cinfo);
                ADJUST_ARGS();
        }
        return nfserr;
@@ -2819,12 +2793,12 @@ nfsd4_encode_remove(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_
 static __be32
 nfsd4_encode_rename(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_rename *rename)
 {
-       ENCODE_HEAD;
+       __be32 *p;
 
        if (!nfserr) {
                RESERVE_SPACE(40);
-               WRITECINFO(rename->rn_sinfo);
-               WRITECINFO(rename->rn_tinfo);
+               write_cinfo(&p, &rename->rn_sinfo);
+               write_cinfo(&p, &rename->rn_tinfo);
                ADJUST_ARGS();
        }
        return nfserr;
@@ -2839,7 +2813,7 @@ nfsd4_encode_secinfo(struct nfsd4_compoundres *resp, __be32 nfserr,
        u32 nflavs;
        struct exp_flavor_info *flavs;
        struct exp_flavor_info def_flavs[2];
-       ENCODE_HEAD;
+       __be32 *p;
 
        if (nfserr)
                goto out;
@@ -2904,7 +2878,7 @@ out:
 static __be32
 nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setattr *setattr)
 {
-       ENCODE_HEAD;
+       __be32 *p;
 
        RESERVE_SPACE(12);
        if (nfserr) {
@@ -2924,7 +2898,7 @@ nfsd4_encode_setattr(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4
 static __be32
 nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_setclientid *scd)
 {
-       ENCODE_HEAD;
+       __be32 *p;
 
        if (!nfserr) {
                RESERVE_SPACE(8 + sizeof(nfs4_verifier));
@@ -2944,7 +2918,7 @@ nfsd4_encode_setclientid(struct nfsd4_compoundres *resp, __be32 nfserr, struct n
 static __be32
 nfsd4_encode_write(struct nfsd4_compoundres *resp, __be32 nfserr, struct nfsd4_write *write)
 {
-       ENCODE_HEAD;
+       __be32 *p;
 
        if (!nfserr) {
                RESERVE_SPACE(16);
@@ -2960,7 +2934,7 @@ static __be32
 nfsd4_encode_exchange_id(struct nfsd4_compoundres *resp, int nfserr,
                         struct nfsd4_exchange_id *exid)
 {
-       ENCODE_HEAD;
+       __be32 *p;
        char *major_id;
        char *server_scope;
        int major_id_sz;
@@ -3015,7 +2989,7 @@ static __be32
 nfsd4_encode_create_session(struct nfsd4_compoundres *resp, int nfserr,
                            struct nfsd4_create_session *sess)
 {
-       ENCODE_HEAD;
+       __be32 *p;
 
        if (nfserr)
                return nfserr;
@@ -3071,7 +3045,7 @@ __be32
 nfsd4_encode_sequence(struct nfsd4_compoundres *resp, int nfserr,
                      struct nfsd4_sequence *seq)
 {
-       ENCODE_HEAD;
+       __be32 *p;
 
        if (nfserr)
                return nfserr;
@@ -3219,7 +3193,7 @@ void
 nfsd4_encode_operation(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
 {
        __be32 *statp;
-       ENCODE_HEAD;
+       __be32 *p;
 
        RESERVE_SPACE(8);
        WRITE32(op->opnum);
@@ -3253,7 +3227,7 @@ status:
 void
 nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
 {
-       ENCODE_HEAD;
+       __be32 *p;
        struct nfs4_replay *rp = op->replay;
 
        BUG_ON(!rp);
@@ -3268,10 +3242,6 @@ nfsd4_encode_replay(struct nfsd4_compoundres *resp, struct nfsd4_op *op)
        ADJUST_ARGS();
 }
 
-/*
- * END OF "GENERIC" ENCODE ROUTINES.
- */
-
 int
 nfs4svc_encode_voidres(struct svc_rqst *rqstp, __be32 *p, void *dummy)
 {
index 5bfc2ac60d543a07a8d70d71057cf880d1bb995e..4638635c5d87d217ea813c9f83b0a07a57624089 100644 (file)
  */
 #define CACHESIZE              1024
 #define HASHSIZE               64
-#define REQHASH(xid)           (((((__force __u32)xid) >> 24) ^ ((__force __u32)xid)) & (HASHSIZE-1))
 
-static struct hlist_head *     hash_list;
+static struct hlist_head *     cache_hash;
 static struct list_head        lru_head;
 static int                     cache_disabled = 1;
 
+/*
+ * Calculate the hash index from an XID.
+ */
+static inline u32 request_hash(u32 xid)
+{
+       u32 h = xid;
+       h ^= (xid >> 24);
+       return h & (HASHSIZE-1);
+}
+
 static int     nfsd_cache_append(struct svc_rqst *rqstp, struct kvec *vec);
 
-/* 
+/*
  * locking for the reply cache:
  * A cache entry is "single use" if c_state == RC_INPROG
  * Otherwise, it when accessing _prev or _next, the lock must be held.
@@ -62,8 +71,8 @@ int nfsd_reply_cache_init(void)
                i--;
        }
 
-       hash_list = kcalloc (HASHSIZE, sizeof(struct hlist_head), GFP_KERNEL);
-       if (!hash_list)
+       cache_hash = kcalloc (HASHSIZE, sizeof(struct hlist_head), GFP_KERNEL);
+       if (!cache_hash)
                goto out_nomem;
 
        cache_disabled = 0;
@@ -88,8 +97,8 @@ void nfsd_reply_cache_shutdown(void)
 
        cache_disabled = 1;
 
-       kfree (hash_list);
-       hash_list = NULL;
+       kfree (cache_hash);
+       cache_hash = NULL;
 }
 
 /*
@@ -108,7 +117,7 @@ static void
 hash_refile(struct svc_cacherep *rp)
 {
        hlist_del_init(&rp->c_hash);
-       hlist_add_head(&rp->c_hash, hash_list + REQHASH(rp->c_xid));
+       hlist_add_head(&rp->c_hash, cache_hash + request_hash(rp->c_xid));
 }
 
 /*
@@ -138,7 +147,7 @@ nfsd_cache_lookup(struct svc_rqst *rqstp, int type)
        spin_lock(&cache_lock);
        rtn = RC_DOIT;
 
-       rh = &hash_list[REQHASH(xid)];
+       rh = &cache_hash[request_hash(xid)];
        hlist_for_each_entry(rp, hn, rh, c_hash) {
                if (rp->c_state != RC_UNUSED &&
                    xid == rp->c_xid && proc == rp->c_proc &&
@@ -165,8 +174,8 @@ nfsd_cache_lookup(struct svc_rqst *rqstp, int type)
        }
        }
 
-       /* This should not happen */
-       if (rp == NULL) {
+       /* All entries on the LRU are in-progress. This should not happen */
+       if (&rp->c_lru == &lru_head) {
                static int      complaints;
 
                printk(KERN_WARNING "nfsd: all repcache entries locked!\n");
@@ -264,7 +273,7 @@ nfsd_cache_update(struct svc_rqst *rqstp, int cachetype, __be32 *statp)
 
        len = resv->iov_len - ((char*)statp - (char*)resv->iov_base);
        len >>= 2;
-       
+
        /* Don't cache excessive amounts of data and XDR failures */
        if (!statp || len > (256 >> 2)) {
                rp->c_state = RC_UNUSED;
index af16849d243ac543c1192b04c49362b8c3eac958..877e713a0fd6886c4f0f43262c66414eacc59029 100644 (file)
@@ -695,8 +695,9 @@ static ssize_t write_threads(struct file *file, char *buf, size_t size)
                if (rv)
                        return rv;
        }
-       sprintf(buf, "%d\n", nfsd_nrthreads());
-       return strlen(buf);
+
+       return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n",
+                                                       nfsd_nrthreads());
 }
 
 /**
@@ -793,7 +794,7 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
 {
        char *mesg = buf;
        char *vers, *minorp, sign;
-       int len, num;
+       int len, num, remaining;
        unsigned minor;
        ssize_t tlen = 0;
        char *sep;
@@ -840,32 +841,50 @@ static ssize_t __write_versions(struct file *file, char *buf, size_t size)
                        }
                next:
                        vers += len + 1;
-                       tlen += len;
                } while ((len = qword_get(&mesg, vers, size)) > 0);
                /* If all get turned off, turn them back on, as
                 * having no versions is BAD
                 */
                nfsd_reset_versions();
        }
+
        /* Now write current state into reply buffer */
        len = 0;
        sep = "";
+       remaining = SIMPLE_TRANSACTION_LIMIT;
        for (num=2 ; num <= 4 ; num++)
                if (nfsd_vers(num, NFSD_AVAIL)) {
-                       len += sprintf(buf+len, "%s%c%d", sep,
+                       len = snprintf(buf, remaining, "%s%c%d", sep,
                                       nfsd_vers(num, NFSD_TEST)?'+':'-',
                                       num);
                        sep = " ";
+
+                       if (len > remaining)
+                               break;
+                       remaining -= len;
+                       buf += len;
+                       tlen += len;
                }
        if (nfsd_vers(4, NFSD_AVAIL))
-               for (minor = 1; minor <= NFSD_SUPPORTED_MINOR_VERSION; minor++)
-                       len += sprintf(buf+len, " %c4.%u",
+               for (minor = 1; minor <= NFSD_SUPPORTED_MINOR_VERSION;
+                    minor++) {
+                       len = snprintf(buf, remaining, " %c4.%u",
                                        (nfsd_vers(4, NFSD_TEST) &&
                                         nfsd_minorversion(minor, NFSD_TEST)) ?
                                                '+' : '-',
                                        minor);
-       len += sprintf(buf+len, "\n");
-       return len;
+
+                       if (len > remaining)
+                               break;
+                       remaining -= len;
+                       buf += len;
+                       tlen += len;
+               }
+
+       len = snprintf(buf, remaining, "\n");
+       if (len > remaining)
+               return -EINVAL;
+       return tlen + len;
 }
 
 /**
@@ -910,104 +929,143 @@ static ssize_t write_versions(struct file *file, char *buf, size_t size)
        return rv;
 }
 
-static ssize_t __write_ports(struct file *file, char *buf, size_t size)
+/*
+ * Zero-length write.  Return a list of NFSD's current listener
+ * transports.
+ */
+static ssize_t __write_ports_names(char *buf)
 {
-       if (size == 0) {
-               int len = 0;
+       if (nfsd_serv == NULL)
+               return 0;
+       return svc_xprt_names(nfsd_serv, buf, SIMPLE_TRANSACTION_LIMIT);
+}
 
-               if (nfsd_serv)
-                       len = svc_xprt_names(nfsd_serv, buf, 0);
-               return len;
-       }
-       /* Either a single 'fd' number is written, in which
-        * case it must be for a socket of a supported family/protocol,
-        * and we use it as an nfsd socket, or
-        * A '-' followed by the 'name' of a socket in which case
-        * we close the socket.
-        */
-       if (isdigit(buf[0])) {
-               char *mesg = buf;
-               int fd;
-               int err;
-               err = get_int(&mesg, &fd);
-               if (err)
-                       return -EINVAL;
-               if (fd < 0)
-                       return -EINVAL;
-               err = nfsd_create_serv();
-               if (!err) {
-                       err = svc_addsock(nfsd_serv, fd, buf);
-                       if (err >= 0) {
-                               err = lockd_up();
-                               if (err < 0)
-                                       svc_sock_names(buf+strlen(buf)+1, nfsd_serv, buf);
-                       }
-                       /* Decrease the count, but don't shutdown the
-                        * the service
-                        */
-                       nfsd_serv->sv_nrthreads--;
-               }
-               return err < 0 ? err : 0;
-       }
-       if (buf[0] == '-' && isdigit(buf[1])) {
-               char *toclose = kstrdup(buf+1, GFP_KERNEL);
-               int len = 0;
-               if (!toclose)
-                       return -ENOMEM;
-               if (nfsd_serv)
-                       len = svc_sock_names(buf, nfsd_serv, toclose);
-               if (len >= 0)
-                       lockd_down();
-               kfree(toclose);
-               return len;
-       }
-       /*
-        * Add a transport listener by writing it's transport name
-        */
-       if (isalpha(buf[0])) {
-               int err;
-               char transport[16];
-               int port;
-               if (sscanf(buf, "%15s %4d", transport, &port) == 2) {
-                       if (port < 1 || port > 65535)
-                               return -EINVAL;
-                       err = nfsd_create_serv();
-                       if (!err) {
-                               err = svc_create_xprt(nfsd_serv,
-                                                     transport, PF_INET, port,
-                                                     SVC_SOCK_ANONYMOUS);
-                               if (err == -ENOENT)
-                                       /* Give a reasonable perror msg for
-                                        * bad transport string */
-                                       err = -EPROTONOSUPPORT;
-                       }
-                       return err < 0 ? err : 0;
-               }
-       }
-       /*
-        * Remove a transport by writing it's transport name and port number
-        */
-       if (buf[0] == '-' && isalpha(buf[1])) {
-               struct svc_xprt *xprt;
-               int err = -EINVAL;
-               char transport[16];
-               int port;
-               if (sscanf(&buf[1], "%15s %4d", transport, &port) == 2) {
-                       if (port < 1 || port > 65535)
-                               return -EINVAL;
-                       if (nfsd_serv) {
-                               xprt = svc_find_xprt(nfsd_serv, transport,
-                                                    AF_UNSPEC, port);
-                               if (xprt) {
-                                       svc_close_xprt(xprt);
-                                       svc_xprt_put(xprt);
-                                       err = 0;
-                               } else
-                                       err = -ENOTCONN;
-                       }
-                       return err < 0 ? err : 0;
-               }
+/*
+ * A single 'fd' number was written, in which case it must be for
+ * a socket of a supported family/protocol, and we use it as an
+ * nfsd listener.
+ */
+static ssize_t __write_ports_addfd(char *buf)
+{
+       char *mesg = buf;
+       int fd, err;
+
+       err = get_int(&mesg, &fd);
+       if (err != 0 || fd < 0)
+               return -EINVAL;
+
+       err = nfsd_create_serv();
+       if (err != 0)
+               return err;
+
+       err = lockd_up();
+       if (err != 0)
+               goto out;
+
+       err = svc_addsock(nfsd_serv, fd, buf, SIMPLE_TRANSACTION_LIMIT);
+       if (err < 0)
+               lockd_down();
+
+out:
+       /* Decrease the count, but don't shut down the service */
+       nfsd_serv->sv_nrthreads--;
+       return err;
+}
+
+/*
+ * A '-' followed by the 'name' of a socket means we close the socket.
+ */
+static ssize_t __write_ports_delfd(char *buf)
+{
+       char *toclose;
+       int len = 0;
+
+       toclose = kstrdup(buf + 1, GFP_KERNEL);
+       if (toclose == NULL)
+               return -ENOMEM;
+
+       if (nfsd_serv != NULL)
+               len = svc_sock_names(nfsd_serv, buf,
+                                       SIMPLE_TRANSACTION_LIMIT, toclose);
+       if (len >= 0)
+               lockd_down();
+
+       kfree(toclose);
+       return len;
+}
+
+/*
+ * A transport listener is added by writing it's transport name and
+ * a port number.
+ */
+static ssize_t __write_ports_addxprt(char *buf)
+{
+       char transport[16];
+       int port, err;
+
+       if (sscanf(buf, "%15s %4u", transport, &port) != 2)
+               return -EINVAL;
+
+       if (port < 1 || port > USHORT_MAX)
+               return -EINVAL;
+
+       err = nfsd_create_serv();
+       if (err != 0)
+               return err;
+
+       err = svc_create_xprt(nfsd_serv, transport,
+                               PF_INET, port, SVC_SOCK_ANONYMOUS);
+       if (err < 0) {
+               /* Give a reasonable perror msg for bad transport string */
+               if (err == -ENOENT)
+                       err = -EPROTONOSUPPORT;
+               return err;
        }
+       return 0;
+}
+
+/*
+ * A transport listener is removed by writing a "-", it's transport
+ * name, and it's port number.
+ */
+static ssize_t __write_ports_delxprt(char *buf)
+{
+       struct svc_xprt *xprt;
+       char transport[16];
+       int port;
+
+       if (sscanf(&buf[1], "%15s %4u", transport, &port) != 2)
+               return -EINVAL;
+
+       if (port < 1 || port > USHORT_MAX || nfsd_serv == NULL)
+               return -EINVAL;
+
+       xprt = svc_find_xprt(nfsd_serv, transport, AF_UNSPEC, port);
+       if (xprt == NULL)
+               return -ENOTCONN;
+
+       svc_close_xprt(xprt);
+       svc_xprt_put(xprt);
+       return 0;
+}
+
+static ssize_t __write_ports(struct file *file, char *buf, size_t size)
+{
+       if (size == 0)
+               return __write_ports_names(buf);
+
+       if (isdigit(buf[0]))
+               return __write_ports_addfd(buf);
+
+       if (buf[0] == '-' && isdigit(buf[1]))
+               return __write_ports_delfd(buf);
+
+       if (isalpha(buf[0]))
+               return __write_ports_addxprt(buf);
+
+       if (buf[0] == '-' && isalpha(buf[1]))
+               return __write_ports_delxprt(buf);
+
        return -EINVAL;
 }
 
@@ -1030,7 +1088,9 @@ static ssize_t __write_ports(struct file *file, char *buf, size_t size)
  *                     buf:            C string containing an unsigned
  *                                     integer value representing a bound
  *                                     but unconnected socket that is to be
- *                                     used as an NFSD listener
+ *                                     used as an NFSD listener; listen(3)
+ *                                     must be called for a SOCK_STREAM
+ *                                     socket, otherwise it is ignored
  *                     size:           non-zero length of C string in @buf
  * Output:
  *     On success:     NFS service is started;
@@ -1138,7 +1198,9 @@ static ssize_t write_maxblksize(struct file *file, char *buf, size_t size)
                nfsd_max_blksize = bsize;
                mutex_unlock(&nfsd_mutex);
        }
-       return sprintf(buf, "%d\n", nfsd_max_blksize);
+
+       return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%d\n",
+                                                       nfsd_max_blksize);
 }
 
 #ifdef CONFIG_NFSD_V4
@@ -1162,8 +1224,9 @@ static ssize_t __write_leasetime(struct file *file, char *buf, size_t size)
                        return -EINVAL;
                nfs4_reset_lease(lease);
        }
-       sprintf(buf, "%ld\n", nfs4_lease_time());
-       return strlen(buf);
+
+       return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%ld\n",
+                                                       nfs4_lease_time());
 }
 
 /**
@@ -1219,8 +1282,9 @@ static ssize_t __write_recoverydir(struct file *file, char *buf, size_t size)
 
                status = nfs4_reset_recoverydir(recdir);
        }
-       sprintf(buf, "%s\n", nfs4_recoverydir());
-       return strlen(buf);
+
+       return scnprintf(buf, SIMPLE_TRANSACTION_LIMIT, "%s\n",
+                                                       nfs4_recoverydir());
 }
 
 /**
index 9f1ca17293d3e0a54a42234643f250aa22c6e062..8847f3fbfc1e31b4260c92bc65e074c1469b41eb 100644 (file)
@@ -27,9 +27,6 @@
 #define NFSDDBG_FACILITY               NFSDDBG_FH
 
 
-static int nfsd_nr_verified;
-static int nfsd_nr_put;
-
 /*
  * our acceptability function.
  * if NOSUBTREECHECK, accept anything
@@ -251,7 +248,6 @@ static __be32 nfsd_set_fh_dentry(struct svc_rqst *rqstp, struct svc_fh *fhp)
 
        fhp->fh_dentry = dentry;
        fhp->fh_export = exp;
-       nfsd_nr_verified++;
        return 0;
 out:
        exp_put(exp);
@@ -552,7 +548,6 @@ fh_compose(struct svc_fh *fhp, struct svc_export *exp, struct dentry *dentry,
                        return nfserr_opnotsupp;
        }
 
-       nfsd_nr_verified++;
        return 0;
 }
 
@@ -609,7 +604,6 @@ fh_put(struct svc_fh *fhp)
                fhp->fh_pre_saved = 0;
                fhp->fh_post_saved = 0;
 #endif
-               nfsd_nr_put++;
        }
        if (exp) {
                cache_put(&exp->h, &svc_export_cache);
index 3b534e527e09922aa0d89b1854ba60a4e5871f5a..58e843b26b98a4a25dd40be34682a47055b27f9c 100644 (file)
@@ -1108,6 +1108,7 @@ extern void locks_copy_lock(struct file_lock *, struct file_lock *);
 extern void __locks_copy_lock(struct file_lock *, const struct file_lock *);
 extern void locks_remove_posix(struct file *, fl_owner_t);
 extern void locks_remove_flock(struct file *);
+extern void locks_release_private(struct file_lock *);
 extern void posix_test_lock(struct file *, struct file_lock *);
 extern int posix_lock_file(struct file *, struct file_lock *, struct file_lock *);
 extern int posix_lock_file_wait(struct file *, struct file_lock *);
index 5bccaab81056bf1bf17075ee7267c884a38b882a..3a3f58934f5ea2d59fa21a468b60a59bb2aa2f2f 100644 (file)
@@ -14,8 +14,7 @@
 #include <linux/uio.h>
 
 /*
- * Representation of a reply cache entry. The first two members *must*
- * be hash_next and hash_prev.
+ * Representation of a reply cache entry.
  */
 struct svc_cacherep {
        struct hlist_node       c_hash;
index afa19016c4a8fdc90fd1fad4cdeff3179cda7613..8f641c908450414b6c65bff05af4ef1a6636418e 100644 (file)
@@ -151,9 +151,15 @@ typedef struct svc_fh {
        __u64                   fh_pre_size;    /* size before operation */
        struct timespec         fh_pre_mtime;   /* mtime before oper */
        struct timespec         fh_pre_ctime;   /* ctime before oper */
+       /*
+        * pre-op nfsv4 change attr: note must check IS_I_VERSION(inode)
+        *  to find out if it is valid.
+        */
+       u64                     fh_pre_change;
 
        /* Post-op attributes saved in fh_unlock */
        struct kstat            fh_post_attr;   /* full attrs after operation */
+       u64                     fh_post_change; /* nfsv4 change; see above */
 #endif /* CONFIG_NFSD_V3 */
 
 } svc_fh;
@@ -298,6 +304,7 @@ fill_pre_wcc(struct svc_fh *fhp)
                fhp->fh_pre_mtime = inode->i_mtime;
                fhp->fh_pre_ctime = inode->i_ctime;
                fhp->fh_pre_size  = inode->i_size;
+               fhp->fh_pre_change = inode->i_version;
                fhp->fh_pre_saved = 1;
        }
 }
index 4d61c873feed3d107d5dfcc3a64564fae252706e..c0c49215ddc52150738a52ce797e740d46326df3 100644 (file)
@@ -61,15 +61,6 @@ typedef struct {
 #define si_stateownerid   si_opaque.so_stateownerid
 #define si_fileid         si_opaque.so_fileid
 
-
-struct nfs4_cb_recall {
-       u32                     cbr_ident;
-       int                     cbr_trunc;
-       stateid_t               cbr_stateid;
-       struct knfsd_fh         cbr_fh;
-       struct nfs4_delegation  *cbr_dp;
-};
-
 struct nfs4_delegation {
        struct list_head        dl_perfile;
        struct list_head        dl_perclnt;
@@ -81,14 +72,15 @@ struct nfs4_delegation {
        struct file             *dl_vfs_file;
        u32                     dl_type;
        time_t                  dl_time;
-       struct nfs4_cb_recall   dl_recall;
+/* For recall: */
+       u32                     dl_ident;
+       stateid_t               dl_stateid;
+       struct knfsd_fh         dl_fh;
+       int                     dl_retries;
 };
 
-#define dl_stateid      dl_recall.cbr_stateid
-#define dl_fh           dl_recall.cbr_fh
-
 /* client delegation callback info */
-struct nfs4_callback {
+struct nfs4_cb_conn {
        /* SETCLIENTID info */
        u32                     cb_addr;
        unsigned short          cb_port;
@@ -97,6 +89,7 @@ struct nfs4_callback {
        /* RPC client info */
        atomic_t                cb_set;     /* successful CB_NULL call */
        struct rpc_clnt *       cb_client;
+       struct rpc_cred *       cb_cred;
 };
 
 /* Maximum number of slots per session. 128 is useful for long haul TCP */
@@ -185,7 +178,7 @@ struct nfs4_client {
        struct svc_cred         cl_cred;        /* setclientid principal */
        clientid_t              cl_clientid;    /* generated by server */
        nfs4_verifier           cl_confirm;     /* generated by server */
-       struct nfs4_callback    cl_callback;    /* callback info */
+       struct nfs4_cb_conn     cl_cb_conn;     /* callback info */
        atomic_t                cl_count;       /* ref count */
        u32                     cl_firststate;  /* recovery dir creation */
 
index f80d6013fdc34ef3d19fc5910d3a49768f3c625c..d0f050f01eca62cd75824ac740b792e03c64a79c 100644 (file)
@@ -64,10 +64,13 @@ static inline bool nfsd4_has_session(struct nfsd4_compound_state *cs)
 
 struct nfsd4_change_info {
        u32             atomic;
+       bool            change_supported;
        u32             before_ctime_sec;
        u32             before_ctime_nsec;
+       u64             before_change;
        u32             after_ctime_sec;
        u32             after_ctime_nsec;
+       u64             after_change;
 };
 
 struct nfsd4_access {
@@ -503,10 +506,16 @@ set_change_info(struct nfsd4_change_info *cinfo, struct svc_fh *fhp)
 {
        BUG_ON(!fhp->fh_pre_saved || !fhp->fh_post_saved);
        cinfo->atomic = 1;
-       cinfo->before_ctime_sec = fhp->fh_pre_ctime.tv_sec;
-       cinfo->before_ctime_nsec = fhp->fh_pre_ctime.tv_nsec;
-       cinfo->after_ctime_sec = fhp->fh_post_attr.ctime.tv_sec;
-       cinfo->after_ctime_nsec = fhp->fh_post_attr.ctime.tv_nsec;
+       cinfo->change_supported = IS_I_VERSION(fhp->fh_dentry->d_inode);
+       if (cinfo->change_supported) {
+               cinfo->before_change = fhp->fh_pre_change;
+               cinfo->after_change = fhp->fh_post_change;
+       } else {
+               cinfo->before_ctime_sec = fhp->fh_pre_ctime.tv_sec;
+               cinfo->before_ctime_nsec = fhp->fh_pre_ctime.tv_nsec;
+               cinfo->after_ctime_sec = fhp->fh_post_attr.ctime.tv_sec;
+               cinfo->after_ctime_nsec = fhp->fh_post_attr.ctime.tv_nsec;
+       }
 }
 
 int nfs4svc_encode_voidres(struct svc_rqst *, __be32 *, void *);
index 0d9cb6ef28b00cf58c7b25824a0325e8909eb98a..2223ae0b5ed5777eea65e0d41d77b8719fdb7893 100644 (file)
@@ -83,7 +83,7 @@ int   svc_port_is_privileged(struct sockaddr *sin);
 int    svc_print_xprts(char *buf, int maxlen);
 struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name,
                        const sa_family_t af, const unsigned short port);
-int    svc_xprt_names(struct svc_serv *serv, char *buf, int buflen);
+int    svc_xprt_names(struct svc_serv *serv, char *buf, const int buflen);
 
 static inline void svc_xprt_get(struct svc_xprt *xprt)
 {
@@ -118,7 +118,7 @@ static inline unsigned short svc_addr_port(const struct sockaddr *sa)
        return 0;
 }
 
-static inline size_t svc_addr_len(struct sockaddr *sa)
+static inline size_t svc_addr_len(const struct sockaddr *sa)
 {
        switch (sa->sa_family) {
        case AF_INET:
@@ -126,7 +126,8 @@ static inline size_t svc_addr_len(struct sockaddr *sa)
        case AF_INET6:
                return sizeof(struct sockaddr_in6);
        }
-       return -EAFNOSUPPORT;
+
+       return 0;
 }
 
 static inline unsigned short svc_xprt_local_port(const struct svc_xprt *xprt)
index 483e10380aae45429b8ad7aba5a930aa8e4f3695..8271631389491c690be8f01596bf0ce484833dfd 100644 (file)
@@ -38,8 +38,11 @@ int          svc_recv(struct svc_rqst *, long);
 int            svc_send(struct svc_rqst *);
 void           svc_drop(struct svc_rqst *);
 void           svc_sock_update_bufs(struct svc_serv *serv);
-int            svc_sock_names(char *buf, struct svc_serv *serv, char *toclose);
-int            svc_addsock(struct svc_serv *serv, int fd, char *name_return);
+int            svc_sock_names(struct svc_serv *serv, char *buf,
+                                       const size_t buflen,
+                                       const char *toclose);
+int            svc_addsock(struct svc_serv *serv, const int fd,
+                                       char *name_return, const size_t len);
 void           svc_init_xprt_sock(void);
 void           svc_cleanup_xprt_sock(void);
 
index c200d92e57e4990c98d5f95ebff026104d5fdedc..6f33d33cc064e0f39f3232c910aff09fd53b3a67 100644 (file)
@@ -11,6 +11,7 @@
 #include <net/sock.h>
 #include <linux/sunrpc/stats.h>
 #include <linux/sunrpc/svc_xprt.h>
+#include <linux/sunrpc/svcsock.h>
 
 #define RPCDBG_FACILITY        RPCDBG_SVCXPRT
 
@@ -1097,36 +1098,58 @@ struct svc_xprt *svc_find_xprt(struct svc_serv *serv, const char *xcl_name,
 }
 EXPORT_SYMBOL_GPL(svc_find_xprt);
 
-/*
- * Format a buffer with a list of the active transports. A zero for
- * the buflen parameter disables target buffer overflow checking.
+static int svc_one_xprt_name(const struct svc_xprt *xprt,
+                            char *pos, int remaining)
+{
+       int len;
+
+       len = snprintf(pos, remaining, "%s %u\n",
+                       xprt->xpt_class->xcl_name,
+                       svc_xprt_local_port(xprt));
+       if (len >= remaining)
+               return -ENAMETOOLONG;
+       return len;
+}
+
+/**
+ * svc_xprt_names - format a buffer with a list of transport names
+ * @serv: pointer to an RPC service
+ * @buf: pointer to a buffer to be filled in
+ * @buflen: length of buffer to be filled in
+ *
+ * Fills in @buf with a string containing a list of transport names,
+ * each name terminated with '\n'.
+ *
+ * Returns positive length of the filled-in string on success; otherwise
+ * a negative errno value is returned if an error occurs.
  */
-int svc_xprt_names(struct svc_serv *serv, char *buf, int buflen)
+int svc_xprt_names(struct svc_serv *serv, char *buf, const int buflen)
 {
        struct svc_xprt *xprt;
-       char xprt_str[64];
-       int totlen = 0;
-       int len;
+       int len, totlen;
+       char *pos;
 
        /* Sanity check args */
        if (!serv)
                return 0;
 
        spin_lock_bh(&serv->sv_lock);
+
+       pos = buf;
+       totlen = 0;
        list_for_each_entry(xprt, &serv->sv_permsocks, xpt_list) {
-               len = snprintf(xprt_str, sizeof(xprt_str),
-                              "%s %d\n", xprt->xpt_class->xcl_name,
-                              svc_xprt_local_port(xprt));
-               /* If the string was truncated, replace with error string */
-               if (len >= sizeof(xprt_str))
-                       strcpy(xprt_str, "name-too-long\n");
-               /* Don't overflow buffer */
-               len = strlen(xprt_str);
-               if (buflen && (len + totlen >= buflen))
+               len = svc_one_xprt_name(xprt, pos, buflen - totlen);
+               if (len < 0) {
+                       *buf = '\0';
+                       totlen = len;
+               }
+               if (len <= 0)
                        break;
-               strcpy(buf+totlen, xprt_str);
+
+               pos += len;
                totlen += len;
        }
+
        spin_unlock_bh(&serv->sv_lock);
        return totlen;
 }
index 9d504234af4a664f5f116171b9d6e02825d1ac4f..004a2f9dc43287c557e131ed9326e73e89c0e479 100644 (file)
@@ -240,42 +240,76 @@ out:
 /*
  * Report socket names for nfsdfs
  */
-static int one_sock_name(char *buf, struct svc_sock *svsk)
+static int svc_one_sock_name(struct svc_sock *svsk, char *buf, int remaining)
 {
+       const struct sock *sk = svsk->sk_sk;
+       const char *proto_name = sk->sk_protocol == IPPROTO_UDP ?
+                                                       "udp" : "tcp";
        int len;
 
-       switch(svsk->sk_sk->sk_family) {
-       case AF_INET:
-               len = sprintf(buf, "ipv4 %s %pI4 %d\n",
-                             svsk->sk_sk->sk_protocol == IPPROTO_UDP ?
-                             "udp" : "tcp",
-                             &inet_sk(svsk->sk_sk)->rcv_saddr,
-                             inet_sk(svsk->sk_sk)->num);
+       switch (sk->sk_family) {
+       case PF_INET:
+               len = snprintf(buf, remaining, "ipv4 %s %pI4 %d\n",
+                               proto_name,
+                               &inet_sk(sk)->rcv_saddr,
+                               inet_sk(sk)->num);
+               break;
+       case PF_INET6:
+               len = snprintf(buf, remaining, "ipv6 %s %pI6 %d\n",
+                               proto_name,
+                               &inet6_sk(sk)->rcv_saddr,
+                               inet_sk(sk)->num);
                break;
        default:
-               len = sprintf(buf, "*unknown-%d*\n",
-                              svsk->sk_sk->sk_family);
+               len = snprintf(buf, remaining, "*unknown-%d*\n",
+                               sk->sk_family);
+       }
+
+       if (len >= remaining) {
+               *buf = '\0';
+               return -ENAMETOOLONG;
        }
        return len;
 }
 
-int
-svc_sock_names(char *buf, struct svc_serv *serv, char *toclose)
+/**
+ * svc_sock_names - construct a list of listener names in a string
+ * @serv: pointer to RPC service
+ * @buf: pointer to a buffer to fill in with socket names
+ * @buflen: size of the buffer to be filled
+ * @toclose: pointer to '\0'-terminated C string containing the name
+ *             of a listener to be closed
+ *
+ * Fills in @buf with a '\n'-separated list of names of listener
+ * sockets.  If @toclose is not NULL, the socket named by @toclose
+ * is closed, and is not included in the output list.
+ *
+ * Returns positive length of the socket name string, or a negative
+ * errno value on error.
+ */
+int svc_sock_names(struct svc_serv *serv, char *buf, const size_t buflen,
+                  const char *toclose)
 {
        struct svc_sock *svsk, *closesk = NULL;
        int len = 0;
 
        if (!serv)
                return 0;
+
        spin_lock_bh(&serv->sv_lock);
        list_for_each_entry(svsk, &serv->sv_permsocks, sk_xprt.xpt_list) {
-               int onelen = one_sock_name(buf+len, svsk);
-               if (toclose && strcmp(toclose, buf+len) == 0)
+               int onelen = svc_one_sock_name(svsk, buf + len, buflen - len);
+               if (onelen < 0) {
+                       len = onelen;
+                       break;
+               }
+               if (toclose && strcmp(toclose, buf + len) == 0)
                        closesk = svsk;
                else
                        len += onelen;
        }
        spin_unlock_bh(&serv->sv_lock);
+
        if (closesk)
                /* Should unregister with portmap, but you cannot
                 * unregister just one protocol...
@@ -427,13 +461,14 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
                long            all[SVC_PKTINFO_SPACE / sizeof(long)];
        } buffer;
        struct cmsghdr *cmh = &buffer.hdr;
-       int             err, len;
        struct msghdr msg = {
                .msg_name = svc_addr(rqstp),
                .msg_control = cmh,
                .msg_controllen = sizeof(buffer),
                .msg_flags = MSG_DONTWAIT,
        };
+       size_t len;
+       int err;
 
        if (test_and_clear_bit(XPT_CHNGBUF, &svsk->sk_xprt.xpt_flags))
            /* udp sockets need large rcvbuf as all pending
@@ -465,8 +500,8 @@ static int svc_udp_recvfrom(struct svc_rqst *rqstp)
                return -EAGAIN;
        }
        len = svc_addr_len(svc_addr(rqstp));
-       if (len < 0)
-               return len;
+       if (len == 0)
+               return -EAFNOSUPPORT;
        rqstp->rq_addrlen = len;
        if (skb->tstamp.tv64 == 0) {
                skb->tstamp = ktime_get_real();
@@ -1148,9 +1183,19 @@ static struct svc_sock *svc_setup_socket(struct svc_serv *serv,
        return svsk;
 }
 
-int svc_addsock(struct svc_serv *serv,
-               int fd,
-               char *name_return)
+/**
+ * svc_addsock - add a listener socket to an RPC service
+ * @serv: pointer to RPC service to which to add a new listener
+ * @fd: file descriptor of the new listener
+ * @name_return: pointer to buffer to fill in with name of listener
+ * @len: size of the buffer
+ *
+ * Fills in socket name and returns positive length of name if successful.
+ * Name is terminated with '\n'.  On error, returns a negative errno
+ * value.
+ */
+int svc_addsock(struct svc_serv *serv, const int fd, char *name_return,
+               const size_t len)
 {
        int err = 0;
        struct socket *so = sockfd_lookup(fd, &err);
@@ -1190,7 +1235,7 @@ int svc_addsock(struct svc_serv *serv,
                sockfd_put(so);
                return err;
        }
-       return one_sock_name(name_return, svsk);
+       return svc_one_sock_name(svsk, name_return, len);
 }
 EXPORT_SYMBOL_GPL(svc_addsock);