Merge branch 'devel' into next
authorTrond Myklebust <Trond.Myklebust@netapp.com>
Tue, 30 Dec 2008 21:51:43 +0000 (16:51 -0500)
committerTrond Myklebust <Trond.Myklebust@netapp.com>
Tue, 30 Dec 2008 21:51:43 +0000 (16:51 -0500)
43 files changed:
fs/lockd/clntlock.c
fs/lockd/host.c
fs/lockd/svc.c
fs/nfs/callback.c
fs/nfs/client.c
fs/nfs/delegation.c
fs/nfs/delegation.h
fs/nfs/dir.c
fs/nfs/inode.c
fs/nfs/internal.h
fs/nfs/mount_clnt.c
fs/nfs/nfs4_fs.h
fs/nfs/nfs4proc.c
fs/nfs/nfs4renewd.c
fs/nfs/nfs4state.c
fs/nfs/nfs4xdr.c
fs/nfs/nfsroot.c
fs/nfs/read.c
fs/nfs/super.c
fs/nfs_common/nfsacl.c
fs/nfsd/nfs4callback.c
fs/nfsd/nfs4state.c
include/linux/jiffies.h
include/linux/lockd/bind.h
include/linux/lockd/lockd.h
include/linux/nfs_fs.h
include/linux/nfs_fs_sb.h
include/linux/nfs_mount.h
include/linux/nfs_xdr.h
include/linux/nfsd/state.h
include/linux/sunrpc/clnt.h
include/linux/sunrpc/rpc_pipe_fs.h
include/linux/sunrpc/svcauth_gss.h
include/linux/sunrpc/xdr.h
include/linux/sunrpc/xprt.h
net/sunrpc/auth.c
net/sunrpc/auth_gss/auth_gss.c
net/sunrpc/auth_gss/gss_generic_token.c
net/sunrpc/auth_gss/gss_mech_switch.c
net/sunrpc/auth_gss/svcauth_gss.c
net/sunrpc/clnt.c
net/sunrpc/rpc_pipe.c
net/sunrpc/xdr.c

index 8307dd64bf46ca746d9c973f209626de49627d17..1f3b0fc0d351de0f6bd6ea7f48e3344e99f1fe56 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/sunrpc/svc.h>
 #include <linux/lockd/lockd.h>
 #include <linux/smp_lock.h>
+#include <linux/kthread.h>
 
 #define NLMDBG_FACILITY                NLMDBG_CLIENT
 
@@ -60,7 +61,7 @@ struct nlm_host *nlmclnt_init(const struct nlmclnt_initdata *nlm_init)
 
        host = nlmclnt_lookup_host(nlm_init->address, nlm_init->addrlen,
                                   nlm_init->protocol, nlm_version,
-                                  nlm_init->hostname);
+                                  nlm_init->hostname, nlm_init->noresvport);
        if (host == NULL) {
                lockd_down();
                return ERR_PTR(-ENOLCK);
@@ -191,11 +192,15 @@ __be32 nlmclnt_grant(const struct sockaddr *addr, const struct nlm_lock *lock)
 void
 nlmclnt_recovery(struct nlm_host *host)
 {
+       struct task_struct *task;
+
        if (!host->h_reclaiming++) {
                nlm_get_host(host);
-               __module_get(THIS_MODULE);
-               if (kernel_thread(reclaimer, host, CLONE_FS | CLONE_FILES) < 0)
-                       module_put(THIS_MODULE);
+               task = kthread_run(reclaimer, host, "%s-reclaim", host->h_name);
+               if (IS_ERR(task))
+                       printk(KERN_ERR "lockd: unable to spawn reclaimer "
+                               "thread. Locks for %s won't be reclaimed! "
+                               "(%ld)\n", host->h_name, PTR_ERR(task));
        }
 }
 
@@ -207,7 +212,6 @@ reclaimer(void *ptr)
        struct file_lock *fl, *next;
        u32 nsmstate;
 
-       daemonize("%s-reclaim", host->h_name);
        allow_signal(SIGKILL);
 
        down_write(&host->h_rwsem);
@@ -233,7 +237,12 @@ restart:
        list_for_each_entry_safe(fl, next, &host->h_reclaim, fl_u.nfs_fl.list) {
                list_del_init(&fl->fl_u.nfs_fl.list);
 
-               /* Why are we leaking memory here? --okir */
+               /*
+                * sending this thread a SIGKILL will result in any unreclaimed
+                * locks being removed from the h_granted list. This means that
+                * the kernel will not attempt to reclaim them again if a new
+                * reclaimer thread is spawned for this host.
+                */
                if (signalled())
                        continue;
                if (nlmclnt_reclaim(host, fl) != 0)
@@ -261,5 +270,5 @@ restart:
        nlm_release_host(host);
        lockd_down();
        unlock_kernel();
-       module_put_and_exit(0);
+       return 0;
 }
index e05d044160378fefd2989674903c032d5668183a..abdebf76b8203c12a6a12532e12f498b83a90614 100644 (file)
@@ -48,6 +48,7 @@ struct nlm_lookup_host_info {
        const size_t            hostname_len;   /* it's length */
        const struct sockaddr   *src_sap;       /* our address (optional) */
        const size_t            src_len;        /* it's length */
+       const int               noresvport;     /* use non-priv port */
 };
 
 /*
@@ -222,6 +223,7 @@ static struct nlm_host *nlm_lookup_host(struct nlm_lookup_host_info *ni)
        host->h_nsmstate   = 0;                 /* real NSM state */
        host->h_nsmhandle  = nsm;
        host->h_server     = ni->server;
+       host->h_noresvport = ni->noresvport;
        hlist_add_head(&host->h_hash, chain);
        INIT_LIST_HEAD(&host->h_lockowners);
        spin_lock_init(&host->h_lock);
@@ -272,6 +274,7 @@ nlm_destroy_host(struct nlm_host *host)
  * @protocol: transport protocol to use
  * @version: NLM protocol version
  * @hostname: '\0'-terminated hostname of server
+ * @noresvport: 1 if non-privileged port should be used
  *
  * Returns an nlm_host structure that matches the passed-in
  * [server address, transport protocol, NLM version, server hostname].
@@ -281,7 +284,9 @@ nlm_destroy_host(struct nlm_host *host)
 struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
                                     const size_t salen,
                                     const unsigned short protocol,
-                                    const u32 version, const char *hostname)
+                                    const u32 version,
+                                    const char *hostname,
+                                    int noresvport)
 {
        const struct sockaddr source = {
                .sa_family      = AF_UNSPEC,
@@ -296,6 +301,7 @@ struct nlm_host *nlmclnt_lookup_host(const struct sockaddr *sap,
                .hostname_len   = strlen(hostname),
                .src_sap        = &source,
                .src_len        = sizeof(source),
+               .noresvport     = noresvport,
        };
 
        dprintk("lockd: %s(host='%s', vers=%u, proto=%s)\n", __func__,
@@ -417,6 +423,8 @@ nlm_bind_host(struct nlm_host *host)
                 */
                if (!host->h_server)
                        args.flags |= RPC_CLNT_CREATE_HARDRTRY;
+               if (host->h_noresvport)
+                       args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
 
                clnt = rpc_create(&args);
                if (!IS_ERR(clnt))
index 56b076736b56afd054aa91b7585d80257f0f22ac..252d80163d024d88fbf849f20ad8b7c7ed48d7ad 100644 (file)
@@ -45,7 +45,7 @@
 static struct svc_program      nlmsvc_program;
 
 struct nlmsvc_binding *                nlmsvc_ops;
-EXPORT_SYMBOL(nlmsvc_ops);
+EXPORT_SYMBOL_GPL(nlmsvc_ops);
 
 static DEFINE_MUTEX(nlmsvc_mutex);
 static unsigned int            nlmsvc_users;
@@ -300,7 +300,7 @@ out:
        mutex_unlock(&nlmsvc_mutex);
        return error;
 }
-EXPORT_SYMBOL(lockd_up);
+EXPORT_SYMBOL_GPL(lockd_up);
 
 /*
  * Decrement the user count and bring down lockd if we're the last.
@@ -329,7 +329,7 @@ lockd_down(void)
 out:
        mutex_unlock(&nlmsvc_mutex);
 }
-EXPORT_SYMBOL(lockd_down);
+EXPORT_SYMBOL_GPL(lockd_down);
 
 #ifdef CONFIG_SYSCTL
 
index c2e9cfd9e5a4d04e3c3a6a82e2f49c8deb8821e6..3e634f2a1083cf556e9c3429320e0cd458d3c5b1 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/mutex.h>
 #include <linux/freezer.h>
 #include <linux/kthread.h>
+#include <linux/sunrpc/svcauth_gss.h>
 
 #include <net/inet_sock.h>
 
@@ -182,10 +183,34 @@ void nfs_callback_down(void)
        mutex_unlock(&nfs_callback_mutex);
 }
 
+static int check_gss_callback_principal(struct nfs_client *clp,
+                                       struct svc_rqst *rqstp)
+{
+       struct rpc_clnt *r = clp->cl_rpcclient;
+       char *p = svc_gss_principal(rqstp);
+
+       /*
+        * It might just be a normal user principal, in which case
+        * userspace won't bother to tell us the name at all.
+        */
+       if (p == NULL)
+               return SVC_DENIED;
+
+       /* Expect a GSS_C_NT_HOSTBASED_NAME like "nfs@serverhostname" */
+
+       if (memcmp(p, "nfs@", 4) != 0)
+               return SVC_DENIED;
+       p += 4;
+       if (strcmp(p, r->cl_server) != 0)
+               return SVC_DENIED;
+       return SVC_OK;
+}
+
 static int nfs_callback_authenticate(struct svc_rqst *rqstp)
 {
        struct nfs_client *clp;
        RPC_IFDEBUG(char buf[RPC_MAX_ADDRBUFLEN]);
+       int ret = SVC_OK;
 
        /* Don't talk to strangers */
        clp = nfs_find_client(svc_addr(rqstp), 4);
@@ -194,21 +219,22 @@ static int nfs_callback_authenticate(struct svc_rqst *rqstp)
 
        dprintk("%s: %s NFSv4 callback!\n", __func__,
                        svc_print_addr(rqstp, buf, sizeof(buf)));
-       nfs_put_client(clp);
 
        switch (rqstp->rq_authop->flavour) {
                case RPC_AUTH_NULL:
                        if (rqstp->rq_proc != CB_NULL)
-                               return SVC_DENIED;
+                               ret = SVC_DENIED;
                        break;
                case RPC_AUTH_UNIX:
                        break;
                case RPC_AUTH_GSS:
-                       /* FIXME: RPCSEC_GSS handling? */
+                       ret = check_gss_callback_principal(clp, rqstp);
+                       break;
                default:
-                       return SVC_DENIED;
+                       ret = SVC_DENIED;
        }
-       return SVC_OK;
+       nfs_put_client(clp);
+       return ret;
 }
 
 /*
index 7547600b61741e26eb1bcaa52b4cfbad9349d673..9b728f3565a13d154c4c47ca8fed9ea1d32a4d9b 100644 (file)
@@ -143,7 +143,6 @@ static struct nfs_client *nfs_alloc_client(const struct nfs_client_initdata *cl_
        clp->cl_proto = cl_init->proto;
 
 #ifdef CONFIG_NFS_V4
-       init_rwsem(&clp->cl_sem);
        INIT_LIST_HEAD(&clp->cl_delegations);
        spin_lock_init(&clp->cl_lock);
        INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state);
@@ -224,31 +223,54 @@ void nfs_put_client(struct nfs_client *clp)
        }
 }
 
-static int nfs_sockaddr_match_ipaddr4(const struct sockaddr_in *sa1,
-                                const struct sockaddr_in *sa2)
+#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
+static const struct in6_addr *nfs_map_ipv4_addr(const struct sockaddr *sa, struct in6_addr *addr_mapped)
 {
-       return sa1->sin_addr.s_addr == sa2->sin_addr.s_addr;
+       switch (sa->sa_family) {
+               default:
+                       return NULL;
+               case AF_INET6:
+                       return &((const struct sockaddr_in6 *)sa)->sin6_addr;
+                       break;
+               case AF_INET:
+                       ipv6_addr_set_v4mapped(((const struct sockaddr_in *)sa)->sin_addr.s_addr,
+                                       addr_mapped);
+                       return addr_mapped;
+       }
 }
 
-static int nfs_sockaddr_match_ipaddr6(const struct sockaddr_in6 *sa1,
-                                const struct sockaddr_in6 *sa2)
+static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,
+               const struct sockaddr *sa2)
+{
+       const struct in6_addr *addr1;
+       const struct in6_addr *addr2;
+       struct in6_addr addr1_mapped;
+       struct in6_addr addr2_mapped;
+
+       addr1 = nfs_map_ipv4_addr(sa1, &addr1_mapped);
+       if (likely(addr1 != NULL)) {
+               addr2 = nfs_map_ipv4_addr(sa2, &addr2_mapped);
+               if (likely(addr2 != NULL))
+                       return ipv6_addr_equal(addr1, addr2);
+       }
+       return 0;
+}
+#else
+static int nfs_sockaddr_match_ipaddr4(const struct sockaddr_in *sa1,
+                                const struct sockaddr_in *sa2)
 {
-       return ipv6_addr_equal(&sa1->sin6_addr, &sa2->sin6_addr);
+       return sa1->sin_addr.s_addr == sa2->sin_addr.s_addr;
 }
 
 static int nfs_sockaddr_match_ipaddr(const struct sockaddr *sa1,
                                 const struct sockaddr *sa2)
 {
-       switch (sa1->sa_family) {
-       case AF_INET:
-               return nfs_sockaddr_match_ipaddr4((const struct sockaddr_in *)sa1,
-                               (const struct sockaddr_in *)sa2);
-       case AF_INET6:
-               return nfs_sockaddr_match_ipaddr6((const struct sockaddr_in6 *)sa1,
-                               (const struct sockaddr_in6 *)sa2);
-       }
-       BUG();
+       if (unlikely(sa1->sa_family != AF_INET || sa2->sa_family != AF_INET))
+               return 0;
+       return nfs_sockaddr_match_ipaddr4((const struct sockaddr_in *)sa1,
+                       (const struct sockaddr_in *)sa2);
 }
+#endif
 
 /*
  * Find a client by IP address and protocol version
@@ -270,8 +292,6 @@ struct nfs_client *nfs_find_client(const struct sockaddr *addr, u32 nfsversion)
                if (clp->rpc_ops->version != nfsversion)
                        continue;
 
-               if (addr->sa_family != clap->sa_family)
-                       continue;
                /* Match only the IP address, not the port number */
                if (!nfs_sockaddr_match_ipaddr(addr, clap))
                        continue;
@@ -305,8 +325,6 @@ struct nfs_client *nfs_find_client_next(struct nfs_client *clp)
                if (clp->rpc_ops->version != nfsvers)
                        continue;
 
-               if (sap->sa_family != clap->sa_family)
-                       continue;
                /* Match only the IP address, not the port number */
                if (!nfs_sockaddr_match_ipaddr(sap, clap))
                        continue;
@@ -470,7 +488,7 @@ static void nfs_init_timeout_values(struct rpc_timeout *to, int proto,
 static int nfs_create_rpc_client(struct nfs_client *clp,
                                 const struct rpc_timeout *timeparms,
                                 rpc_authflavor_t flavor,
-                                int flags)
+                                int discrtry, int noresvport)
 {
        struct rpc_clnt         *clnt = NULL;
        struct rpc_create_args args = {
@@ -482,9 +500,13 @@ static int nfs_create_rpc_client(struct nfs_client *clp,
                .program        = &nfs_program,
                .version        = clp->rpc_ops->version,
                .authflavor     = flavor,
-               .flags          = flags,
        };
 
+       if (discrtry)
+               args.flags |= RPC_CLNT_CREATE_DISCRTRY;
+       if (noresvport)
+               args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
+
        if (!IS_ERR(clp->cl_rpcclient))
                return 0;
 
@@ -522,6 +544,8 @@ static int nfs_start_lockd(struct nfs_server *server)
                .protocol       = server->flags & NFS_MOUNT_TCP ?
                                                IPPROTO_TCP : IPPROTO_UDP,
                .nfs_version    = clp->rpc_ops->version,
+               .noresvport     = server->flags & NFS_MOUNT_NORESVPORT ?
+                                       1 : 0,
        };
 
        if (nlm_init.nfs_version > 3)
@@ -623,7 +647,8 @@ static int nfs_init_client(struct nfs_client *clp,
         * Create a client RPC handle for doing FSSTAT with UNIX auth only
         * - RFC 2623, sec 2.3.2
         */
-       error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX, 0);
+       error = nfs_create_rpc_client(clp, timeparms, RPC_AUTH_UNIX,
+                                     0, data->flags & NFS_MOUNT_NORESVPORT);
        if (error < 0)
                goto error;
        nfs_mark_client_ready(clp, NFS_CS_READY);
@@ -965,7 +990,8 @@ error:
 static int nfs4_init_client(struct nfs_client *clp,
                const struct rpc_timeout *timeparms,
                const char *ip_addr,
-               rpc_authflavor_t authflavour)
+               rpc_authflavor_t authflavour,
+               int flags)
 {
        int error;
 
@@ -979,7 +1005,7 @@ static int nfs4_init_client(struct nfs_client *clp,
        clp->rpc_ops = &nfs_v4_clientops;
 
        error = nfs_create_rpc_client(clp, timeparms, authflavour,
-                                       RPC_CLNT_CREATE_DISCRTRY);
+                                     1, flags & NFS_MOUNT_NORESVPORT);
        if (error < 0)
                goto error;
        memcpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr));
@@ -1030,7 +1056,8 @@ static int nfs4_set_client(struct nfs_server *server,
                error = PTR_ERR(clp);
                goto error;
        }
-       error = nfs4_init_client(clp, timeparms, ip_addr, authflavour);
+       error = nfs4_init_client(clp, timeparms, ip_addr, authflavour,
+                                       server->flags);
        if (error < 0)
                goto error_put;
 
@@ -1059,6 +1086,10 @@ static int nfs4_init_server(struct nfs_server *server,
        nfs_init_timeout_values(&timeparms, data->nfs_server.protocol,
                        data->timeo, data->retrans);
 
+       /* Initialise the client representation from the mount data */
+       server->flags = data->flags;
+       server->caps |= NFS_CAP_ATOMIC_OPEN;
+
        /* Get a client record */
        error = nfs4_set_client(server,
                        data->nfs_server.hostname,
@@ -1071,10 +1102,6 @@ static int nfs4_init_server(struct nfs_server *server,
        if (error < 0)
                goto error;
 
-       /* Initialise the client representation from the mount data */
-       server->flags = data->flags;
-       server->caps |= NFS_CAP_ATOMIC_OPEN;
-
        if (data->rsize)
                server->rsize = nfs_block_size(data->rsize, NULL);
        if (data->wsize)
@@ -1177,6 +1204,10 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
        parent_server = NFS_SB(data->sb);
        parent_client = parent_server->nfs_client;
 
+       /* Initialise the client representation from the parent server */
+       nfs_server_copy_userdata(server, parent_server);
+       server->caps |= NFS_CAP_ATOMIC_OPEN;
+
        /* Get a client representation.
         * Note: NFSv4 always uses TCP, */
        error = nfs4_set_client(server, data->hostname,
@@ -1189,10 +1220,6 @@ struct nfs_server *nfs4_create_referral_server(struct nfs_clone_mount *data,
        if (error < 0)
                goto error;
 
-       /* Initialise the client representation from the parent server */
-       nfs_server_copy_userdata(server, parent_server);
-       server->caps |= NFS_CAP_ATOMIC_OPEN;
-
        error = nfs_init_server_rpcclient(server, parent_server->client->cl_timeout, data->authflavor);
        if (error < 0)
                goto error;
index cc563cfa69407c64553d4e6730bc020df2f11790..968225a88015b5c544394dc321831e642ee210ff 100644 (file)
@@ -43,6 +43,27 @@ static void nfs_free_delegation(struct nfs_delegation *delegation)
                put_rpccred(cred);
 }
 
+void nfs_mark_delegation_referenced(struct nfs_delegation *delegation)
+{
+       set_bit(NFS_DELEGATION_REFERENCED, &delegation->flags);
+}
+
+int nfs_have_delegation(struct inode *inode, fmode_t flags)
+{
+       struct nfs_delegation *delegation;
+       int ret = 0;
+
+       flags &= FMODE_READ|FMODE_WRITE;
+       rcu_read_lock();
+       delegation = rcu_dereference(NFS_I(inode)->delegation);
+       if (delegation != NULL && (delegation->type & flags) == flags) {
+               nfs_mark_delegation_referenced(delegation);
+               ret = 1;
+       }
+       rcu_read_unlock();
+       return ret;
+}
+
 static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state)
 {
        struct inode *inode = state->inode;
@@ -119,7 +140,7 @@ void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, st
        delegation->maxsize = res->maxsize;
        oldcred = delegation->cred;
        delegation->cred = get_rpccred(cred);
-       delegation->flags &= ~NFS_DELEGATION_NEED_RECLAIM;
+       clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
        NFS_I(inode)->delegation_state = delegation->type;
        smp_wmb();
        put_rpccred(oldcred);
@@ -134,19 +155,35 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *
        return res;
 }
 
+static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation)
+{
+       struct inode *inode = NULL;
+
+       spin_lock(&delegation->lock);
+       if (delegation->inode != NULL)
+               inode = igrab(delegation->inode);
+       spin_unlock(&delegation->lock);
+       return inode;
+}
+
 static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid)
 {
        struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation);
 
        if (delegation == NULL)
                goto nomatch;
+       spin_lock(&delegation->lock);
        if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data,
                                sizeof(delegation->stateid.data)) != 0)
-               goto nomatch;
+               goto nomatch_unlock;
        list_del_rcu(&delegation->super_list);
+       delegation->inode = NULL;
        nfsi->delegation_state = 0;
        rcu_assign_pointer(nfsi->delegation, NULL);
+       spin_unlock(&delegation->lock);
        return delegation;
+nomatch_unlock:
+       spin_unlock(&delegation->lock);
 nomatch:
        return NULL;
 }
@@ -172,6 +209,8 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
        delegation->change_attr = nfsi->change_attr;
        delegation->cred = get_rpccred(cred);
        delegation->inode = inode;
+       delegation->flags = 1<<NFS_DELEGATION_REFERENCED;
+       spin_lock_init(&delegation->lock);
 
        spin_lock(&clp->cl_lock);
        if (rcu_dereference(nfsi->delegation) != NULL) {
@@ -226,21 +265,46 @@ static void nfs_msync_inode(struct inode *inode)
  */
 static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation)
 {
-       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
        struct nfs_inode *nfsi = NFS_I(inode);
 
        nfs_msync_inode(inode);
-       down_read(&clp->cl_sem);
        /* Guard against new delegated open calls */
        down_write(&nfsi->rwsem);
        nfs_delegation_claim_opens(inode, &delegation->stateid);
        up_write(&nfsi->rwsem);
-       up_read(&clp->cl_sem);
        nfs_msync_inode(inode);
 
        return nfs_do_return_delegation(inode, delegation, 1);
 }
 
+/*
+ * Return all delegations that have been marked for return
+ */
+void nfs_client_return_marked_delegations(struct nfs_client *clp)
+{
+       struct nfs_delegation *delegation;
+       struct inode *inode;
+
+restart:
+       rcu_read_lock();
+       list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
+               if (!test_and_clear_bit(NFS_DELEGATION_RETURN, &delegation->flags))
+                       continue;
+               inode = nfs_delegation_grab_inode(delegation);
+               if (inode == NULL)
+                       continue;
+               spin_lock(&clp->cl_lock);
+               delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
+               spin_unlock(&clp->cl_lock);
+               rcu_read_unlock();
+               if (delegation != NULL)
+                       __nfs_inode_return_delegation(inode, delegation);
+               iput(inode);
+               goto restart;
+       }
+       rcu_read_unlock();
+}
+
 /*
  * This function returns the delegation without reclaiming opens
  * or protecting against delegation reclaims.
@@ -279,83 +343,55 @@ int nfs_inode_return_delegation(struct inode *inode)
        return err;
 }
 
+static void nfs_mark_return_delegation(struct nfs_client *clp, struct nfs_delegation *delegation)
+{
+       set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
+       set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
+}
+
 /*
  * Return all delegations associated to a super block
  */
-void nfs_return_all_delegations(struct super_block *sb)
+void nfs_super_return_all_delegations(struct super_block *sb)
 {
        struct nfs_client *clp = NFS_SB(sb)->nfs_client;
        struct nfs_delegation *delegation;
-       struct inode *inode;
 
        if (clp == NULL)
                return;
-restart:
        rcu_read_lock();
        list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
-               if (delegation->inode->i_sb != sb)
-                       continue;
-               inode = igrab(delegation->inode);
-               if (inode == NULL)
-                       continue;
-               spin_lock(&clp->cl_lock);
-               delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
-               spin_unlock(&clp->cl_lock);
-               rcu_read_unlock();
-               if (delegation != NULL)
-                       __nfs_inode_return_delegation(inode, delegation);
-               iput(inode);
-               goto restart;
+               spin_lock(&delegation->lock);
+               if (delegation->inode != NULL && delegation->inode->i_sb == sb)
+                       set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
+               spin_unlock(&delegation->lock);
        }
        rcu_read_unlock();
+       nfs_client_return_marked_delegations(clp);
 }
 
-static int nfs_do_expire_all_delegations(void *ptr)
+static void nfs_client_mark_return_all_delegations(struct nfs_client *clp)
 {
-       struct nfs_client *clp = ptr;
        struct nfs_delegation *delegation;
-       struct inode *inode;
 
-       allow_signal(SIGKILL);
-restart:
-       if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) != 0)
-               goto out;
-       if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0)
-               goto out;
        rcu_read_lock();
        list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
-               inode = igrab(delegation->inode);
-               if (inode == NULL)
-                       continue;
-               spin_lock(&clp->cl_lock);
-               delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
-               spin_unlock(&clp->cl_lock);
-               rcu_read_unlock();
-               if (delegation)
-                       __nfs_inode_return_delegation(inode, delegation);
-               iput(inode);
-               goto restart;
+               set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
+               set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
        }
        rcu_read_unlock();
-out:
-       nfs_put_client(clp);
-       module_put_and_exit(0);
+}
+
+static void nfs_delegation_run_state_manager(struct nfs_client *clp)
+{
+       if (test_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state))
+               nfs4_schedule_state_manager(clp);
 }
 
 void nfs_expire_all_delegations(struct nfs_client *clp)
 {
-       struct task_struct *task;
-
-       __module_get(THIS_MODULE);
-       atomic_inc(&clp->cl_count);
-       task = kthread_run(nfs_do_expire_all_delegations, clp,
-                               "%s-delegreturn",
-                               rpc_peeraddr2str(clp->cl_rpcclient,
-                                                       RPC_DISPLAY_ADDR));
-       if (!IS_ERR(task))
-               return;
-       nfs_put_client(clp);
-       module_put(THIS_MODULE);
+       nfs_client_mark_return_all_delegations(clp);
+       nfs_delegation_run_state_manager(clp);
 }
 
 /*
@@ -363,68 +399,29 @@ void nfs_expire_all_delegations(struct nfs_client *clp)
  */
 void nfs_handle_cb_pathdown(struct nfs_client *clp)
 {
-       struct nfs_delegation *delegation;
-       struct inode *inode;
-
        if (clp == NULL)
                return;
-restart:
+       nfs_client_mark_return_all_delegations(clp);
+}
+
+static void nfs_client_mark_return_unreferenced_delegations(struct nfs_client *clp)
+{
+       struct nfs_delegation *delegation;
+
        rcu_read_lock();
        list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
-               inode = igrab(delegation->inode);
-               if (inode == NULL)
+               if (test_and_clear_bit(NFS_DELEGATION_REFERENCED, &delegation->flags))
                        continue;
-               spin_lock(&clp->cl_lock);
-               delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
-               spin_unlock(&clp->cl_lock);
-               rcu_read_unlock();
-               if (delegation != NULL)
-                       __nfs_inode_return_delegation(inode, delegation);
-               iput(inode);
-               goto restart;
+               set_bit(NFS_DELEGATION_RETURN, &delegation->flags);
+               set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
        }
        rcu_read_unlock();
 }
 
-struct recall_threadargs {
-       struct inode *inode;
-       struct nfs_client *clp;
-       const nfs4_stateid *stateid;
-
-       struct completion started;
-       int result;
-};
-
-static int recall_thread(void *data)
+void nfs_expire_unreferenced_delegations(struct nfs_client *clp)
 {
-       struct recall_threadargs *args = (struct recall_threadargs *)data;
-       struct inode *inode = igrab(args->inode);
-       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
-       struct nfs_inode *nfsi = NFS_I(inode);
-       struct nfs_delegation *delegation;
-
-       daemonize("nfsv4-delegreturn");
-
-       nfs_msync_inode(inode);
-       down_read(&clp->cl_sem);
-       down_write(&nfsi->rwsem);
-       spin_lock(&clp->cl_lock);
-       delegation = nfs_detach_delegation_locked(nfsi, args->stateid);
-       if (delegation != NULL)
-               args->result = 0;
-       else
-               args->result = -ENOENT;
-       spin_unlock(&clp->cl_lock);
-       complete(&args->started);
-       nfs_delegation_claim_opens(inode, args->stateid);
-       up_write(&nfsi->rwsem);
-       up_read(&clp->cl_sem);
-       nfs_msync_inode(inode);
-
-       if (delegation != NULL)
-               nfs_do_return_delegation(inode, delegation, 1);
-       iput(inode);
-       module_put_and_exit(0);
+       nfs_client_mark_return_unreferenced_delegations(clp);
+       nfs_delegation_run_state_manager(clp);
 }
 
 /*
@@ -432,22 +429,20 @@ static int recall_thread(void *data)
  */
 int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *stateid)
 {
-       struct recall_threadargs data = {
-               .inode = inode,
-               .stateid = stateid,
-       };
-       int status;
+       struct nfs_client *clp = NFS_SERVER(inode)->nfs_client;
+       struct nfs_delegation *delegation;
 
-       init_completion(&data.started);
-       __module_get(THIS_MODULE);
-       status = kernel_thread(recall_thread, &data, CLONE_KERNEL);
-       if (status < 0)
-               goto out_module_put;
-       wait_for_completion(&data.started);
-       return data.result;
-out_module_put:
-       module_put(THIS_MODULE);
-       return status;
+       rcu_read_lock();
+       delegation = rcu_dereference(NFS_I(inode)->delegation);
+       if (delegation == NULL || memcmp(delegation->stateid.data, stateid->data,
+                               sizeof(delegation->stateid.data)) != 0) {
+               rcu_read_unlock();
+               return -ENOENT;
+       }
+       nfs_mark_return_delegation(clp, delegation);
+       rcu_read_unlock();
+       nfs_delegation_run_state_manager(clp);
+       return 0;
 }
 
 /*
@@ -459,10 +454,14 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs
        struct inode *res = NULL;
        rcu_read_lock();
        list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
-               if (nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
+               spin_lock(&delegation->lock);
+               if (delegation->inode != NULL &&
+                   nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
                        res = igrab(delegation->inode);
-                       break;
                }
+               spin_unlock(&delegation->lock);
+               if (res != NULL)
+                       break;
        }
        rcu_read_unlock();
        return res;
@@ -476,7 +475,7 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp)
        struct nfs_delegation *delegation;
        rcu_read_lock();
        list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list)
-               delegation->flags |= NFS_DELEGATION_NEED_RECLAIM;
+               set_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags);
        rcu_read_unlock();
 }
 
@@ -486,17 +485,22 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp)
 void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
 {
        struct nfs_delegation *delegation;
+       struct inode *inode;
 restart:
        rcu_read_lock();
        list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) {
-               if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0)
+               if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) == 0)
+                       continue;
+               inode = nfs_delegation_grab_inode(delegation);
+               if (inode == NULL)
                        continue;
                spin_lock(&clp->cl_lock);
-               delegation = nfs_detach_delegation_locked(NFS_I(delegation->inode), NULL);
+               delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL);
                spin_unlock(&clp->cl_lock);
                rcu_read_unlock();
                if (delegation != NULL)
                        nfs_free_delegation(delegation);
+               iput(inode);
                goto restart;
        }
        rcu_read_unlock();
index f1c5e2a5d88e35ace148060ea33c4492dd2f3f4d..09f383795174d065f36a413a6f698b467a93c57c 100644 (file)
@@ -17,14 +17,20 @@ struct nfs_delegation {
        struct rpc_cred *cred;
        struct inode *inode;
        nfs4_stateid stateid;
-       int type;
-#define NFS_DELEGATION_NEED_RECLAIM 1
-       long flags;
+       fmode_t type;
        loff_t maxsize;
        __u64 change_attr;
+       unsigned long flags;
+       spinlock_t lock;
        struct rcu_head rcu;
 };
 
+enum {
+       NFS_DELEGATION_NEED_RECLAIM = 0,
+       NFS_DELEGATION_RETURN,
+       NFS_DELEGATION_REFERENCED,
+};
+
 int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
 void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res);
 int nfs_inode_return_delegation(struct inode *inode);
@@ -32,9 +38,11 @@ int nfs_async_inode_return_delegation(struct inode *inode, const nfs4_stateid *s
 void nfs_inode_return_delegation_noreclaim(struct inode *inode);
 
 struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs_fh *fhandle);
-void nfs_return_all_delegations(struct super_block *sb);
+void nfs_super_return_all_delegations(struct super_block *sb);
 void nfs_expire_all_delegations(struct nfs_client *clp);
+void nfs_expire_unreferenced_delegations(struct nfs_client *clp);
 void nfs_handle_cb_pathdown(struct nfs_client *clp);
+void nfs_client_return_marked_delegations(struct nfs_client *clp);
 
 void nfs_delegation_mark_reclaim(struct nfs_client *clp);
 void nfs_delegation_reap_unclaimed(struct nfs_client *clp);
@@ -45,22 +53,11 @@ int nfs4_open_delegation_recall(struct nfs_open_context *ctx, struct nfs4_state
 int nfs4_lock_delegation_recall(struct nfs4_state *state, struct file_lock *fl);
 int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode);
 
-static inline int nfs_have_delegation(struct inode *inode, int flags)
-{
-       struct nfs_delegation *delegation;
-       int ret = 0;
-
-       flags &= FMODE_READ|FMODE_WRITE;
-       rcu_read_lock();
-       delegation = rcu_dereference(NFS_I(inode)->delegation);
-       if (delegation != NULL && (delegation->type & flags) == flags)
-               ret = 1;
-       rcu_read_unlock();
-       return ret;
-}
+void nfs_mark_delegation_referenced(struct nfs_delegation *delegation);
+int nfs_have_delegation(struct inode *inode, fmode_t flags);
 
 #else
-static inline int nfs_have_delegation(struct inode *inode, int flags)
+static inline int nfs_have_delegation(struct inode *inode, fmode_t flags)
 {
        return 0;
 }
index 3e64b98f3a9337ab241624fc5a544425e7f05e45..e35c8199f82f108daade505679e6eee22ef3ec34 100644 (file)
@@ -799,6 +799,9 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
                goto out_bad;
        }
 
+       if (nfs_have_delegation(inode, FMODE_READ))
+               goto out_set_verifier;
+
        /* Force a full look up iff the parent directory has changed */
        if (!nfs_is_exclusive_create(dir, nd) && nfs_check_verifier(dir, dentry)) {
                if (nfs_lookup_verify_inode(inode, nd))
@@ -817,6 +820,7 @@ static int nfs_lookup_revalidate(struct dentry * dentry, struct nameidata *nd)
        if ((error = nfs_refresh_inode(inode, &fattr)) != 0)
                goto out_bad;
 
+out_set_verifier:
        nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
  out_valid:
        dput(parent);
@@ -973,7 +977,7 @@ struct dentry_operations nfs4_dentry_operations = {
  * Use intent information to determine whether we need to substitute
  * the NFSv4-style stateful OPEN for the LOOKUP call
  */
-static int is_atomic_open(struct inode *dir, struct nameidata *nd)
+static int is_atomic_open(struct nameidata *nd)
 {
        if (nd == NULL || nfs_lookup_check_intent(nd, LOOKUP_OPEN) == 0)
                return 0;
@@ -996,7 +1000,7 @@ static struct dentry *nfs_atomic_lookup(struct inode *dir, struct dentry *dentry
                        dir->i_sb->s_id, dir->i_ino, dentry->d_name.name);
 
        /* Check that we are indeed trying to open this file */
-       if (!is_atomic_open(dir, nd))
+       if (!is_atomic_open(nd))
                goto no_open;
 
        if (dentry->d_name.len > NFS_SERVER(dir)->namelen) {
@@ -1047,10 +1051,10 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
        struct inode *dir;
        int openflags, ret = 0;
 
+       if (!is_atomic_open(nd))
+               goto no_open;
        parent = dget_parent(dentry);
        dir = parent->d_inode;
-       if (!is_atomic_open(dir, nd))
-               goto no_open;
        /* We can't create new files in nfs_open_revalidate(), so we
         * optimize away revalidation of negative dentries.
         */
@@ -1062,11 +1066,11 @@ static int nfs_open_revalidate(struct dentry *dentry, struct nameidata *nd)
 
        /* NFS only supports OPEN on regular files */
        if (!S_ISREG(inode->i_mode))
-               goto no_open;
+               goto no_open_dput;
        openflags = nd->intent.open.flags;
        /* We cannot do exclusive creation on a positive dentry */
        if ((openflags & (O_CREAT|O_EXCL)) == (O_CREAT|O_EXCL))
-               goto no_open;
+               goto no_open_dput;
        /* We can't create new files, or truncate existing ones here */
        openflags &= ~(O_CREAT|O_TRUNC);
 
@@ -1081,10 +1085,9 @@ out:
        if (!ret)
                d_drop(dentry);
        return ret;
-no_open:
+no_open_dput:
        dput(parent);
-       if (inode != NULL && nfs_have_delegation(inode, FMODE_READ))
-               return 1;
+no_open:
        return nfs_lookup_revalidate(dentry, nd);
 }
 #endif /* CONFIG_NFSV4 */
@@ -1794,7 +1797,8 @@ static int nfs_access_get_cached(struct inode *inode, struct rpc_cred *cred, str
        cache = nfs_access_search_rbtree(inode, cred);
        if (cache == NULL)
                goto out;
-       if (!time_in_range(jiffies, cache->jiffies, cache->jiffies + nfsi->attrtimeo))
+       if (!nfs_have_delegation(inode, FMODE_READ) &&
+           !time_in_range_open(jiffies, cache->jiffies, cache->jiffies + nfsi->attrtimeo))
                goto out_stale;
        res->jiffies = cache->jiffies;
        res->cred = cache->cred;
index d22eb383e1cf80bb8234207e2a681391af802045..0c381686171e30cc1eab1f71128bce7837bd22dd 100644 (file)
@@ -592,7 +592,7 @@ static void nfs_file_set_open_context(struct file *filp, struct nfs_open_context
 /*
  * Given an inode, search for an open context with the desired characteristics
  */
-struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, int mode)
+struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode)
 {
        struct nfs_inode *nfsi = NFS_I(inode);
        struct nfs_open_context *pos, *ctx = NULL;
@@ -712,14 +712,7 @@ int nfs_attribute_timeout(struct inode *inode)
 
        if (nfs_have_delegation(inode, FMODE_READ))
                return 0;
-       /*
-        * Special case: if the attribute timeout is set to 0, then always
-        *               treat the cache as having expired (unless holding
-        *               a delegation).
-        */
-       if (nfsi->attrtimeo == 0)
-               return 1;
-       return !time_in_range(jiffies, nfsi->read_cache_jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo);
+       return !time_in_range_open(jiffies, nfsi->read_cache_jiffies, nfsi->read_cache_jiffies + nfsi->attrtimeo);
 }
 
 /**
@@ -1182,7 +1175,7 @@ static int nfs_update_inode(struct inode *inode, struct nfs_fattr *fattr)
                nfsi->attrtimeo_timestamp = now;
                nfsi->attr_gencount = nfs_inc_attr_generation_counter();
        } else {
-               if (!time_in_range(now, nfsi->attrtimeo_timestamp, nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) {
+               if (!time_in_range_open(now, nfsi->attrtimeo_timestamp, nfsi->attrtimeo_timestamp + nfsi->attrtimeo)) {
                        if ((nfsi->attrtimeo <<= 1) > NFS_MAXATTRTIMEO(inode))
                                nfsi->attrtimeo = NFS_MAXATTRTIMEO(inode);
                        nfsi->attrtimeo_timestamp = now;
index d212ee41caf21ce069f92784b229a7214859c58a..340ede8f608fe3402952890e7d82f26c004cbb65 100644 (file)
@@ -63,6 +63,20 @@ struct nfs_parsed_mount_data {
        struct security_mnt_opts lsm_opts;
 };
 
+/* mount_clnt.c */
+struct nfs_mount_request {
+       struct sockaddr         *sap;
+       size_t                  salen;
+       char                    *hostname;
+       char                    *dirpath;
+       u32                     version;
+       unsigned short          protocol;
+       struct nfs_fh           *fh;
+       int                     noresvport;
+};
+
+extern int nfs_mount(struct nfs_mount_request *info);
+
 /* client.c */
 extern struct rpc_program nfs_program;
 
index 086a6830d785c10269a3d0251130f14caad9d477..ca905a5bb1ba7457270953e18e06cd2b3b7596de 100644 (file)
@@ -29,47 +29,43 @@ struct mnt_fhstatus {
 
 /**
  * nfs_mount - Obtain an NFS file handle for the given host and path
- * @addr: pointer to server's address
- * @len: size of server's address
- * @hostname: name of server host, or NULL
- * @path: pointer to string containing export path to mount
- * @version: mount version to use for this request
- * @protocol: transport protocol to use for thie request
- * @fh: pointer to location to place returned file handle
+ * @info: pointer to mount request arguments
  *
  * Uses default timeout parameters specified by underlying transport.
  */
-int nfs_mount(struct sockaddr *addr, size_t len, char *hostname, char *path,
-             int version, int protocol, struct nfs_fh *fh)
+int nfs_mount(struct nfs_mount_request *info)
 {
        struct mnt_fhstatus     result = {
-               .fh             = fh
+               .fh             = info->fh
        };
        struct rpc_message msg  = {
-               .rpc_argp       = path,
+               .rpc_argp       = info->dirpath,
                .rpc_resp       = &result,
        };
        struct rpc_create_args args = {
-               .protocol       = protocol,
-               .address        = addr,
-               .addrsize       = len,
-               .servername     = hostname,
+               .protocol       = info->protocol,
+               .address        = info->sap,
+               .addrsize       = info->salen,
+               .servername     = info->hostname,
                .program        = &mnt_program,
-               .version        = version,
+               .version        = info->version,
                .authflavor     = RPC_AUTH_UNIX,
-               .flags          = 0,
        };
        struct rpc_clnt         *mnt_clnt;
        int                     status;
 
        dprintk("NFS: sending MNT request for %s:%s\n",
-               (hostname ? hostname : "server"), path);
+               (info->hostname ? info->hostname : "server"),
+                       info->dirpath);
+
+       if (info->noresvport)
+               args.flags |= RPC_CLNT_CREATE_NONPRIVPORT;
 
        mnt_clnt = rpc_create(&args);
        if (IS_ERR(mnt_clnt))
                goto out_clnt_err;
 
-       if (version == NFS_MNT3_VERSION)
+       if (info->version == NFS_MNT3_VERSION)
                msg.rpc_proc = &mnt_clnt->cl_procinfo[MOUNTPROC3_MNT];
        else
                msg.rpc_proc = &mnt_clnt->cl_procinfo[MNTPROC_MNT];
index ea790645fda66385a461c515c5bca63652cdd699..4e4d332043766915fd34f16625d92d345fffba39 100644 (file)
@@ -38,8 +38,12 @@ struct idmap;
  ((err) != NFSERR_NOFILEHANDLE))
 
 enum nfs4_client_state {
-       NFS4CLNT_STATE_RECOVER  = 0,
+       NFS4CLNT_MANAGER_RUNNING  = 0,
+       NFS4CLNT_CHECK_LEASE,
        NFS4CLNT_LEASE_EXPIRED,
+       NFS4CLNT_RECLAIM_REBOOT,
+       NFS4CLNT_RECLAIM_NOGRACE,
+       NFS4CLNT_DELEGRETURN,
 };
 
 /*
@@ -90,12 +94,18 @@ struct nfs4_state_owner {
 
        spinlock_t           so_lock;
        atomic_t             so_count;
+       unsigned long        so_flags;
        struct list_head     so_states;
        struct list_head     so_delegations;
        struct nfs_seqid_counter so_seqid;
        struct rpc_sequence  so_sequence;
 };
 
+enum {
+       NFS_OWNER_RECLAIM_REBOOT,
+       NFS_OWNER_RECLAIM_NOGRACE
+};
+
 /*
  * struct nfs4_state maintains the client-side state for a given
  * (state_owner,inode) tuple (OPEN) or state_owner (LOCK).
@@ -128,6 +138,8 @@ enum {
        NFS_O_RDONLY_STATE,             /* OPEN stateid has read-only state */
        NFS_O_WRONLY_STATE,             /* OPEN stateid has write-only state */
        NFS_O_RDWR_STATE,               /* OPEN stateid has read/write state */
+       NFS_STATE_RECLAIM_REBOOT,       /* OPEN stateid server rebooted */
+       NFS_STATE_RECLAIM_NOGRACE,      /* OPEN stateid needs to recover state */
 };
 
 struct nfs4_state {
@@ -149,7 +161,7 @@ struct nfs4_state {
        unsigned int n_rdonly;          /* Number of read-only references */
        unsigned int n_wronly;          /* Number of write-only references */
        unsigned int n_rdwr;            /* Number of read/write references */
-       int state;                      /* State on the server (R,W, or RW) */
+       fmode_t state;                  /* State on the server (R,W, or RW) */
        atomic_t count;
 };
 
@@ -157,9 +169,12 @@ struct nfs4_state {
 struct nfs4_exception {
        long timeout;
        int retry;
+       struct nfs4_state *state;
 };
 
 struct nfs4_state_recovery_ops {
+       int owner_flag_bit;
+       int state_flag_bit;
        int (*recover_open)(struct nfs4_state_owner *, struct nfs4_state *);
        int (*recover_lock)(struct nfs4_state *, struct file_lock *);
 };
@@ -174,7 +189,6 @@ extern ssize_t nfs4_listxattr(struct dentry *, char *, size_t);
 
 
 /* nfs4proc.c */
-extern int nfs4_map_errors(int err);
 extern int nfs4_proc_setclientid(struct nfs_client *, u32, unsigned short, struct rpc_cred *);
 extern int nfs4_proc_setclientid_confirm(struct nfs_client *, struct rpc_cred *);
 extern int nfs4_proc_async_renew(struct nfs_client *, struct rpc_cred *);
@@ -187,7 +201,7 @@ extern int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
                struct nfs4_fs_locations *fs_locations, struct page *page);
 
 extern struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops;
-extern struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops;
+extern struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops;
 
 extern const u32 nfs4_fattr_bitmap[2];
 extern const u32 nfs4_statfs_bitmap[2];
@@ -202,16 +216,18 @@ extern void nfs4_kill_renewd(struct nfs_client *);
 extern void nfs4_renew_state(struct work_struct *);
 
 /* nfs4state.c */
-struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp);
+struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp);
 
 extern struct nfs4_state_owner * nfs4_get_state_owner(struct nfs_server *, struct rpc_cred *);
 extern void nfs4_put_state_owner(struct nfs4_state_owner *);
 extern struct nfs4_state * nfs4_get_open_state(struct inode *, struct nfs4_state_owner *);
 extern void nfs4_put_open_state(struct nfs4_state *);
-extern void nfs4_close_state(struct path *, struct nfs4_state *, mode_t);
-extern void nfs4_close_sync(struct path *, struct nfs4_state *, mode_t);
-extern void nfs4_state_set_mode_locked(struct nfs4_state *, mode_t);
+extern void nfs4_close_state(struct path *, struct nfs4_state *, fmode_t);
+extern void nfs4_close_sync(struct path *, struct nfs4_state *, fmode_t);
+extern void nfs4_state_set_mode_locked(struct nfs4_state *, fmode_t);
 extern void nfs4_schedule_state_recovery(struct nfs_client *);
+extern void nfs4_schedule_state_manager(struct nfs_client *);
+extern int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state);
 extern void nfs4_put_lock_state(struct nfs4_lock_state *lsp);
 extern int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl);
 extern void nfs4_copy_stateid(nfs4_stateid *, struct nfs4_state *, fl_owner_t);
index 83e700a2b0c0096d100c58a31fcf3ad0023f73b6..8dde84b988d9cd349604eb4663a70fc4333fe367 100644 (file)
 struct nfs4_opendata;
 static int _nfs4_proc_open(struct nfs4_opendata *data);
 static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *);
-static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *);
-static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception);
-static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp);
+static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *, struct nfs4_state *);
 static int _nfs4_proc_lookup(struct inode *dir, const struct qstr *name, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
 static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr);
 
 /* Prevent leaks of NFSv4 errors into userland */
-int nfs4_map_errors(int err)
+static int nfs4_map_errors(int err)
 {
        if (err < -1000) {
                dprintk("%s could not handle NFSv4 error %d\n",
@@ -195,6 +193,83 @@ static void nfs4_setup_readdir(u64 cookie, __be32 *verifier, struct dentry *dent
        kunmap_atomic(start, KM_USER0);
 }
 
+static int nfs4_wait_bit_killable(void *word)
+{
+       if (fatal_signal_pending(current))
+               return -ERESTARTSYS;
+       schedule();
+       return 0;
+}
+
+static int nfs4_wait_clnt_recover(struct nfs_client *clp)
+{
+       int res;
+
+       might_sleep();
+
+       res = wait_on_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING,
+                       nfs4_wait_bit_killable, TASK_KILLABLE);
+       return res;
+}
+
+static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
+{
+       int res = 0;
+
+       might_sleep();
+
+       if (*timeout <= 0)
+               *timeout = NFS4_POLL_RETRY_MIN;
+       if (*timeout > NFS4_POLL_RETRY_MAX)
+               *timeout = NFS4_POLL_RETRY_MAX;
+       schedule_timeout_killable(*timeout);
+       if (fatal_signal_pending(current))
+               res = -ERESTARTSYS;
+       *timeout <<= 1;
+       return res;
+}
+
+/* This is the error handling routine for processes that are allowed
+ * to sleep.
+ */
+static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
+{
+       struct nfs_client *clp = server->nfs_client;
+       struct nfs4_state *state = exception->state;
+       int ret = errorcode;
+
+       exception->retry = 0;
+       switch(errorcode) {
+               case 0:
+                       return 0;
+               case -NFS4ERR_ADMIN_REVOKED:
+               case -NFS4ERR_BAD_STATEID:
+               case -NFS4ERR_OPENMODE:
+                       if (state == NULL)
+                               break;
+                       nfs4_state_mark_reclaim_nograce(clp, state);
+               case -NFS4ERR_STALE_CLIENTID:
+               case -NFS4ERR_STALE_STATEID:
+               case -NFS4ERR_EXPIRED:
+                       nfs4_schedule_state_recovery(clp);
+                       ret = nfs4_wait_clnt_recover(clp);
+                       if (ret == 0)
+                               exception->retry = 1;
+                       break;
+               case -NFS4ERR_FILE_OPEN:
+               case -NFS4ERR_GRACE:
+               case -NFS4ERR_DELAY:
+                       ret = nfs4_delay(server->client, &exception->timeout);
+                       if (ret != 0)
+                               break;
+               case -NFS4ERR_OLD_STATEID:
+                       exception->retry = 1;
+       }
+       /* We failed to handle the error */
+       return nfs4_map_errors(ret);
+}
+
+
 static void renew_lease(const struct nfs_server *server, unsigned long timestamp)
 {
        struct nfs_client *clp = server->nfs_client;
@@ -248,7 +323,7 @@ static void nfs4_init_opendata_res(struct nfs4_opendata *p)
 }
 
 static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
-               struct nfs4_state_owner *sp, int flags,
+               struct nfs4_state_owner *sp, fmode_t fmode, int flags,
                const struct iattr *attrs)
 {
        struct dentry *parent = dget_parent(path->dentry);
@@ -268,7 +343,8 @@ static struct nfs4_opendata *nfs4_opendata_alloc(struct path *path,
        p->owner = sp;
        atomic_inc(&sp->so_count);
        p->o_arg.fh = NFS_FH(dir);
-       p->o_arg.open_flags = flags,
+       p->o_arg.open_flags = flags;
+       p->o_arg.fmode = fmode & (FMODE_READ|FMODE_WRITE);
        p->o_arg.clientid = server->nfs_client->cl_clientid;
        p->o_arg.id = sp->so_owner_id.id;
        p->o_arg.name = &p->path.dentry->d_name;
@@ -324,10 +400,13 @@ static int nfs4_wait_for_completion_rpc_task(struct rpc_task *task)
        return ret;
 }
 
-static int can_open_cached(struct nfs4_state *state, int mode)
+static int can_open_cached(struct nfs4_state *state, fmode_t mode, int open_mode)
 {
        int ret = 0;
-       switch (mode & (FMODE_READ|FMODE_WRITE|O_EXCL)) {
+
+       if (open_mode & O_EXCL)
+               goto out;
+       switch (mode & (FMODE_READ|FMODE_WRITE)) {
                case FMODE_READ:
                        ret |= test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0;
                        break;
@@ -337,21 +416,23 @@ static int can_open_cached(struct nfs4_state *state, int mode)
                case FMODE_READ|FMODE_WRITE:
                        ret |= test_bit(NFS_O_RDWR_STATE, &state->flags) != 0;
        }
+out:
        return ret;
 }
 
-static int can_open_delegated(struct nfs_delegation *delegation, mode_t open_flags)
+static int can_open_delegated(struct nfs_delegation *delegation, fmode_t fmode)
 {
-       if ((delegation->type & open_flags) != open_flags)
+       if ((delegation->type & fmode) != fmode)
                return 0;
-       if (delegation->flags & NFS_DELEGATION_NEED_RECLAIM)
+       if (test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags))
                return 0;
+       nfs_mark_delegation_referenced(delegation);
        return 1;
 }
 
-static void update_open_stateflags(struct nfs4_state *state, mode_t open_flags)
+static void update_open_stateflags(struct nfs4_state *state, fmode_t fmode)
 {
-       switch (open_flags) {
+       switch (fmode) {
                case FMODE_WRITE:
                        state->n_wronly++;
                        break;
@@ -361,15 +442,15 @@ static void update_open_stateflags(struct nfs4_state *state, mode_t open_flags)
                case FMODE_READ|FMODE_WRITE:
                        state->n_rdwr++;
        }
-       nfs4_state_set_mode_locked(state, state->state | open_flags);
+       nfs4_state_set_mode_locked(state, state->state | fmode);
 }
 
-static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags)
+static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
 {
        if (test_bit(NFS_DELEGATED_STATE, &state->flags) == 0)
                memcpy(state->stateid.data, stateid->data, sizeof(state->stateid.data));
        memcpy(state->open_stateid.data, stateid->data, sizeof(state->open_stateid.data));
-       switch (open_flags) {
+       switch (fmode) {
                case FMODE_READ:
                        set_bit(NFS_O_RDONLY_STATE, &state->flags);
                        break;
@@ -381,16 +462,15 @@ static void nfs_set_open_stateid_locked(struct nfs4_state *state, nfs4_stateid *
        }
 }
 
-static void nfs_set_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, int open_flags)
+static void nfs_set_open_stateid(struct nfs4_state *state, nfs4_stateid *stateid, fmode_t fmode)
 {
        write_seqlock(&state->seqlock);
-       nfs_set_open_stateid_locked(state, stateid, open_flags);
+       nfs_set_open_stateid_locked(state, stateid, fmode);
        write_sequnlock(&state->seqlock);
 }
 
-static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *deleg_stateid, int open_flags)
+static void __update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, const nfs4_stateid *deleg_stateid, fmode_t fmode)
 {
-       open_flags &= (FMODE_READ|FMODE_WRITE);
        /*
         * Protect the call to nfs4_state_set_mode_locked and
         * serialise the stateid update
@@ -401,20 +481,60 @@ static void update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_sta
                set_bit(NFS_DELEGATED_STATE, &state->flags);
        }
        if (open_stateid != NULL)
-               nfs_set_open_stateid_locked(state, open_stateid, open_flags);
+               nfs_set_open_stateid_locked(state, open_stateid, fmode);
        write_sequnlock(&state->seqlock);
        spin_lock(&state->owner->so_lock);
-       update_open_stateflags(state, open_flags);
+       update_open_stateflags(state, fmode);
        spin_unlock(&state->owner->so_lock);
 }
 
-static void nfs4_return_incompatible_delegation(struct inode *inode, mode_t open_flags)
+static int update_open_stateid(struct nfs4_state *state, nfs4_stateid *open_stateid, nfs4_stateid *delegation, fmode_t fmode)
+{
+       struct nfs_inode *nfsi = NFS_I(state->inode);
+       struct nfs_delegation *deleg_cur;
+       int ret = 0;
+
+       fmode &= (FMODE_READ|FMODE_WRITE);
+
+       rcu_read_lock();
+       deleg_cur = rcu_dereference(nfsi->delegation);
+       if (deleg_cur == NULL)
+               goto no_delegation;
+
+       spin_lock(&deleg_cur->lock);
+       if (nfsi->delegation != deleg_cur ||
+           (deleg_cur->type & fmode) != fmode)
+               goto no_delegation_unlock;
+
+       if (delegation == NULL)
+               delegation = &deleg_cur->stateid;
+       else if (memcmp(deleg_cur->stateid.data, delegation->data, NFS4_STATEID_SIZE) != 0)
+               goto no_delegation_unlock;
+
+       nfs_mark_delegation_referenced(deleg_cur);
+       __update_open_stateid(state, open_stateid, &deleg_cur->stateid, fmode);
+       ret = 1;
+no_delegation_unlock:
+       spin_unlock(&deleg_cur->lock);
+no_delegation:
+       rcu_read_unlock();
+
+       if (!ret && open_stateid != NULL) {
+               __update_open_stateid(state, open_stateid, NULL, fmode);
+               ret = 1;
+       }
+
+       return ret;
+}
+
+
+static void nfs4_return_incompatible_delegation(struct inode *inode, fmode_t fmode)
 {
        struct nfs_delegation *delegation;
 
        rcu_read_lock();
        delegation = rcu_dereference(NFS_I(inode)->delegation);
-       if (delegation == NULL || (delegation->type & open_flags) == open_flags) {
+       if (delegation == NULL || (delegation->type & fmode) == fmode) {
                rcu_read_unlock();
                return;
        }
@@ -427,27 +547,28 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
        struct nfs4_state *state = opendata->state;
        struct nfs_inode *nfsi = NFS_I(state->inode);
        struct nfs_delegation *delegation;
-       int open_mode = opendata->o_arg.open_flags & (FMODE_READ|FMODE_WRITE|O_EXCL);
+       int open_mode = opendata->o_arg.open_flags & O_EXCL;
+       fmode_t fmode = opendata->o_arg.fmode;
        nfs4_stateid stateid;
        int ret = -EAGAIN;
 
-       rcu_read_lock();
-       delegation = rcu_dereference(nfsi->delegation);
        for (;;) {
-               if (can_open_cached(state, open_mode)) {
+               if (can_open_cached(state, fmode, open_mode)) {
                        spin_lock(&state->owner->so_lock);
-                       if (can_open_cached(state, open_mode)) {
-                               update_open_stateflags(state, open_mode);
+                       if (can_open_cached(state, fmode, open_mode)) {
+                               update_open_stateflags(state, fmode);
                                spin_unlock(&state->owner->so_lock);
-                               rcu_read_unlock();
                                goto out_return_state;
                        }
                        spin_unlock(&state->owner->so_lock);
                }
-               if (delegation == NULL)
-                       break;
-               if (!can_open_delegated(delegation, open_mode))
+               rcu_read_lock();
+               delegation = rcu_dereference(nfsi->delegation);
+               if (delegation == NULL ||
+                   !can_open_delegated(delegation, fmode)) {
+                       rcu_read_unlock();
                        break;
+               }
                /* Save the delegation */
                memcpy(stateid.data, delegation->stateid.data, sizeof(stateid.data));
                rcu_read_unlock();
@@ -455,19 +576,11 @@ static struct nfs4_state *nfs4_try_open_cached(struct nfs4_opendata *opendata)
                if (ret != 0)
                        goto out;
                ret = -EAGAIN;
-               rcu_read_lock();
-               delegation = rcu_dereference(nfsi->delegation);
-               /* If no delegation, try a cached open */
-               if (delegation == NULL)
-                       continue;
-               /* Is the delegation still valid? */
-               if (memcmp(stateid.data, delegation->stateid.data, sizeof(stateid.data)) != 0)
-                       continue;
-               rcu_read_unlock();
-               update_open_stateid(state, NULL, &stateid, open_mode);
-               goto out_return_state;
+
+               /* Try to update the stateid using the delegation */
+               if (update_open_stateid(state, NULL, &stateid, fmode))
+                       goto out_return_state;
        }
-       rcu_read_unlock();
 out:
        return ERR_PTR(ret);
 out_return_state:
@@ -480,7 +593,6 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data
        struct inode *inode;
        struct nfs4_state *state = NULL;
        struct nfs_delegation *delegation;
-       nfs4_stateid *deleg_stateid = NULL;
        int ret;
 
        if (!data->rpc_done) {
@@ -507,7 +619,7 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data
                if (delegation)
                        delegation_flags = delegation->flags;
                rcu_read_unlock();
-               if (!(delegation_flags & NFS_DELEGATION_NEED_RECLAIM))
+               if ((delegation_flags & 1UL<<NFS_DELEGATION_NEED_RECLAIM) == 0)
                        nfs_inode_set_delegation(state->inode,
                                        data->owner->so_cred,
                                        &data->o_res);
@@ -516,12 +628,9 @@ static struct nfs4_state *nfs4_opendata_to_nfs4_state(struct nfs4_opendata *data
                                        data->owner->so_cred,
                                        &data->o_res);
        }
-       rcu_read_lock();
-       delegation = rcu_dereference(NFS_I(inode)->delegation);
-       if (delegation != NULL)
-               deleg_stateid = &delegation->stateid;
-       update_open_stateid(state, &data->o_res.stateid, deleg_stateid, data->o_arg.open_flags);
-       rcu_read_unlock();
+
+       update_open_stateid(state, &data->o_res.stateid, NULL,
+                       data->o_arg.fmode);
        iput(inode);
 out:
        return state;
@@ -552,7 +661,7 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context
 {
        struct nfs4_opendata *opendata;
 
-       opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, NULL);
+       opendata = nfs4_opendata_alloc(&ctx->path, state->owner, 0, 0, NULL);
        if (opendata == NULL)
                return ERR_PTR(-ENOMEM);
        opendata->state = state;
@@ -560,12 +669,13 @@ static struct nfs4_opendata *nfs4_open_recoverdata_alloc(struct nfs_open_context
        return opendata;
 }
 
-static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, mode_t openflags, struct nfs4_state **res)
+static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, fmode_t fmode, struct nfs4_state **res)
 {
        struct nfs4_state *newstate;
        int ret;
 
-       opendata->o_arg.open_flags = openflags;
+       opendata->o_arg.open_flags = 0;
+       opendata->o_arg.fmode = fmode;
        memset(&opendata->o_res, 0, sizeof(opendata->o_res));
        memset(&opendata->c_res, 0, sizeof(opendata->c_res));
        nfs4_init_opendata_res(opendata);
@@ -575,7 +685,7 @@ static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, mode_t openf
        newstate = nfs4_opendata_to_nfs4_state(opendata);
        if (IS_ERR(newstate))
                return PTR_ERR(newstate);
-       nfs4_close_state(&opendata->path, newstate, openflags);
+       nfs4_close_state(&opendata->path, newstate, fmode);
        *res = newstate;
        return 0;
 }
@@ -631,7 +741,7 @@ static int _nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state
 {
        struct nfs_delegation *delegation;
        struct nfs4_opendata *opendata;
-       int delegation_type = 0;
+       fmode_t delegation_type = 0;
        int status;
 
        opendata = nfs4_open_recoverdata_alloc(ctx, state);
@@ -641,7 +751,7 @@ static int _nfs4_do_open_reclaim(struct nfs_open_context *ctx, struct nfs4_state
        opendata->o_arg.fh = NFS_FH(state->inode);
        rcu_read_lock();
        delegation = rcu_dereference(NFS_I(state->inode)->delegation);
-       if (delegation != NULL && (delegation->flags & NFS_DELEGATION_NEED_RECLAIM) != 0)
+       if (delegation != NULL && test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) != 0)
                delegation_type = delegation->type;
        rcu_read_unlock();
        opendata->o_arg.u.delegation_type = delegation_type;
@@ -744,7 +854,7 @@ static void nfs4_open_confirm_release(void *calldata)
                goto out_free;
        state = nfs4_opendata_to_nfs4_state(data);
        if (!IS_ERR(state))
-               nfs4_close_state(&data->path, state, data->o_arg.open_flags);
+               nfs4_close_state(&data->path, state, data->o_arg.fmode);
 out_free:
        nfs4_opendata_put(data);
 }
@@ -808,12 +918,12 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata)
        if (data->state != NULL) {
                struct nfs_delegation *delegation;
 
-               if (can_open_cached(data->state, data->o_arg.open_flags & (FMODE_READ|FMODE_WRITE|O_EXCL)))
+               if (can_open_cached(data->state, data->o_arg.fmode, data->o_arg.open_flags))
                        goto out_no_action;
                rcu_read_lock();
                delegation = rcu_dereference(NFS_I(data->state->inode)->delegation);
                if (delegation != NULL &&
-                  (delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0) {
+                   test_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags) == 0) {
                        rcu_read_unlock();
                        goto out_no_action;
                }
@@ -877,7 +987,7 @@ static void nfs4_open_release(void *calldata)
                goto out_free;
        state = nfs4_opendata_to_nfs4_state(data);
        if (!IS_ERR(state))
-               nfs4_close_state(&data->path, state, data->o_arg.open_flags);
+               nfs4_close_state(&data->path, state, data->o_arg.fmode);
 out_free:
        nfs4_opendata_put(data);
 }
@@ -955,10 +1065,11 @@ static int nfs4_recover_expired_lease(struct nfs_server *server)
        int ret;
 
        for (;;) {
-               ret = nfs4_wait_clnt_recover(server->client, clp);
+               ret = nfs4_wait_clnt_recover(clp);
                if (ret != 0)
                        return ret;
-               if (!test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
+               if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) &&
+                   !test_bit(NFS4CLNT_CHECK_LEASE,&clp->cl_state))
                        break;
                nfs4_schedule_state_recovery(clp);
        }
@@ -993,8 +1104,9 @@ static inline int nfs4_do_open_expired(struct nfs_open_context *ctx, struct nfs4
 
        do {
                err = _nfs4_open_expired(ctx, state);
-               if (err == -NFS4ERR_DELAY)
-                       nfs4_handle_exception(server, err, &exception);
+               if (err != -NFS4ERR_DELAY)
+                       break;
+               nfs4_handle_exception(server, err, &exception);
        } while (exception.retry);
        return err;
 }
@@ -1031,12 +1143,11 @@ static inline void nfs4_exclusive_attrset(struct nfs4_opendata *opendata, struct
 /*
  * Returns a referenced nfs4_state
  */
-static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
+static int _nfs4_do_open(struct inode *dir, struct path *path, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred, struct nfs4_state **res)
 {
        struct nfs4_state_owner  *sp;
        struct nfs4_state     *state = NULL;
        struct nfs_server       *server = NFS_SERVER(dir);
-       struct nfs_client *clp = server->nfs_client;
        struct nfs4_opendata *opendata;
        int status;
 
@@ -1050,12 +1161,11 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct
        if (status != 0)
                goto err_put_state_owner;
        if (path->dentry->d_inode != NULL)
-               nfs4_return_incompatible_delegation(path->dentry->d_inode, flags & (FMODE_READ|FMODE_WRITE));
-       down_read(&clp->cl_sem);
+               nfs4_return_incompatible_delegation(path->dentry->d_inode, fmode);
        status = -ENOMEM;
-       opendata = nfs4_opendata_alloc(path, sp, flags, sattr);
+       opendata = nfs4_opendata_alloc(path, sp, fmode, flags, sattr);
        if (opendata == NULL)
-               goto err_release_rwsem;
+               goto err_put_state_owner;
 
        if (path->dentry->d_inode != NULL)
                opendata->state = nfs4_get_open_state(path->dentry->d_inode, sp);
@@ -1073,13 +1183,10 @@ static int _nfs4_do_open(struct inode *dir, struct path *path, int flags, struct
                goto err_opendata_put;
        nfs4_opendata_put(opendata);
        nfs4_put_state_owner(sp);
-       up_read(&clp->cl_sem);
        *res = state;
        return 0;
 err_opendata_put:
        nfs4_opendata_put(opendata);
-err_release_rwsem:
-       up_read(&clp->cl_sem);
 err_put_state_owner:
        nfs4_put_state_owner(sp);
 out_err:
@@ -1088,14 +1195,14 @@ out_err:
 }
 
 
-static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, int flags, struct iattr *sattr, struct rpc_cred *cred)
+static struct nfs4_state *nfs4_do_open(struct inode *dir, struct path *path, fmode_t fmode, int flags, struct iattr *sattr, struct rpc_cred *cred)
 {
        struct nfs4_exception exception = { };
        struct nfs4_state *res;
        int status;
 
        do {
-               status = _nfs4_do_open(dir, path, flags, sattr, cred, &res);
+               status = _nfs4_do_open(dir, path, fmode, flags, sattr, cred, &res);
                if (status == 0)
                        break;
                /* NOTE: BAD_SEQID means the server and client disagree about the
@@ -1230,10 +1337,13 @@ static void nfs4_close_done(struct rpc_task *task, void *data)
                        renew_lease(server, calldata->timestamp);
                        break;
                case -NFS4ERR_STALE_STATEID:
+               case -NFS4ERR_OLD_STATEID:
+               case -NFS4ERR_BAD_STATEID:
                case -NFS4ERR_EXPIRED:
-                       break;
+                       if (calldata->arg.fmode == 0)
+                               break;
                default:
-                       if (nfs4_async_handle_error(task, server) == -EAGAIN) {
+                       if (nfs4_async_handle_error(task, server, state) == -EAGAIN) {
                                rpc_restart_call(task);
                                return;
                        }
@@ -1272,10 +1382,10 @@ static void nfs4_close_prepare(struct rpc_task *task, void *data)
        nfs_fattr_init(calldata->res.fattr);
        if (test_bit(NFS_O_RDONLY_STATE, &state->flags) != 0) {
                task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
-               calldata->arg.open_flags = FMODE_READ;
+               calldata->arg.fmode = FMODE_READ;
        } else if (test_bit(NFS_O_WRONLY_STATE, &state->flags) != 0) {
                task->tk_msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_DOWNGRADE];
-               calldata->arg.open_flags = FMODE_WRITE;
+               calldata->arg.fmode = FMODE_WRITE;
        }
        calldata->timestamp = jiffies;
        rpc_call_start(task);
@@ -1328,6 +1438,7 @@ int nfs4_do_close(struct path *path, struct nfs4_state *state, int wait)
        calldata->arg.seqid = nfs_alloc_seqid(&state->owner->so_seqid);
        if (calldata->arg.seqid == NULL)
                goto out_free_calldata;
+       calldata->arg.fmode = 0;
        calldata->arg.bitmask = server->attr_bitmask;
        calldata->res.fattr = &calldata->fattr;
        calldata->res.seqid = calldata->arg.seqid;
@@ -1354,13 +1465,13 @@ out:
        return status;
 }
 
-static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct nfs4_state *state)
+static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct nfs4_state *state, fmode_t fmode)
 {
        struct file *filp;
        int ret;
 
        /* If the open_intent is for execute, we have an extra check to make */
-       if (nd->intent.open.flags & FMODE_EXEC) {
+       if (fmode & FMODE_EXEC) {
                ret = nfs_may_open(state->inode,
                                state->owner->so_cred,
                                nd->intent.open.flags);
@@ -1376,7 +1487,7 @@ static int nfs4_intent_set_file(struct nameidata *nd, struct path *path, struct
        }
        ret = PTR_ERR(filp);
 out_close:
-       nfs4_close_sync(path, state, nd->intent.open.flags);
+       nfs4_close_sync(path, state, fmode & (FMODE_READ|FMODE_WRITE));
        return ret;
 }
 
@@ -1392,6 +1503,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
        struct rpc_cred *cred;
        struct nfs4_state *state;
        struct dentry *res;
+       fmode_t fmode = nd->intent.open.flags & (FMODE_READ | FMODE_WRITE | FMODE_EXEC);
 
        if (nd->flags & LOOKUP_CREATE) {
                attr.ia_mode = nd->intent.open.create_mode;
@@ -1409,7 +1521,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
        parent = dentry->d_parent;
        /* Protect against concurrent sillydeletes */
        nfs_block_sillyrename(parent);
-       state = nfs4_do_open(dir, &path, nd->intent.open.flags, &attr, cred);
+       state = nfs4_do_open(dir, &path, fmode, nd->intent.open.flags, &attr, cred);
        put_rpccred(cred);
        if (IS_ERR(state)) {
                if (PTR_ERR(state) == -ENOENT) {
@@ -1424,7 +1536,7 @@ nfs4_atomic_open(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
                path.dentry = res;
        nfs_set_verifier(path.dentry, nfs_save_change_attribute(dir));
        nfs_unblock_sillyrename(parent);
-       nfs4_intent_set_file(nd, &path, state);
+       nfs4_intent_set_file(nd, &path, state, fmode);
        return res;
 }
 
@@ -1437,11 +1549,12 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st
        };
        struct rpc_cred *cred;
        struct nfs4_state *state;
+       fmode_t fmode = openflags & (FMODE_READ | FMODE_WRITE);
 
        cred = rpc_lookup_cred();
        if (IS_ERR(cred))
                return PTR_ERR(cred);
-       state = nfs4_do_open(dir, &path, openflags, NULL, cred);
+       state = nfs4_do_open(dir, &path, fmode, openflags, NULL, cred);
        put_rpccred(cred);
        if (IS_ERR(state)) {
                switch (PTR_ERR(state)) {
@@ -1458,10 +1571,10 @@ nfs4_open_revalidate(struct inode *dir, struct dentry *dentry, int openflags, st
        }
        if (state->inode == dentry->d_inode) {
                nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
-               nfs4_intent_set_file(nd, &path, state);
+               nfs4_intent_set_file(nd, &path, state, fmode);
                return 1;
        }
-       nfs4_close_sync(&path, state, openflags);
+       nfs4_close_sync(&path, state, fmode);
 out_drop:
        d_drop(dentry);
        return 0;
@@ -1887,6 +2000,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
        };
        struct nfs4_state *state;
        struct rpc_cred *cred;
+       fmode_t fmode = flags & (FMODE_READ | FMODE_WRITE);
        int status = 0;
 
        cred = rpc_lookup_cred();
@@ -1894,7 +2008,7 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
                status = PTR_ERR(cred);
                goto out;
        }
-       state = nfs4_do_open(dir, &path, flags, sattr, cred);
+       state = nfs4_do_open(dir, &path, fmode, flags, sattr, cred);
        d_drop(dentry);
        if (IS_ERR(state)) {
                status = PTR_ERR(state);
@@ -1910,9 +2024,9 @@ nfs4_proc_create(struct inode *dir, struct dentry *dentry, struct iattr *sattr,
                nfs_post_op_update_inode(state->inode, &fattr);
        }
        if (status == 0 && (nd->flags & LOOKUP_OPEN) != 0)
-               status = nfs4_intent_set_file(nd, &path, state);
+               status = nfs4_intent_set_file(nd, &path, state, fmode);
        else
-               nfs4_close_sync(&path, state, flags);
+               nfs4_close_sync(&path, state, fmode);
 out_putcred:
        put_rpccred(cred);
 out:
@@ -1974,7 +2088,7 @@ static int nfs4_proc_unlink_done(struct rpc_task *task, struct inode *dir)
 {
        struct nfs_removeres *res = task->tk_msg.rpc_resp;
 
-       if (nfs4_async_handle_error(task, res->server) == -EAGAIN)
+       if (nfs4_async_handle_error(task, res->server, NULL) == -EAGAIN)
                return 0;
        update_changeattr(dir, &res->cinfo);
        nfs_post_op_update_inode(dir, &res->dir_attr);
@@ -2402,7 +2516,7 @@ static int nfs4_read_done(struct rpc_task *task, struct nfs_read_data *data)
 {
        struct nfs_server *server = NFS_SERVER(data->inode);
 
-       if (nfs4_async_handle_error(task, server) == -EAGAIN) {
+       if (nfs4_async_handle_error(task, server, data->args.context->state) == -EAGAIN) {
                rpc_restart_call(task);
                return -EAGAIN;
        }
@@ -2423,7 +2537,7 @@ static int nfs4_write_done(struct rpc_task *task, struct nfs_write_data *data)
 {
        struct inode *inode = data->inode;
        
-       if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) {
+       if (nfs4_async_handle_error(task, NFS_SERVER(inode), data->args.context->state) == -EAGAIN) {
                rpc_restart_call(task);
                return -EAGAIN;
        }
@@ -2449,7 +2563,7 @@ static int nfs4_commit_done(struct rpc_task *task, struct nfs_write_data *data)
 {
        struct inode *inode = data->inode;
        
-       if (nfs4_async_handle_error(task, NFS_SERVER(inode)) == -EAGAIN) {
+       if (nfs4_async_handle_error(task, NFS_SERVER(inode), NULL) == -EAGAIN) {
                rpc_restart_call(task);
                return -EAGAIN;
        }
@@ -2742,19 +2856,25 @@ static int nfs4_proc_set_acl(struct inode *inode, const void *buf, size_t buflen
 }
 
 static int
-nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server)
+nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server, struct nfs4_state *state)
 {
        struct nfs_client *clp = server->nfs_client;
 
        if (!clp || task->tk_status >= 0)
                return 0;
        switch(task->tk_status) {
+               case -NFS4ERR_ADMIN_REVOKED:
+               case -NFS4ERR_BAD_STATEID:
+               case -NFS4ERR_OPENMODE:
+                       if (state == NULL)
+                               break;
+                       nfs4_state_mark_reclaim_nograce(clp, state);
                case -NFS4ERR_STALE_CLIENTID:
                case -NFS4ERR_STALE_STATEID:
                case -NFS4ERR_EXPIRED:
                        rpc_sleep_on(&clp->cl_rpcwaitq, task, NULL);
                        nfs4_schedule_state_recovery(clp);
-                       if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) == 0)
+                       if (test_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) == 0)
                                rpc_wake_up_queued_task(&clp->cl_rpcwaitq, task);
                        task->tk_status = 0;
                        return -EAGAIN;
@@ -2772,79 +2892,6 @@ nfs4_async_handle_error(struct rpc_task *task, const struct nfs_server *server)
        return 0;
 }
 
-static int nfs4_wait_bit_killable(void *word)
-{
-       if (fatal_signal_pending(current))
-               return -ERESTARTSYS;
-       schedule();
-       return 0;
-}
-
-static int nfs4_wait_clnt_recover(struct rpc_clnt *clnt, struct nfs_client *clp)
-{
-       int res;
-
-       might_sleep();
-
-       rwsem_acquire(&clp->cl_sem.dep_map, 0, 0, _RET_IP_);
-
-       res = wait_on_bit(&clp->cl_state, NFS4CLNT_STATE_RECOVER,
-                       nfs4_wait_bit_killable, TASK_KILLABLE);
-
-       rwsem_release(&clp->cl_sem.dep_map, 1, _RET_IP_);
-       return res;
-}
-
-static int nfs4_delay(struct rpc_clnt *clnt, long *timeout)
-{
-       int res = 0;
-
-       might_sleep();
-
-       if (*timeout <= 0)
-               *timeout = NFS4_POLL_RETRY_MIN;
-       if (*timeout > NFS4_POLL_RETRY_MAX)
-               *timeout = NFS4_POLL_RETRY_MAX;
-       schedule_timeout_killable(*timeout);
-       if (fatal_signal_pending(current))
-               res = -ERESTARTSYS;
-       *timeout <<= 1;
-       return res;
-}
-
-/* This is the error handling routine for processes that are allowed
- * to sleep.
- */
-static int nfs4_handle_exception(const struct nfs_server *server, int errorcode, struct nfs4_exception *exception)
-{
-       struct nfs_client *clp = server->nfs_client;
-       int ret = errorcode;
-
-       exception->retry = 0;
-       switch(errorcode) {
-               case 0:
-                       return 0;
-               case -NFS4ERR_STALE_CLIENTID:
-               case -NFS4ERR_STALE_STATEID:
-               case -NFS4ERR_EXPIRED:
-                       nfs4_schedule_state_recovery(clp);
-                       ret = nfs4_wait_clnt_recover(server->client, clp);
-                       if (ret == 0)
-                               exception->retry = 1;
-                       break;
-               case -NFS4ERR_FILE_OPEN:
-               case -NFS4ERR_GRACE:
-               case -NFS4ERR_DELAY:
-                       ret = nfs4_delay(server->client, &exception->timeout);
-                       if (ret != 0)
-                               break;
-               case -NFS4ERR_OLD_STATEID:
-                       exception->retry = 1;
-       }
-       /* We failed to handle the error */
-       return nfs4_map_errors(ret);
-}
-
 int nfs4_proc_setclientid(struct nfs_client *clp, u32 program, unsigned short port, struct rpc_cred *cred)
 {
        nfs4_verifier sc_verifier;
@@ -2916,7 +2963,6 @@ static int _nfs4_proc_setclientid_confirm(struct nfs_client *clp, struct rpc_cre
                spin_lock(&clp->cl_lock);
                clp->cl_lease_time = fsinfo.lease_time * HZ;
                clp->cl_last_renewal = now;
-               clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
                spin_unlock(&clp->cl_lock);
        }
        return status;
@@ -3074,7 +3120,6 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
        struct nfs4_lock_state *lsp;
        int status;
 
-       down_read(&clp->cl_sem);
        arg.lock_owner.clientid = clp->cl_clientid;
        status = nfs4_set_lock_state(state, request);
        if (status != 0)
@@ -3091,7 +3136,6 @@ static int _nfs4_proc_getlk(struct nfs4_state *state, int cmd, struct file_lock
        }
        request->fl_ops->fl_release_private(request);
 out:
-       up_read(&clp->cl_sem);
        return status;
 }
 
@@ -3181,11 +3225,13 @@ static void nfs4_locku_done(struct rpc_task *task, void *data)
                                        sizeof(calldata->lsp->ls_stateid.data));
                        renew_lease(calldata->server, calldata->timestamp);
                        break;
+               case -NFS4ERR_BAD_STATEID:
+               case -NFS4ERR_OLD_STATEID:
                case -NFS4ERR_STALE_STATEID:
                case -NFS4ERR_EXPIRED:
                        break;
                default:
-                       if (nfs4_async_handle_error(task, calldata->server) == -EAGAIN)
+                       if (nfs4_async_handle_error(task, calldata->server, NULL) == -EAGAIN)
                                rpc_restart_call(task);
        }
 }
@@ -3248,6 +3294,7 @@ static struct rpc_task *nfs4_do_unlck(struct file_lock *fl,
 
 static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *request)
 {
+       struct nfs_inode *nfsi = NFS_I(state->inode);
        struct nfs_seqid *seqid;
        struct nfs4_lock_state *lsp;
        struct rpc_task *task;
@@ -3257,8 +3304,12 @@ static int nfs4_proc_unlck(struct nfs4_state *state, int cmd, struct file_lock *
        status = nfs4_set_lock_state(state, request);
        /* Unlock _before_ we do the RPC call */
        request->fl_flags |= FL_EXISTS;
-       if (do_vfs_lock(request->fl_file, request) == -ENOENT)
+       down_read(&nfsi->rwsem);
+       if (do_vfs_lock(request->fl_file, request) == -ENOENT) {
+               up_read(&nfsi->rwsem);
                goto out;
+       }
+       up_read(&nfsi->rwsem);
        if (status != 0)
                goto out;
        /* Is this a delegated lock? */
@@ -3484,7 +3535,7 @@ static int nfs4_lock_expired(struct nfs4_state *state, struct file_lock *request
 
 static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock *request)
 {
-       struct nfs_client *clp = state->owner->so_client;
+       struct nfs_inode *nfsi = NFS_I(state->inode);
        unsigned char fl_flags = request->fl_flags;
        int status;
 
@@ -3496,19 +3547,13 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
        status = do_vfs_lock(request->fl_file, request);
        if (status < 0)
                goto out;
-       down_read(&clp->cl_sem);
+       down_read(&nfsi->rwsem);
        if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
-               struct nfs_inode *nfsi = NFS_I(state->inode);
                /* Yes: cache locks! */
-               down_read(&nfsi->rwsem);
                /* ...but avoid races with delegation recall... */
-               if (test_bit(NFS_DELEGATED_STATE, &state->flags)) {
-                       request->fl_flags = fl_flags & ~FL_SLEEP;
-                       status = do_vfs_lock(request->fl_file, request);
-                       up_read(&nfsi->rwsem);
-                       goto out_unlock;
-               }
-               up_read(&nfsi->rwsem);
+               request->fl_flags = fl_flags & ~FL_SLEEP;
+               status = do_vfs_lock(request->fl_file, request);
+               goto out_unlock;
        }
        status = _nfs4_do_setlk(state, cmd, request, 0);
        if (status != 0)
@@ -3518,7 +3563,7 @@ static int _nfs4_proc_setlk(struct nfs4_state *state, int cmd, struct file_lock
        if (do_vfs_lock(request->fl_file, request) < 0)
                printk(KERN_WARNING "%s: VFS is out of sync with lock manager!\n", __func__);
 out_unlock:
-       up_read(&clp->cl_sem);
+       up_read(&nfsi->rwsem);
 out:
        request->fl_flags = fl_flags;
        return status;
@@ -3664,11 +3709,15 @@ int nfs4_proc_fs_locations(struct inode *dir, const struct qstr *name,
 }
 
 struct nfs4_state_recovery_ops nfs4_reboot_recovery_ops = {
+       .owner_flag_bit = NFS_OWNER_RECLAIM_REBOOT,
+       .state_flag_bit = NFS_STATE_RECLAIM_REBOOT,
        .recover_open   = nfs4_open_reclaim,
        .recover_lock   = nfs4_lock_reclaim,
 };
 
-struct nfs4_state_recovery_ops nfs4_network_partition_recovery_ops = {
+struct nfs4_state_recovery_ops nfs4_nograce_recovery_ops = {
+       .owner_flag_bit = NFS_OWNER_RECLAIM_NOGRACE,
+       .state_flag_bit = NFS_STATE_RECLAIM_NOGRACE,
        .recover_open   = nfs4_open_expired,
        .recover_lock   = nfs4_lock_expired,
 };
index 3305acbbe2ae3b7f4258baed8f5f807fbf7a1805..f524e932ff7b295f8bf0adce947f058f2d8ede73 100644 (file)
@@ -65,7 +65,6 @@ nfs4_renew_state(struct work_struct *work)
        long lease, timeout;
        unsigned long last, now;
 
-       down_read(&clp->cl_sem);
        dprintk("%s: start\n", __func__);
        /* Are there any active superblocks? */
        if (list_empty(&clp->cl_superblocks))
@@ -77,17 +76,19 @@ nfs4_renew_state(struct work_struct *work)
        timeout = (2 * lease) / 3 + (long)last - (long)now;
        /* Are we close to a lease timeout? */
        if (time_after(now, last + lease/3)) {
-               cred = nfs4_get_renew_cred(clp);
+               cred = nfs4_get_renew_cred_locked(clp);
+               spin_unlock(&clp->cl_lock);
                if (cred == NULL) {
-                       set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
-                       spin_unlock(&clp->cl_lock);
+                       if (list_empty(&clp->cl_delegations)) {
+                               set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+                               goto out;
+                       }
                        nfs_expire_all_delegations(clp);
-                       goto out;
+               } else {
+                       /* Queue an asynchronous RENEW. */
+                       nfs4_proc_async_renew(clp, cred);
+                       put_rpccred(cred);
                }
-               spin_unlock(&clp->cl_lock);
-               /* Queue an asynchronous RENEW. */
-               nfs4_proc_async_renew(clp, cred);
-               put_rpccred(cred);
                timeout = (2 * lease) / 3;
                spin_lock(&clp->cl_lock);
        } else
@@ -100,12 +101,11 @@ nfs4_renew_state(struct work_struct *work)
        cancel_delayed_work(&clp->cl_renewd);
        schedule_delayed_work(&clp->cl_renewd, timeout);
        spin_unlock(&clp->cl_lock);
+       nfs_expire_unreferenced_delegations(clp);
 out:
-       up_read(&clp->cl_sem);
        dprintk("%s: done\n", __func__);
 }
 
-/* Must be called with clp->cl_sem locked for writes */
 void
 nfs4_schedule_state_renewal(struct nfs_client *clp)
 {
index 401ef8b28f979231cc291ad016586abd353eff42..2022fe47966f52d641496fe633ad0d0b98b15fc7 100644 (file)
@@ -71,14 +71,12 @@ static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred)
        return status;
 }
 
-static struct rpc_cred *nfs4_get_machine_cred(struct nfs_client *clp)
+static struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp)
 {
        struct rpc_cred *cred = NULL;
 
-       spin_lock(&clp->cl_lock);
        if (clp->cl_machine_cred != NULL)
                cred = get_rpccred(clp->cl_machine_cred);
-       spin_unlock(&clp->cl_lock);
        return cred;
 }
 
@@ -94,7 +92,7 @@ static void nfs4_clear_machine_cred(struct nfs_client *clp)
                put_rpccred(cred);
 }
 
-struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp)
+struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp)
 {
        struct nfs4_state_owner *sp;
        struct rb_node *pos;
@@ -110,13 +108,24 @@ struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp)
        return cred;
 }
 
+static struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp)
+{
+       struct rpc_cred *cred;
+
+       spin_lock(&clp->cl_lock);
+       cred = nfs4_get_renew_cred_locked(clp);
+       spin_unlock(&clp->cl_lock);
+       return cred;
+}
+
 static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp)
 {
        struct nfs4_state_owner *sp;
        struct rb_node *pos;
        struct rpc_cred *cred;
 
-       cred = nfs4_get_machine_cred(clp);
+       spin_lock(&clp->cl_lock);
+       cred = nfs4_get_machine_cred_locked(clp);
        if (cred != NULL)
                goto out;
        pos = rb_first(&clp->cl_state_owners);
@@ -125,6 +134,7 @@ static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp)
                cred = get_rpccred(sp->so_cred);
        }
 out:
+       spin_unlock(&clp->cl_lock);
        return cred;
 }
 
@@ -295,10 +305,6 @@ nfs4_drop_state_owner(struct nfs4_state_owner *sp)
        }
 }
 
-/*
- * Note: must be called with clp->cl_sem held in order to prevent races
- *       with reboot recovery!
- */
 struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred)
 {
        struct nfs_client *clp = server->nfs_client;
@@ -327,10 +333,6 @@ struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct
        return sp;
 }
 
-/*
- * Must be called with clp->cl_sem held in order to avoid races
- * with state recovery...
- */
 void nfs4_put_state_owner(struct nfs4_state_owner *sp)
 {
        struct nfs_client *clp = sp->so_client;
@@ -361,18 +363,18 @@ nfs4_alloc_open_state(void)
 }
 
 void
-nfs4_state_set_mode_locked(struct nfs4_state *state, mode_t mode)
+nfs4_state_set_mode_locked(struct nfs4_state *state, fmode_t fmode)
 {
-       if (state->state == mode)
+       if (state->state == fmode)
                return;
        /* NB! List reordering - see the reclaim code for why.  */
-       if ((mode & FMODE_WRITE) != (state->state & FMODE_WRITE)) {
-               if (mode & FMODE_WRITE)
+       if ((fmode & FMODE_WRITE) != (state->state & FMODE_WRITE)) {
+               if (fmode & FMODE_WRITE)
                        list_move(&state->open_states, &state->owner->so_states);
                else
                        list_move_tail(&state->open_states, &state->owner->so_states);
        }
-       state->state = mode;
+       state->state = fmode;
 }
 
 static struct nfs4_state *
@@ -432,10 +434,6 @@ out:
        return state;
 }
 
-/*
- * Beware! Caller must be holding exactly one
- * reference to clp->cl_sem!
- */
 void nfs4_put_open_state(struct nfs4_state *state)
 {
        struct inode *inode = state->inode;
@@ -456,16 +454,16 @@ void nfs4_put_open_state(struct nfs4_state *state)
 /*
  * Close the current file.
  */
-static void __nfs4_close(struct path *path, struct nfs4_state *state, mode_t mode, int wait)
+static void __nfs4_close(struct path *path, struct nfs4_state *state, fmode_t fmode, int wait)
 {
        struct nfs4_state_owner *owner = state->owner;
        int call_close = 0;
-       int newstate;
+       fmode_t newstate;
 
        atomic_inc(&owner->so_count);
        /* Protect against nfs4_find_state() */
        spin_lock(&owner->so_lock);
-       switch (mode & (FMODE_READ | FMODE_WRITE)) {
+       switch (fmode & (FMODE_READ | FMODE_WRITE)) {
                case FMODE_READ:
                        state->n_rdonly--;
                        break;
@@ -500,14 +498,14 @@ static void __nfs4_close(struct path *path, struct nfs4_state *state, mode_t mod
                nfs4_do_close(path, state, wait);
 }
 
-void nfs4_close_state(struct path *path, struct nfs4_state *state, mode_t mode)
+void nfs4_close_state(struct path *path, struct nfs4_state *state, fmode_t fmode)
 {
-       __nfs4_close(path, state, mode, 0);
+       __nfs4_close(path, state, fmode, 0);
 }
 
-void nfs4_close_sync(struct path *path, struct nfs4_state *state, mode_t mode)
+void nfs4_close_sync(struct path *path, struct nfs4_state *state, fmode_t fmode)
 {
-       __nfs4_close(path, state, mode, 1);
+       __nfs4_close(path, state, fmode, 1);
 }
 
 /*
@@ -568,7 +566,6 @@ static void nfs4_free_lock_state(struct nfs4_lock_state *lsp)
  * Return a compatible lock_state. If no initialized lock_state structure
  * exists, return an uninitialized one.
  *
- * The caller must be holding clp->cl_sem
  */
 static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner)
 {
@@ -770,32 +767,34 @@ unlock:
        return status;
 }
 
-static int reclaimer(void *);
+static int nfs4_run_state_manager(void *);
 
-static inline void nfs4_clear_recover_bit(struct nfs_client *clp)
+static void nfs4_clear_state_manager_bit(struct nfs_client *clp)
 {
        smp_mb__before_clear_bit();
-       clear_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state);
+       clear_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state);
        smp_mb__after_clear_bit();
-       wake_up_bit(&clp->cl_state, NFS4CLNT_STATE_RECOVER);
+       wake_up_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING);
        rpc_wake_up(&clp->cl_rpcwaitq);
 }
 
 /*
- * State recovery routine
+ * Schedule the nfs_client asynchronous state management routine
  */
-static void nfs4_recover_state(struct nfs_client *clp)
+void nfs4_schedule_state_manager(struct nfs_client *clp)
 {
        struct task_struct *task;
 
+       if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
+               return;
        __module_get(THIS_MODULE);
        atomic_inc(&clp->cl_count);
-       task = kthread_run(reclaimer, clp, "%s-reclaim",
+       task = kthread_run(nfs4_run_state_manager, clp, "%s-manager",
                                rpc_peeraddr2str(clp->cl_rpcclient,
                                                        RPC_DISPLAY_ADDR));
        if (!IS_ERR(task))
                return;
-       nfs4_clear_recover_bit(clp);
+       nfs4_clear_state_manager_bit(clp);
        nfs_put_client(clp);
        module_put(THIS_MODULE);
 }
@@ -807,16 +806,42 @@ void nfs4_schedule_state_recovery(struct nfs_client *clp)
 {
        if (!clp)
                return;
-       if (test_and_set_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) == 0)
-               nfs4_recover_state(clp);
+       if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
+               set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
+       nfs4_schedule_state_manager(clp);
 }
 
-static int nfs4_reclaim_locks(struct nfs4_state_recovery_ops *ops, struct nfs4_state *state)
+static int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state)
+{
+
+       set_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
+       /* Don't recover state that expired before the reboot */
+       if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags)) {
+               clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
+               return 0;
+       }
+       set_bit(NFS_OWNER_RECLAIM_REBOOT, &state->owner->so_flags);
+       set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state);
+       return 1;
+}
+
+int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state)
+{
+       set_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags);
+       clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags);
+       set_bit(NFS_OWNER_RECLAIM_NOGRACE, &state->owner->so_flags);
+       set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
+       return 1;
+}
+
+static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops)
 {
        struct inode *inode = state->inode;
+       struct nfs_inode *nfsi = NFS_I(inode);
        struct file_lock *fl;
        int status = 0;
 
+       down_write(&nfsi->rwsem);
        for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) {
                if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK)))
                        continue;
@@ -839,12 +864,14 @@ static int nfs4_reclaim_locks(struct nfs4_state_recovery_ops *ops, struct nfs4_s
                                goto out_err;
                }
        }
+       up_write(&nfsi->rwsem);
        return 0;
 out_err:
+       up_write(&nfsi->rwsem);
        return status;
 }
 
-static int nfs4_reclaim_open_state(struct nfs4_state_recovery_ops *ops, struct nfs4_state_owner *sp)
+static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs4_state_recovery_ops *ops)
 {
        struct nfs4_state *state;
        struct nfs4_lock_state *lock;
@@ -858,28 +885,34 @@ static int nfs4_reclaim_open_state(struct nfs4_state_recovery_ops *ops, struct n
         * recovering after a network partition or a reboot from a
         * server that doesn't support a grace period.
         */
+restart:
+       spin_lock(&sp->so_lock);
        list_for_each_entry(state, &sp->so_states, open_states) {
+               if (!test_and_clear_bit(ops->state_flag_bit, &state->flags))
+                       continue;
                if (state->state == 0)
                        continue;
+               atomic_inc(&state->count);
+               spin_unlock(&sp->so_lock);
                status = ops->recover_open(sp, state);
                if (status >= 0) {
-                       status = nfs4_reclaim_locks(ops, state);
-                       if (status < 0)
-                               goto out_err;
-                       list_for_each_entry(lock, &state->lock_states, ls_locks) {
-                               if (!(lock->ls_flags & NFS_LOCK_INITIALIZED))
-                                       printk("%s: Lock reclaim failed!\n",
+                       status = nfs4_reclaim_locks(state, ops);
+                       if (status >= 0) {
+                               list_for_each_entry(lock, &state->lock_states, ls_locks) {
+                                       if (!(lock->ls_flags & NFS_LOCK_INITIALIZED))
+                                               printk("%s: Lock reclaim failed!\n",
                                                        __func__);
+                               }
+                               nfs4_put_open_state(state);
+                               goto restart;
                        }
-                       continue;
                }
                switch (status) {
                        default:
                                printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n",
                                                __func__, status);
                        case -ENOENT:
-                       case -NFS4ERR_RECLAIM_BAD:
-                       case -NFS4ERR_RECLAIM_CONFLICT:
+                       case -ESTALE:
                                /*
                                 * Open state on this file cannot be recovered
                                 * All we can do is revert to using the zero stateid.
@@ -889,84 +922,176 @@ static int nfs4_reclaim_open_state(struct nfs4_state_recovery_ops *ops, struct n
                                /* Mark the file as being 'closed' */
                                state->state = 0;
                                break;
+                       case -NFS4ERR_RECLAIM_BAD:
+                       case -NFS4ERR_RECLAIM_CONFLICT:
+                               nfs4_state_mark_reclaim_nograce(sp->so_client, state);
+                               break;
                        case -NFS4ERR_EXPIRED:
                        case -NFS4ERR_NO_GRACE:
+                               nfs4_state_mark_reclaim_nograce(sp->so_client, state);
                        case -NFS4ERR_STALE_CLIENTID:
                                goto out_err;
                }
+               nfs4_put_open_state(state);
+               goto restart;
        }
+       spin_unlock(&sp->so_lock);
        return 0;
 out_err:
+       nfs4_put_open_state(state);
        return status;
 }
 
-static void nfs4_state_mark_reclaim(struct nfs_client *clp)
+static void nfs4_clear_open_state(struct nfs4_state *state)
+{
+       struct nfs4_lock_state *lock;
+
+       clear_bit(NFS_DELEGATED_STATE, &state->flags);
+       clear_bit(NFS_O_RDONLY_STATE, &state->flags);
+       clear_bit(NFS_O_WRONLY_STATE, &state->flags);
+       clear_bit(NFS_O_RDWR_STATE, &state->flags);
+       list_for_each_entry(lock, &state->lock_states, ls_locks) {
+               lock->ls_seqid.flags = 0;
+               lock->ls_flags &= ~NFS_LOCK_INITIALIZED;
+       }
+}
+
+static void nfs4_state_mark_reclaim_helper(struct nfs_client *clp, int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state))
 {
        struct nfs4_state_owner *sp;
        struct rb_node *pos;
        struct nfs4_state *state;
-       struct nfs4_lock_state *lock;
 
        /* Reset all sequence ids to zero */
        for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
                sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
-               sp->so_seqid.counter = 0;
                sp->so_seqid.flags = 0;
                spin_lock(&sp->so_lock);
                list_for_each_entry(state, &sp->so_states, open_states) {
-                       clear_bit(NFS_DELEGATED_STATE, &state->flags);
-                       clear_bit(NFS_O_RDONLY_STATE, &state->flags);
-                       clear_bit(NFS_O_WRONLY_STATE, &state->flags);
-                       clear_bit(NFS_O_RDWR_STATE, &state->flags);
-                       list_for_each_entry(lock, &state->lock_states, ls_locks) {
-                               lock->ls_seqid.counter = 0;
-                               lock->ls_seqid.flags = 0;
-                               lock->ls_flags &= ~NFS_LOCK_INITIALIZED;
-                       }
+                       if (mark_reclaim(clp, state))
+                               nfs4_clear_open_state(state);
                }
                spin_unlock(&sp->so_lock);
        }
 }
 
-static int reclaimer(void *ptr)
+static void nfs4_state_start_reclaim_reboot(struct nfs_client *clp)
+{
+       /* Mark all delegations for reclaim */
+       nfs_delegation_mark_reclaim(clp);
+       nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_reboot);
+}
+
+static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp)
 {
-       struct nfs_client *clp = ptr;
        struct nfs4_state_owner *sp;
        struct rb_node *pos;
-       struct nfs4_state_recovery_ops *ops;
-       struct rpc_cred *cred;
+       struct nfs4_state *state;
+
+       if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
+               return;
+
+       for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
+               sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
+               spin_lock(&sp->so_lock);
+               list_for_each_entry(state, &sp->so_states, open_states) {
+                       if (!test_and_clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags))
+                               continue;
+                       nfs4_state_mark_reclaim_nograce(clp, state);
+               }
+               spin_unlock(&sp->so_lock);
+       }
+
+       nfs_delegation_reap_unclaimed(clp);
+}
+
+static void nfs_delegation_clear_all(struct nfs_client *clp)
+{
+       nfs_delegation_mark_reclaim(clp);
+       nfs_delegation_reap_unclaimed(clp);
+}
+
+static void nfs4_state_start_reclaim_nograce(struct nfs_client *clp)
+{
+       nfs_delegation_clear_all(clp);
+       nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce);
+}
+
+static void nfs4_state_end_reclaim_nograce(struct nfs_client *clp)
+{
+       clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
+}
+
+static void nfs4_recovery_handle_error(struct nfs_client *clp, int error)
+{
+       switch (error) {
+               case -NFS4ERR_CB_PATH_DOWN:
+                       nfs_handle_cb_pathdown(clp);
+                       break;
+               case -NFS4ERR_STALE_CLIENTID:
+               case -NFS4ERR_LEASE_MOVED:
+                       set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+                       nfs4_state_start_reclaim_reboot(clp);
+                       break;
+               case -NFS4ERR_EXPIRED:
+                       set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+                       nfs4_state_start_reclaim_nograce(clp);
+       }
+}
+
+static int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recovery_ops *ops)
+{
+       struct rb_node *pos;
        int status = 0;
 
-       allow_signal(SIGKILL);
+restart:
+       spin_lock(&clp->cl_lock);
+       for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
+               struct nfs4_state_owner *sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
+               if (!test_and_clear_bit(ops->owner_flag_bit, &sp->so_flags))
+                       continue;
+               atomic_inc(&sp->so_count);
+               spin_unlock(&clp->cl_lock);
+               status = nfs4_reclaim_open_state(sp, ops);
+               if (status < 0) {
+                       set_bit(ops->owner_flag_bit, &sp->so_flags);
+                       nfs4_put_state_owner(sp);
+                       nfs4_recovery_handle_error(clp, status);
+                       return status;
+               }
+               nfs4_put_state_owner(sp);
+               goto restart;
+       }
+       spin_unlock(&clp->cl_lock);
+       return status;
+}
 
-       /* Ensure exclusive access to NFSv4 state */
-       down_write(&clp->cl_sem);
-       /* Are there any NFS mounts out there? */
-       if (list_empty(&clp->cl_superblocks))
-               goto out;
-restart_loop:
-       ops = &nfs4_network_partition_recovery_ops;
-       /* Are there any open files on this volume? */
+static int nfs4_check_lease(struct nfs_client *clp)
+{
+       struct rpc_cred *cred;
+       int status = -NFS4ERR_EXPIRED;
+
+       /* Is the client already known to have an expired lease? */
+       if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state))
+               return 0;
        cred = nfs4_get_renew_cred(clp);
-       if (cred != NULL) {
-               /* Yes there are: try to renew the old lease */
-               status = nfs4_proc_renew(clp, cred);
-               put_rpccred(cred);
-               switch (status) {
-                       case 0:
-                       case -NFS4ERR_CB_PATH_DOWN:
-                               goto out;
-                       case -NFS4ERR_STALE_CLIENTID:
-                       case -NFS4ERR_LEASE_MOVED:
-                               ops = &nfs4_reboot_recovery_ops;
-               }
-       } else {
-               /* "reboot" to ensure we clear all state on the server */
-               clp->cl_boot_time = CURRENT_TIME;
+       if (cred == NULL) {
+               cred = nfs4_get_setclientid_cred(clp);
+               if (cred == NULL)
+                       goto out;
        }
-       /* We're going to have to re-establish a clientid */
-       nfs4_state_mark_reclaim(clp);
-       status = -ENOENT;
+       status = nfs4_proc_renew(clp, cred);
+       put_rpccred(cred);
+out:
+       nfs4_recovery_handle_error(clp, status);
+       return status;
+}
+
+static int nfs4_reclaim_lease(struct nfs_client *clp)
+{
+       struct rpc_cred *cred;
+       int status = -ENOENT;
+
        cred = nfs4_get_setclientid_cred(clp);
        if (cred != NULL) {
                status = nfs4_init_client(clp, cred);
@@ -974,42 +1099,90 @@ restart_loop:
                /* Handle case where the user hasn't set up machine creds */
                if (status == -EACCES && cred == clp->cl_machine_cred) {
                        nfs4_clear_machine_cred(clp);
-                       goto restart_loop;
+                       status = -EAGAIN;
                }
        }
-       if (status)
-               goto out_error;
-       /* Mark all delegations for reclaim */
-       nfs_delegation_mark_reclaim(clp);
-       /* Note: list is protected by exclusive lock on cl->cl_sem */
-       for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) {
-               sp = rb_entry(pos, struct nfs4_state_owner, so_client_node);
-               status = nfs4_reclaim_open_state(ops, sp);
-               if (status < 0) {
-                       if (status == -NFS4ERR_NO_GRACE) {
-                               ops = &nfs4_network_partition_recovery_ops;
-                               status = nfs4_reclaim_open_state(ops, sp);
+       return status;
+}
+
+static void nfs4_state_manager(struct nfs_client *clp)
+{
+       int status = 0;
+
+       /* Ensure exclusive access to NFSv4 state */
+       for(;;) {
+               if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) {
+                       /* We're going to have to re-establish a clientid */
+                       status = nfs4_reclaim_lease(clp);
+                       if (status) {
+                               set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
+                               if (status == -EAGAIN)
+                                       continue;
+                               goto out_error;
                        }
+                       clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state);
+               }
+
+               if (test_and_clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state)) {
+                       status = nfs4_check_lease(clp);
+                       if (status != 0)
+                               continue;
+               }
+
+               /* First recover reboot state... */
+               if (test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) {
+                       status = nfs4_do_reclaim(clp, &nfs4_reboot_recovery_ops);
                        if (status == -NFS4ERR_STALE_CLIENTID)
-                               goto restart_loop;
-                       if (status == -NFS4ERR_EXPIRED)
-                               goto restart_loop;
+                               continue;
+                       nfs4_state_end_reclaim_reboot(clp);
+                       continue;
+               }
+
+               /* Now recover expired state... */
+               if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) {
+                       status = nfs4_do_reclaim(clp, &nfs4_nograce_recovery_ops);
+                       if (status < 0) {
+                               set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state);
+                               if (status == -NFS4ERR_STALE_CLIENTID)
+                                       continue;
+                               if (status == -NFS4ERR_EXPIRED)
+                                       continue;
+                               goto out_error;
+                       } else
+                               nfs4_state_end_reclaim_nograce(clp);
+                       continue;
                }
+
+               if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) {
+                       nfs_client_return_marked_delegations(clp);
+                       continue;
+               }
+
+               nfs4_clear_state_manager_bit(clp);
+               /* Did we race with an attempt to give us more work? */
+               if (clp->cl_state == 0)
+                       break;
+               if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0)
+                       break;
        }
-       nfs_delegation_reap_unclaimed(clp);
-out:
-       up_write(&clp->cl_sem);
-       if (status == -NFS4ERR_CB_PATH_DOWN)
-               nfs_handle_cb_pathdown(clp);
-       nfs4_clear_recover_bit(clp);
+       return;
+out_error:
+       printk(KERN_WARNING "Error: state manager failed on NFSv4 server %s"
+                       " with error %d\n", clp->cl_hostname, -status);
+       if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state))
+               nfs4_state_end_reclaim_reboot(clp);
+       nfs4_clear_state_manager_bit(clp);
+}
+
+static int nfs4_run_state_manager(void *ptr)
+{
+       struct nfs_client *clp = ptr;
+
+       allow_signal(SIGKILL);
+       nfs4_state_manager(clp);
        nfs_put_client(clp);
        module_put_and_exit(0);
        return 0;
-out_error:
-       printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %s"
-                       " with error %d\n", clp->cl_hostname, -status);
-       set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
-       goto out;
 }
 
 /*
index b916297d233461c84a7ce3d8b6a49ea52252dee0..d1e4c8f8a0a9e8b5106564cf71986579340efa77 100644 (file)
@@ -8,7 +8,7 @@
  *
  *  Kendrick Smith <kmsmith@umich.edu>
  *  Andy Adamson   <andros@umich.edu>
- * 
+ *
  *  Redistribution and use in source and binary forms, with or without
  *  modification, are permitted provided that the following conditions
  *  are met:
@@ -67,7 +67,7 @@ static int nfs4_stat_to_errno(int);
 #define NFS4_MAXTAGLEN         0
 #endif
 
-/* lock,open owner id: 
+/* lock,open owner id:
  * we currently use size 2 (u64) out of (NFS4_OPAQUE_LIMIT  >> 2)
  */
 #define open_owner_id_maxsz    (1 + 4)
@@ -541,6 +541,7 @@ static struct {
 struct compound_hdr {
        int32_t         status;
        uint32_t        nops;
+       __be32 *        nops_p;
        uint32_t        taglen;
        char *          tag;
 };
@@ -578,7 +579,7 @@ static void encode_string(struct xdr_stream *xdr, unsigned int len, const char *
        xdr_encode_opaque(p, str, len);
 }
 
-static int encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
+static void encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
 {
        __be32 *p;
 
@@ -588,8 +589,13 @@ static int encode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
        WRITE32(hdr->taglen);
        WRITEMEM(hdr->tag, hdr->taglen);
        WRITE32(NFS4_MINOR_VERSION);
+       hdr->nops_p = p;
        WRITE32(hdr->nops);
-       return 0;
+}
+
+static void encode_nops(struct compound_hdr *hdr)
+{
+       *hdr->nops_p = htonl(hdr->nops);
 }
 
 static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *verf)
@@ -601,7 +607,7 @@ static void encode_nfs4_verifier(struct xdr_stream *xdr, const nfs4_verifier *ve
        xdr_encode_opaque_fixed(p, verf->data, NFS4_VERIFIER_SIZE);
 }
 
-static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const struct nfs_server *server)
+static void encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const struct nfs_server *server)
 {
        char owner_name[IDMAP_NAMESZ];
        char owner_group[IDMAP_NAMESZ];
@@ -612,7 +618,6 @@ static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const s
        int len;
        uint32_t bmval0 = 0;
        uint32_t bmval1 = 0;
-       int status;
 
        /*
         * We reserve enough space to write the entire attribute buffer at once.
@@ -709,7 +714,7 @@ static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const s
                bmval1 |= FATTR4_WORD1_TIME_MODIFY_SET;
                WRITE32(NFS4_SET_TO_SERVER_TIME);
        }
-       
+
        /*
         * Now we backfill the bitmap and the attribute buffer length.
         */
@@ -723,23 +728,20 @@ static int encode_attrs(struct xdr_stream *xdr, const struct iattr *iap, const s
        *q++ = htonl(bmval1);
        *q++ = htonl(len);
 
-       status = 0;
 /* out: */
-       return status;
 }
 
-static int encode_access(struct xdr_stream *xdr, u32 access)
+static void encode_access(struct xdr_stream *xdr, u32 access, struct compound_hdr *hdr)
 {
        __be32 *p;
 
        RESERVE_SPACE(8);
        WRITE32(OP_ACCESS);
        WRITE32(access);
-       
-       return 0;
+       hdr->nops++;
 }
 
-static int encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg)
+static void encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg, struct compound_hdr *hdr)
 {
        __be32 *p;
 
@@ -747,26 +749,24 @@ static int encode_close(struct xdr_stream *xdr, const struct nfs_closeargs *arg)
        WRITE32(OP_CLOSE);
        WRITE32(arg->seqid->sequence->counter);
        WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE);
-       
-       return 0;
+       hdr->nops++;
 }
 
-static int encode_commit(struct xdr_stream *xdr, const struct nfs_writeargs *args)
+static void encode_commit(struct xdr_stream *xdr, const struct nfs_writeargs *args, struct compound_hdr *hdr)
 {
        __be32 *p;
-        
-        RESERVE_SPACE(16);
-        WRITE32(OP_COMMIT);
-        WRITE64(args->offset);
-        WRITE32(args->count);
 
-        return 0;
+       RESERVE_SPACE(16);
+       WRITE32(OP_COMMIT);
+       WRITE64(args->offset);
+       WRITE32(args->count);
+       hdr->nops++;
 }
 
-static int encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *create)
+static void encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *create, struct compound_hdr *hdr)
 {
        __be32 *p;
-       
+
        RESERVE_SPACE(8);
        WRITE32(OP_CREATE);
        WRITE32(create->ftype);
@@ -791,64 +791,62 @@ static int encode_create(struct xdr_stream *xdr, const struct nfs4_create_arg *c
        RESERVE_SPACE(4 + create->name->len);
        WRITE32(create->name->len);
        WRITEMEM(create->name->name, create->name->len);
+       hdr->nops++;
 
-       return encode_attrs(xdr, create->attrs, create->server);
+       encode_attrs(xdr, create->attrs, create->server);
 }
 
-static int encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap)
+static void encode_getattr_one(struct xdr_stream *xdr, uint32_t bitmap, struct compound_hdr *hdr)
 {
-        __be32 *p;
+       __be32 *p;
 
-        RESERVE_SPACE(12);
-        WRITE32(OP_GETATTR);
-        WRITE32(1);
-        WRITE32(bitmap);
-        return 0;
+       RESERVE_SPACE(12);
+       WRITE32(OP_GETATTR);
+       WRITE32(1);
+       WRITE32(bitmap);
+       hdr->nops++;
 }
 
-static int encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm1)
+static void encode_getattr_two(struct xdr_stream *xdr, uint32_t bm0, uint32_t bm1, struct compound_hdr *hdr)
 {
-        __be32 *p;
+       __be32 *p;
 
-        RESERVE_SPACE(16);
-        WRITE32(OP_GETATTR);
-        WRITE32(2);
-        WRITE32(bm0);
-        WRITE32(bm1);
-        return 0;
+       RESERVE_SPACE(16);
+       WRITE32(OP_GETATTR);
+       WRITE32(2);
+       WRITE32(bm0);
+       WRITE32(bm1);
+       hdr->nops++;
 }
 
-static int encode_getfattr(struct xdr_stream *xdr, const u32* bitmask)
+static void encode_getfattr(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
 {
-       return encode_getattr_two(xdr,
-                       bitmask[0] & nfs4_fattr_bitmap[0],
-                       bitmask[1] & nfs4_fattr_bitmap[1]);
+       encode_getattr_two(xdr, bitmask[0] & nfs4_fattr_bitmap[0],
+                          bitmask[1] & nfs4_fattr_bitmap[1], hdr);
 }
 
-static int encode_fsinfo(struct xdr_stream *xdr, const u32* bitmask)
+static void encode_fsinfo(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
 {
-       return encode_getattr_two(xdr, bitmask[0] & nfs4_fsinfo_bitmap[0],
-                       bitmask[1] & nfs4_fsinfo_bitmap[1]);
+       encode_getattr_two(xdr, bitmask[0] & nfs4_fsinfo_bitmap[0],
+                          bitmask[1] & nfs4_fsinfo_bitmap[1], hdr);
 }
 
-static int encode_fs_locations(struct xdr_stream *xdr, const u32* bitmask)
+static void encode_fs_locations(struct xdr_stream *xdr, const u32* bitmask, struct compound_hdr *hdr)
 {
-       return encode_getattr_two(xdr,
-                                 bitmask[0] & nfs4_fs_locations_bitmap[0],
-                                 bitmask[1] & nfs4_fs_locations_bitmap[1]);
+       encode_getattr_two(xdr, bitmask[0] & nfs4_fs_locations_bitmap[0],
+                          bitmask[1] & nfs4_fs_locations_bitmap[1], hdr);
 }
 
-static int encode_getfh(struct xdr_stream *xdr)
+static void encode_getfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
 {
        __be32 *p;
 
        RESERVE_SPACE(4);
        WRITE32(OP_GETFH);
-
-       return 0;
+       hdr->nops++;
 }
 
-static int encode_link(struct xdr_stream *xdr, const struct qstr *name)
+static void encode_link(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr)
 {
        __be32 *p;
 
@@ -856,8 +854,7 @@ static int encode_link(struct xdr_stream *xdr, const struct qstr *name)
        WRITE32(OP_LINK);
        WRITE32(name->len);
        WRITEMEM(name->name, name->len);
-       
-       return 0;
+       hdr->nops++;
 }
 
 static inline int nfs4_lock_type(struct file_lock *fl, int block)
@@ -878,7 +875,7 @@ static inline uint64_t nfs4_lock_length(struct file_lock *fl)
  * opcode,type,reclaim,offset,length,new_lock_owner = 32
  * open_seqid,open_stateid,lock_seqid,lock_owner.clientid, lock_owner.id = 40
  */
-static int encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args)
+static void encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args, struct compound_hdr *hdr)
 {
        __be32 *p;
 
@@ -904,11 +901,10 @@ static int encode_lock(struct xdr_stream *xdr, const struct nfs_lock_args *args)
                WRITEMEM(args->lock_stateid->data, NFS4_STATEID_SIZE);
                WRITE32(args->lock_seqid->sequence->counter);
        }
-
-       return 0;
+       hdr->nops++;
 }
 
-static int encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *args)
+static void encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *args, struct compound_hdr *hdr)
 {
        __be32 *p;
 
@@ -921,11 +917,10 @@ static int encode_lockt(struct xdr_stream *xdr, const struct nfs_lockt_args *arg
        WRITE32(16);
        WRITEMEM("lock id:", 8);
        WRITE64(args->lock_owner.id);
-
-       return 0;
+       hdr->nops++;
 }
 
-static int encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *args)
+static void encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *args, struct compound_hdr *hdr)
 {
        __be32 *p;
 
@@ -936,11 +931,10 @@ static int encode_locku(struct xdr_stream *xdr, const struct nfs_locku_args *arg
        WRITEMEM(args->stateid->data, NFS4_STATEID_SIZE);
        WRITE64(args->fl->fl_start);
        WRITE64(nfs4_lock_length(args->fl));
-
-       return 0;
+       hdr->nops++;
 }
 
-static int encode_lookup(struct xdr_stream *xdr, const struct qstr *name)
+static void encode_lookup(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr)
 {
        int len = name->len;
        __be32 *p;
@@ -949,27 +943,26 @@ static int encode_lookup(struct xdr_stream *xdr, const struct qstr *name)
        WRITE32(OP_LOOKUP);
        WRITE32(len);
        WRITEMEM(name->name, len);
-
-       return 0;
+       hdr->nops++;
 }
 
-static void encode_share_access(struct xdr_stream *xdr, int open_flags)
+static void encode_share_access(struct xdr_stream *xdr, fmode_t fmode)
 {
        __be32 *p;
 
        RESERVE_SPACE(8);
-       switch (open_flags & (FMODE_READ|FMODE_WRITE)) {
-               case FMODE_READ:
-                       WRITE32(NFS4_SHARE_ACCESS_READ);
-                       break;
-               case FMODE_WRITE:
-                       WRITE32(NFS4_SHARE_ACCESS_WRITE);
-                       break;
-               case FMODE_READ|FMODE_WRITE:
-                       WRITE32(NFS4_SHARE_ACCESS_BOTH);
-                       break;
-               default:
-                       BUG();
+       switch (fmode & (FMODE_READ|FMODE_WRITE)) {
+       case FMODE_READ:
+               WRITE32(NFS4_SHARE_ACCESS_READ);
+               break;
+       case FMODE_WRITE:
+               WRITE32(NFS4_SHARE_ACCESS_WRITE);
+               break;
+       case FMODE_READ|FMODE_WRITE:
+               WRITE32(NFS4_SHARE_ACCESS_BOTH);
+               break;
+       default:
+               WRITE32(0);
        }
        WRITE32(0);             /* for linux, share_deny = 0 always */
 }
@@ -984,7 +977,7 @@ static inline void encode_openhdr(struct xdr_stream *xdr, const struct nfs_opena
        RESERVE_SPACE(8);
        WRITE32(OP_OPEN);
        WRITE32(arg->seqid->sequence->counter);
-       encode_share_access(xdr, arg->open_flags);
+       encode_share_access(xdr, arg->fmode);
        RESERVE_SPACE(28);
        WRITE64(arg->clientid);
        WRITE32(16);
@@ -998,13 +991,13 @@ static inline void encode_createmode(struct xdr_stream *xdr, const struct nfs_op
 
        RESERVE_SPACE(4);
        switch(arg->open_flags & O_EXCL) {
-               case 0:
-                       WRITE32(NFS4_CREATE_UNCHECKED);
-                       encode_attrs(xdr, arg->u.attrs, arg->server);
-                       break;
-               default:
-                       WRITE32(NFS4_CREATE_EXCLUSIVE);
-                       encode_nfs4_verifier(xdr, &arg->u.verifier);
+       case 0:
+               WRITE32(NFS4_CREATE_UNCHECKED);
+               encode_attrs(xdr, arg->u.attrs, arg->server);
+               break;
+       default:
+               WRITE32(NFS4_CREATE_EXCLUSIVE);
+               encode_nfs4_verifier(xdr, &arg->u.verifier);
        }
 }
 
@@ -1014,33 +1007,33 @@ static void encode_opentype(struct xdr_stream *xdr, const struct nfs_openargs *a
 
        RESERVE_SPACE(4);
        switch (arg->open_flags & O_CREAT) {
-               case 0:
-                       WRITE32(NFS4_OPEN_NOCREATE);
-                       break;
-               default:
-                       BUG_ON(arg->claim != NFS4_OPEN_CLAIM_NULL);
-                       WRITE32(NFS4_OPEN_CREATE);
-                       encode_createmode(xdr, arg);
+       case 0:
+               WRITE32(NFS4_OPEN_NOCREATE);
+               break;
+       default:
+               BUG_ON(arg->claim != NFS4_OPEN_CLAIM_NULL);
+               WRITE32(NFS4_OPEN_CREATE);
+               encode_createmode(xdr, arg);
        }
 }
 
-static inline void encode_delegation_type(struct xdr_stream *xdr, int delegation_type)
+static inline void encode_delegation_type(struct xdr_stream *xdr, fmode_t delegation_type)
 {
        __be32 *p;
 
        RESERVE_SPACE(4);
        switch (delegation_type) {
-               case 0:
-                       WRITE32(NFS4_OPEN_DELEGATE_NONE);
-                       break;
-               case FMODE_READ:
-                       WRITE32(NFS4_OPEN_DELEGATE_READ);
-                       break;
-               case FMODE_WRITE|FMODE_READ:
-                       WRITE32(NFS4_OPEN_DELEGATE_WRITE);
-                       break;
-               default:
-                       BUG();
+       case 0:
+               WRITE32(NFS4_OPEN_DELEGATE_NONE);
+               break;
+       case FMODE_READ:
+               WRITE32(NFS4_OPEN_DELEGATE_READ);
+               break;
+       case FMODE_WRITE|FMODE_READ:
+               WRITE32(NFS4_OPEN_DELEGATE_WRITE);
+               break;
+       default:
+               BUG();
        }
 }
 
@@ -1053,7 +1046,7 @@ static inline void encode_claim_null(struct xdr_stream *xdr, const struct qstr *
        encode_string(xdr, name->len, name->name);
 }
 
-static inline void encode_claim_previous(struct xdr_stream *xdr, int type)
+static inline void encode_claim_previous(struct xdr_stream *xdr, fmode_t type)
 {
        __be32 *p;
 
@@ -1072,27 +1065,27 @@ static inline void encode_claim_delegate_cur(struct xdr_stream *xdr, const struc
        encode_string(xdr, name->len, name->name);
 }
 
-static int encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg)
+static void encode_open(struct xdr_stream *xdr, const struct nfs_openargs *arg, struct compound_hdr *hdr)
 {
        encode_openhdr(xdr, arg);
        encode_opentype(xdr, arg);
        switch (arg->claim) {
-               case NFS4_OPEN_CLAIM_NULL:
-                       encode_claim_null(xdr, arg->name);
-                       break;
-               case NFS4_OPEN_CLAIM_PREVIOUS:
-                       encode_claim_previous(xdr, arg->u.delegation_type);
-                       break;
-               case NFS4_OPEN_CLAIM_DELEGATE_CUR:
-                       encode_claim_delegate_cur(xdr, arg->name, &arg->u.delegation);
-                       break;
-               default:
-                       BUG();
+       case NFS4_OPEN_CLAIM_NULL:
+               encode_claim_null(xdr, arg->name);
+               break;
+       case NFS4_OPEN_CLAIM_PREVIOUS:
+               encode_claim_previous(xdr, arg->u.delegation_type);
+               break;
+       case NFS4_OPEN_CLAIM_DELEGATE_CUR:
+               encode_claim_delegate_cur(xdr, arg->name, &arg->u.delegation);
+               break;
+       default:
+               BUG();
        }
-       return 0;
+       hdr->nops++;
 }
 
-static int encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_confirmargs *arg)
+static void encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_confirmargs *arg, struct compound_hdr *hdr)
 {
        __be32 *p;
 
@@ -1100,11 +1093,10 @@ static int encode_open_confirm(struct xdr_stream *xdr, const struct nfs_open_con
        WRITE32(OP_OPEN_CONFIRM);
        WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE);
        WRITE32(arg->seqid->sequence->counter);
-
-       return 0;
+       hdr->nops++;
 }
 
-static int encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closeargs *arg)
+static void encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closeargs *arg, struct compound_hdr *hdr)
 {
        __be32 *p;
 
@@ -1112,12 +1104,12 @@ static int encode_open_downgrade(struct xdr_stream *xdr, const struct nfs_closea
        WRITE32(OP_OPEN_DOWNGRADE);
        WRITEMEM(arg->stateid->data, NFS4_STATEID_SIZE);
        WRITE32(arg->seqid->sequence->counter);
-       encode_share_access(xdr, arg->open_flags);
-       return 0;
+       encode_share_access(xdr, arg->fmode);
+       hdr->nops++;
 }
 
-static int
-encode_putfh(struct xdr_stream *xdr, const struct nfs_fh *fh)
+static void
+encode_putfh(struct xdr_stream *xdr, const struct nfs_fh *fh, struct compound_hdr *hdr)
 {
        int len = fh->size;
        __be32 *p;
@@ -1126,18 +1118,16 @@ encode_putfh(struct xdr_stream *xdr, const struct nfs_fh *fh)
        WRITE32(OP_PUTFH);
        WRITE32(len);
        WRITEMEM(fh->data, len);
-
-       return 0;
+       hdr->nops++;
 }
 
-static int encode_putrootfh(struct xdr_stream *xdr)
+static void encode_putrootfh(struct xdr_stream *xdr, struct compound_hdr *hdr)
 {
-        __be32 *p;
-        
-        RESERVE_SPACE(4);
-        WRITE32(OP_PUTROOTFH);
+       __be32 *p;
 
-        return 0;
+       RESERVE_SPACE(4);
+       WRITE32(OP_PUTROOTFH);
+       hdr->nops++;
 }
 
 static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context *ctx)
@@ -1153,7 +1143,7 @@ static void encode_stateid(struct xdr_stream *xdr, const struct nfs_open_context
                WRITEMEM(zero_stateid.data, NFS4_STATEID_SIZE);
 }
 
-static int encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args)
+static void encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args, struct compound_hdr *hdr)
 {
        __be32 *p;
 
@@ -1165,11 +1155,10 @@ static int encode_read(struct xdr_stream *xdr, const struct nfs_readargs *args)
        RESERVE_SPACE(12);
        WRITE64(args->offset);
        WRITE32(args->count);
-
-       return 0;
+       hdr->nops++;
 }
 
-static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req)
+static void encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg *readdir, struct rpc_rqst *req, struct compound_hdr *hdr)
 {
        uint32_t attrs[2] = {
                FATTR4_WORD0_RDATTR_ERROR|FATTR4_WORD0_FILEID,
@@ -1191,6 +1180,7 @@ static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
                attrs[1] &= ~FATTR4_WORD1_MOUNTED_ON_FILEID;
        WRITE32(attrs[0] & readdir->bitmask[0]);
        WRITE32(attrs[1] & readdir->bitmask[1]);
+       hdr->nops++;
        dprintk("%s: cookie = %Lu, verifier = %08x:%08x, bitmap = %08x:%08x\n",
                        __func__,
                        (unsigned long long)readdir->cookie,
@@ -1198,21 +1188,18 @@ static int encode_readdir(struct xdr_stream *xdr, const struct nfs4_readdir_arg
                        ((u32 *)readdir->verifier.data)[1],
                        attrs[0] & readdir->bitmask[0],
                        attrs[1] & readdir->bitmask[1]);
-
-       return 0;
 }
 
-static int encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *readlink, struct rpc_rqst *req)
+static void encode_readlink(struct xdr_stream *xdr, const struct nfs4_readlink *readlink, struct rpc_rqst *req, struct compound_hdr *hdr)
 {
        __be32 *p;
 
        RESERVE_SPACE(4);
        WRITE32(OP_READLINK);
-
-       return 0;
+       hdr->nops++;
 }
 
-static int encode_remove(struct xdr_stream *xdr, const struct qstr *name)
+static void encode_remove(struct xdr_stream *xdr, const struct qstr *name, struct compound_hdr *hdr)
 {
        __be32 *p;
 
@@ -1220,11 +1207,10 @@ static int encode_remove(struct xdr_stream *xdr, const struct qstr *name)
        WRITE32(OP_REMOVE);
        WRITE32(name->len);
        WRITEMEM(name->name, name->len);
-
-       return 0;
+       hdr->nops++;
 }
 
-static int encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, const struct qstr *newname)
+static void encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, const struct qstr *newname, struct compound_hdr *hdr)
 {
        __be32 *p;
 
@@ -1232,38 +1218,35 @@ static int encode_rename(struct xdr_stream *xdr, const struct qstr *oldname, con
        WRITE32(OP_RENAME);
        WRITE32(oldname->len);
        WRITEMEM(oldname->name, oldname->len);
-       
+
        RESERVE_SPACE(4 + newname->len);
        WRITE32(newname->len);
        WRITEMEM(newname->name, newname->len);
-
-       return 0;
+       hdr->nops++;
 }
 
-static int encode_renew(struct xdr_stream *xdr, const struct nfs_client *client_stateid)
+static void encode_renew(struct xdr_stream *xdr, const struct nfs_client *client_stateid, struct compound_hdr *hdr)
 {
        __be32 *p;
 
        RESERVE_SPACE(12);
        WRITE32(OP_RENEW);
        WRITE64(client_stateid->cl_clientid);
-
-       return 0;
+       hdr->nops++;
 }
 
-static int
-encode_restorefh(struct xdr_stream *xdr)
+static void
+encode_restorefh(struct xdr_stream *xdr, struct compound_hdr *hdr)
 {
        __be32 *p;
 
        RESERVE_SPACE(4);
        WRITE32(OP_RESTOREFH);
-
-       return 0;
+       hdr->nops++;
 }
 
 static int
-encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg)
+encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg, struct compound_hdr *hdr)
 {
        __be32 *p;
 
@@ -1278,36 +1261,32 @@ encode_setacl(struct xdr_stream *xdr, struct nfs_setaclargs *arg)
        RESERVE_SPACE(4);
        WRITE32(arg->acl_len);
        xdr_write_pages(xdr, arg->acl_pages, arg->acl_pgbase, arg->acl_len);
+       hdr->nops++;
        return 0;
 }
 
-static int
-encode_savefh(struct xdr_stream *xdr)
+static void
+encode_savefh(struct xdr_stream *xdr, struct compound_hdr *hdr)
 {
        __be32 *p;
 
        RESERVE_SPACE(4);
        WRITE32(OP_SAVEFH);
-
-       return 0;
+       hdr->nops++;
 }
 
-static int encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs *arg, const struct nfs_server *server)
+static void encode_setattr(struct xdr_stream *xdr, const struct nfs_setattrargs *arg, const struct nfs_server *server, struct compound_hdr *hdr)
 {
-       int status;
        __be32 *p;
-       
-        RESERVE_SPACE(4+NFS4_STATEID_SIZE);
-        WRITE32(OP_SETATTR);
-       WRITEMEM(arg->stateid.data, NFS4_STATEID_SIZE);
 
-        if ((status = encode_attrs(xdr, arg->iap, server)))
-               return status;
-
-        return 0;
+       RESERVE_SPACE(4+NFS4_STATEID_SIZE);
+       WRITE32(OP_SETATTR);
+       WRITEMEM(arg->stateid.data, NFS4_STATEID_SIZE);
+       hdr->nops++;
+       encode_attrs(xdr, arg->iap, server);
 }
 
-static int encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid)
+static void encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclientid *setclientid, struct compound_hdr *hdr)
 {
        __be32 *p;
 
@@ -1322,23 +1301,21 @@ static int encode_setclientid(struct xdr_stream *xdr, const struct nfs4_setclien
        encode_string(xdr, setclientid->sc_uaddr_len, setclientid->sc_uaddr);
        RESERVE_SPACE(4);
        WRITE32(setclientid->sc_cb_ident);
-
-       return 0;
+       hdr->nops++;
 }
 
-static int encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs_client *client_state)
+static void encode_setclientid_confirm(struct xdr_stream *xdr, const struct nfs_client *client_state, struct compound_hdr *hdr)
 {
-        __be32 *p;
-
-        RESERVE_SPACE(12 + NFS4_VERIFIER_SIZE);
-        WRITE32(OP_SETCLIENTID_CONFIRM);
-        WRITE64(client_state->cl_clientid);
-        WRITEMEM(client_state->cl_confirm.data, NFS4_VERIFIER_SIZE);
+       __be32 *p;
 
-        return 0;
+       RESERVE_SPACE(12 + NFS4_VERIFIER_SIZE);
+       WRITE32(OP_SETCLIENTID_CONFIRM);
+       WRITE64(client_state->cl_clientid);
+       WRITEMEM(client_state->cl_confirm.data, NFS4_VERIFIER_SIZE);
+       hdr->nops++;
 }
 
-static int encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *args)
+static void encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *args, struct compound_hdr *hdr)
 {
        __be32 *p;
 
@@ -1353,11 +1330,10 @@ static int encode_write(struct xdr_stream *xdr, const struct nfs_writeargs *args
        WRITE32(args->count);
 
        xdr_write_pages(xdr, args->pages, args->pgbase, args->count);
-
-       return 0;
+       hdr->nops++;
 }
 
-static int encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *stateid)
+static void encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *stateid, struct compound_hdr *hdr)
 {
        __be32 *p;
 
@@ -1365,8 +1341,7 @@ static int encode_delegreturn(struct xdr_stream *xdr, const nfs4_stateid *statei
 
        WRITE32(OP_DELEGRETURN);
        WRITEMEM(stateid->data, NFS4_STATEID_SIZE);
-       return 0;
-
+       hdr->nops++;
 }
 /*
  * END OF "GENERIC" ENCODE ROUTINES.
@@ -1379,21 +1354,16 @@ static int nfs4_xdr_enc_access(struct rpc_rqst *req, __be32 *p, const struct nfs
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 3,
+               .nops = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, args->fh);
-       if (status != 0)
-               goto out;
-       status = encode_access(&xdr, args->access);
-       if (status != 0)
-               goto out;
-       status = encode_getfattr(&xdr, args->bitmask);
-out:
-       return status;
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_access(&xdr, args->access, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1403,21 +1373,17 @@ static int nfs4_xdr_enc_lookup(struct rpc_rqst *req, __be32 *p, const struct nfs
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 4,
+               .nops = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       if ((status = encode_putfh(&xdr, args->dir_fh)) != 0)
-               goto out;
-       if ((status = encode_lookup(&xdr, args->name)) != 0)
-               goto out;
-       if ((status = encode_getfh(&xdr)) != 0)
-               goto out;
-       status = encode_getfattr(&xdr, args->bitmask);
-out:
-       return status;
+       encode_putfh(&xdr, args->dir_fh, &hdr);
+       encode_lookup(&xdr, args->name, &hdr);
+       encode_getfh(&xdr, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1427,18 +1393,16 @@ static int nfs4_xdr_enc_lookup_root(struct rpc_rqst *req, __be32 *p, const struc
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 3,
+               .nops = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       if ((status = encode_putrootfh(&xdr)) != 0)
-               goto out;
-       if ((status = encode_getfh(&xdr)) == 0)
-               status = encode_getfattr(&xdr, args->bitmask);
-out:
-       return status;
+       encode_putrootfh(&xdr, &hdr);
+       encode_getfh(&xdr, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1448,19 +1412,16 @@ static int nfs4_xdr_enc_remove(struct rpc_rqst *req, __be32 *p, const struct nfs
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 3,
+               .nops = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       if ((status = encode_putfh(&xdr, args->fh)) != 0)
-               goto out;
-       if ((status = encode_remove(&xdr, &args->name)) != 0)
-               goto out;
-       status = encode_getfattr(&xdr, args->bitmask);
-out:
-       return status;
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_remove(&xdr, &args->name, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1470,27 +1431,20 @@ static int nfs4_xdr_enc_rename(struct rpc_rqst *req, __be32 *p, const struct nfs
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 7,
+               .nops = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       if ((status = encode_putfh(&xdr, args->old_dir)) != 0)
-               goto out;
-       if ((status = encode_savefh(&xdr)) != 0)
-               goto out;
-       if ((status = encode_putfh(&xdr, args->new_dir)) != 0)
-               goto out;
-       if ((status = encode_rename(&xdr, args->old_name, args->new_name)) != 0)
-               goto out;
-       if ((status = encode_getfattr(&xdr, args->bitmask)) != 0)
-               goto out;
-       if ((status = encode_restorefh(&xdr)) != 0)
-               goto out;
-       status = encode_getfattr(&xdr, args->bitmask);
-out:
-       return status;
+       encode_putfh(&xdr, args->old_dir, &hdr);
+       encode_savefh(&xdr, &hdr);
+       encode_putfh(&xdr, args->new_dir, &hdr);
+       encode_rename(&xdr, args->old_name, args->new_name, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_restorefh(&xdr, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1500,27 +1454,20 @@ static int nfs4_xdr_enc_link(struct rpc_rqst *req, __be32 *p, const struct nfs4_
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 7,
+               .nops = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       if ((status = encode_putfh(&xdr, args->fh)) != 0)
-               goto out;
-       if ((status = encode_savefh(&xdr)) != 0)
-               goto out;
-       if ((status = encode_putfh(&xdr, args->dir_fh)) != 0)
-               goto out;
-       if ((status = encode_link(&xdr, args->name)) != 0)
-               goto out;
-       if ((status = encode_getfattr(&xdr, args->bitmask)) != 0)
-               goto out;
-       if ((status = encode_restorefh(&xdr)) != 0)
-               goto out;
-       status = encode_getfattr(&xdr, args->bitmask);
-out:
-       return status;
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_savefh(&xdr, &hdr);
+       encode_putfh(&xdr, args->dir_fh, &hdr);
+       encode_link(&xdr, args->name, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_restorefh(&xdr, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1530,27 +1477,20 @@ static int nfs4_xdr_enc_create(struct rpc_rqst *req, __be32 *p, const struct nfs
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 7,
+               .nops = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       if ((status = encode_putfh(&xdr, args->dir_fh)) != 0)
-               goto out;
-       if ((status = encode_savefh(&xdr)) != 0)
-               goto out;
-       if ((status = encode_create(&xdr, args)) != 0)
-               goto out;
-       if ((status = encode_getfh(&xdr)) != 0)
-               goto out;
-       if ((status = encode_getfattr(&xdr, args->bitmask)) != 0)
-               goto out;
-       if ((status = encode_restorefh(&xdr)) != 0)
-               goto out;
-       status = encode_getfattr(&xdr, args->bitmask);
-out:
-       return status;
+       encode_putfh(&xdr, args->dir_fh, &hdr);
+       encode_savefh(&xdr, &hdr);
+       encode_create(&xdr, args, &hdr);
+       encode_getfh(&xdr, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_restorefh(&xdr, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1568,15 +1508,15 @@ static int nfs4_xdr_enc_getattr(struct rpc_rqst *req, __be32 *p, const struct nf
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 2,
+               .nops = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       if ((status = encode_putfh(&xdr, args->fh)) == 0)
-               status = encode_getfattr(&xdr, args->bitmask);
-       return status;
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1584,23 +1524,18 @@ static int nfs4_xdr_enc_getattr(struct rpc_rqst *req, __be32 *p, const struct nf
  */
 static int nfs4_xdr_enc_close(struct rpc_rqst *req, __be32 *p, struct nfs_closeargs *args)
 {
-        struct xdr_stream xdr;
-        struct compound_hdr hdr = {
-                .nops   = 3,
-        };
-        int status;
-
-        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-        encode_compound_hdr(&xdr, &hdr);
-        status = encode_putfh(&xdr, args->fh);
-        if(status)
-                goto out;
-        status = encode_close(&xdr, args);
-       if (status != 0)
-               goto out;
-       status = encode_getfattr(&xdr, args->bitmask);
-out:
-        return status;
+       struct xdr_stream xdr;
+       struct compound_hdr hdr = {
+               .nops   = 0,
+       };
+
+       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
+       encode_compound_hdr(&xdr, &hdr);
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_close(&xdr, args, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1610,33 +1545,20 @@ static int nfs4_xdr_enc_open(struct rpc_rqst *req, __be32 *p, struct nfs_openarg
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 7,
+               .nops = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, args->fh);
-       if (status)
-               goto out;
-       status = encode_savefh(&xdr);
-       if (status)
-               goto out;
-       status = encode_open(&xdr, args);
-       if (status)
-               goto out;
-       status = encode_getfh(&xdr);
-       if (status)
-               goto out;
-       status = encode_getfattr(&xdr, args->bitmask);
-       if (status)
-               goto out;
-       status = encode_restorefh(&xdr);
-       if (status)
-               goto out;
-       status = encode_getfattr(&xdr, args->bitmask);
-out:
-       return status;
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_savefh(&xdr, &hdr);
+       encode_open(&xdr, args, &hdr);
+       encode_getfh(&xdr, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_restorefh(&xdr, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1646,18 +1568,15 @@ static int nfs4_xdr_enc_open_confirm(struct rpc_rqst *req, __be32 *p, struct nfs
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 2,
+               .nops   = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, args->fh);
-       if(status)
-               goto out;
-       status = encode_open_confirm(&xdr, args);
-out:
-       return status;
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_open_confirm(&xdr, args, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1667,21 +1586,16 @@ static int nfs4_xdr_enc_open_noattr(struct rpc_rqst *req, __be32 *p, struct nfs_
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 3,
+               .nops   = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, args->fh);
-       if (status)
-               goto out;
-       status = encode_open(&xdr, args);
-       if (status)
-               goto out;
-       status = encode_getfattr(&xdr, args->bitmask);
-out:
-       return status;
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_open(&xdr, args, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1691,21 +1605,16 @@ static int nfs4_xdr_enc_open_downgrade(struct rpc_rqst *req, __be32 *p, struct n
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 3,
+               .nops   = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, args->fh);
-       if (status)
-               goto out;
-       status = encode_open_downgrade(&xdr, args);
-       if (status != 0)
-               goto out;
-       status = encode_getfattr(&xdr, args->bitmask);
-out:
-       return status;
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_open_downgrade(&xdr, args, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1715,18 +1624,15 @@ static int nfs4_xdr_enc_lock(struct rpc_rqst *req, __be32 *p, struct nfs_lock_ar
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 2,
+               .nops   = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, args->fh);
-       if(status)
-               goto out;
-       status = encode_lock(&xdr, args);
-out:
-       return status;
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_lock(&xdr, args, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1736,18 +1642,15 @@ static int nfs4_xdr_enc_lockt(struct rpc_rqst *req, __be32 *p, struct nfs_lockt_
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 2,
+               .nops   = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, args->fh);
-       if(status)
-               goto out;
-       status = encode_lockt(&xdr, args);
-out:
-       return status;
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_lockt(&xdr, args, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1757,18 +1660,15 @@ static int nfs4_xdr_enc_locku(struct rpc_rqst *req, __be32 *p, struct nfs_locku_
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 2,
+               .nops   = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, args->fh);
-       if(status)
-               goto out;
-       status = encode_locku(&xdr, args);
-out:
-       return status;
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_locku(&xdr, args, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1778,18 +1678,15 @@ static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, __be32 *p, const struct n
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 2,
+               .nops = 0,
        };
        struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
        unsigned int replen;
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, args->fh);
-       if(status)
-               goto out;
-       status = encode_readlink(&xdr, args, req);
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_readlink(&xdr, args, req, &hdr);
 
        /* set up reply kvec
         *    toplevel_status + taglen + rescount + OP_PUTFH + status
@@ -1798,9 +1695,8 @@ static int nfs4_xdr_enc_readlink(struct rpc_rqst *req, __be32 *p, const struct n
        replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_readlink_sz) << 2;
        xdr_inline_pages(&req->rq_rcv_buf, replen, args->pages,
                        args->pgbase, args->pglen);
-
-out:
-       return status;
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1810,18 +1706,15 @@ static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nf
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 2,
+               .nops = 0,
        };
        struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
        int replen;
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, args->fh);
-       if(status)
-               goto out;
-       status = encode_readdir(&xdr, args, req);
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_readdir(&xdr, args, req, &hdr);
 
        /* set up reply kvec
         *    toplevel_status + taglen + rescount + OP_PUTFH + status
@@ -1833,9 +1726,8 @@ static int nfs4_xdr_enc_readdir(struct rpc_rqst *req, __be32 *p, const struct nf
        dprintk("%s: inlined page args = (%u, %p, %u, %u)\n",
                        __func__, replen, args->pages,
                        args->pgbase, args->count);
-
-out:
-       return status;
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1846,18 +1738,14 @@ static int nfs4_xdr_enc_read(struct rpc_rqst *req, __be32 *p, struct nfs_readarg
        struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 2,
+               .nops = 0,
        };
-       int replen, status;
+       int replen;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, args->fh);
-       if (status)
-               goto out;
-       status = encode_read(&xdr, args);
-       if (status)
-               goto out;
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_read(&xdr, args, &hdr);
 
        /* set up reply kvec
         *    toplevel status + taglen=0 + rescount + OP_PUTFH + status
@@ -1867,33 +1755,27 @@ static int nfs4_xdr_enc_read(struct rpc_rqst *req, __be32 *p, struct nfs_readarg
        xdr_inline_pages(&req->rq_rcv_buf, replen,
                         args->pages, args->pgbase, args->count);
        req->rq_rcv_buf.flags |= XDRBUF_READ;
-out:
-       return status;
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
  * Encode an SETATTR request
  */
 static int nfs4_xdr_enc_setattr(struct rpc_rqst *req, __be32 *p, struct nfs_setattrargs *args)
-
 {
-        struct xdr_stream xdr;
-        struct compound_hdr hdr = {
-                .nops   = 3,
-        };
-        int status;
-
-        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-        encode_compound_hdr(&xdr, &hdr);
-        status = encode_putfh(&xdr, args->fh);
-        if(status)
-                goto out;
-        status = encode_setattr(&xdr, args, args->server);
-        if(status)
-                goto out;
-       status = encode_getfattr(&xdr, args->bitmask);
-out:
-        return status;
+       struct xdr_stream xdr;
+       struct compound_hdr hdr = {
+               .nops   = 0,
+       };
+
+       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
+       encode_compound_hdr(&xdr, &hdr);
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_setattr(&xdr, args, args->server, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1906,22 +1788,21 @@ nfs4_xdr_enc_getacl(struct rpc_rqst *req, __be32 *p,
        struct xdr_stream xdr;
        struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
        struct compound_hdr hdr = {
-               .nops   = 2,
+               .nops   = 0,
        };
-       int replen, status;
+       int replen;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, args->fh);
-       if (status)
-               goto out;
-       status = encode_getattr_two(&xdr, FATTR4_WORD0_ACL, 0);
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_getattr_two(&xdr, FATTR4_WORD0_ACL, 0, &hdr);
+
        /* set up reply buffer: */
        replen = (RPC_REPHDRSIZE + auth->au_rslack + NFS4_dec_getacl_sz) << 2;
        xdr_inline_pages(&req->rq_rcv_buf, replen,
                args->acl_pages, args->acl_pgbase, args->acl_len);
-out:
-       return status;
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1931,22 +1812,17 @@ static int nfs4_xdr_enc_write(struct rpc_rqst *req, __be32 *p, struct nfs_writea
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 3,
+               .nops = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, args->fh);
-       if (status)
-               goto out;
-       status = encode_write(&xdr, args);
-       if (status)
-               goto out;
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_write(&xdr, args, &hdr);
        req->rq_snd_buf.flags |= XDRBUF_WRITE;
-       status = encode_getfattr(&xdr, args->bitmask);
-out:
-       return status;
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1956,21 +1832,16 @@ static int nfs4_xdr_enc_commit(struct rpc_rqst *req, __be32 *p, struct nfs_write
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 3,
+               .nops = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, args->fh);
-       if (status)
-               goto out;
-       status = encode_commit(&xdr, args);
-       if (status)
-               goto out;
-       status = encode_getfattr(&xdr, args->bitmask);
-out:
-       return status;
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_commit(&xdr, args, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1980,16 +1851,15 @@ static int nfs4_xdr_enc_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs4_fsin
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 2,
+               .nops   = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, args->fh);
-       if (!status)
-               status = encode_fsinfo(&xdr, args->bitmask);
-       return status;
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_fsinfo(&xdr, args->bitmask, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -1999,17 +1869,16 @@ static int nfs4_xdr_enc_pathconf(struct rpc_rqst *req, __be32 *p, const struct n
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 2,
+               .nops = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, args->fh);
-       if (!status)
-               status = encode_getattr_one(&xdr,
-                               args->bitmask[0] & nfs4_pathconf_bitmap[0]);
-       return status;
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_getattr_one(&xdr, args->bitmask[0] & nfs4_pathconf_bitmap[0],
+                          &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -2019,18 +1888,16 @@ static int nfs4_xdr_enc_statfs(struct rpc_rqst *req, __be32 *p, const struct nfs
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 2,
+               .nops = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, args->fh);
-       if (status == 0)
-               status = encode_getattr_two(&xdr,
-                               args->bitmask[0] & nfs4_statfs_bitmap[0],
-                               args->bitmask[1] & nfs4_statfs_bitmap[1]);
-       return status;
+       encode_putfh(&xdr, args->fh, &hdr);
+       encode_getattr_two(&xdr, args->bitmask[0] & nfs4_statfs_bitmap[0],
+                          args->bitmask[1] & nfs4_statfs_bitmap[1], &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -2040,19 +1907,18 @@ static int nfs4_xdr_enc_server_caps(struct rpc_rqst *req, __be32 *p, const struc
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 2,
+               .nops = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, fhandle);
-       if (status == 0)
-               status = encode_getattr_one(&xdr, FATTR4_WORD0_SUPPORTED_ATTRS|
-                               FATTR4_WORD0_LINK_SUPPORT|
-                               FATTR4_WORD0_SYMLINK_SUPPORT|
-                               FATTR4_WORD0_ACLSUPPORT);
-       return status;
+       encode_putfh(&xdr, fhandle, &hdr);
+       encode_getattr_one(&xdr, FATTR4_WORD0_SUPPORTED_ATTRS|
+                          FATTR4_WORD0_LINK_SUPPORT|
+                          FATTR4_WORD0_SYMLINK_SUPPORT|
+                          FATTR4_WORD0_ACLSUPPORT, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -2062,12 +1928,14 @@ static int nfs4_xdr_enc_renew(struct rpc_rqst *req, __be32 *p, struct nfs_client
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 1,
+               .nops   = 0,
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       return encode_renew(&xdr, clp);
+       encode_renew(&xdr, clp, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -2077,12 +1945,14 @@ static int nfs4_xdr_enc_setclientid(struct rpc_rqst *req, __be32 *p, struct nfs4
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 1,
+               .nops   = 0,
        };
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       return encode_setclientid(&xdr, sc);
+       encode_setclientid(&xdr, sc, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -2092,19 +1962,17 @@ static int nfs4_xdr_enc_setclientid_confirm(struct rpc_rqst *req, __be32 *p, str
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops   = 3,
+               .nops   = 0,
        };
        const u32 lease_bitmap[2] = { FATTR4_WORD0_LEASE_TIME, 0 };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_setclientid_confirm(&xdr, clp);
-       if (!status)
-               status = encode_putrootfh(&xdr);
-       if (!status)
-               status = encode_fsinfo(&xdr, lease_bitmap);
-       return status;
+       encode_setclientid_confirm(&xdr, clp, &hdr);
+       encode_putrootfh(&xdr, &hdr);
+       encode_fsinfo(&xdr, lease_bitmap, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -2114,21 +1982,16 @@ static int nfs4_xdr_enc_delegreturn(struct rpc_rqst *req, __be32 *p, const struc
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 3,
+               .nops = 0,
        };
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       status = encode_putfh(&xdr, args->fhandle);
-       if (status != 0)
-               goto out;
-       status = encode_delegreturn(&xdr, args->stateid);
-       if (status != 0)
-               goto out;
-       status = encode_getfattr(&xdr, args->bitmask);
-out:
-       return status;
+       encode_putfh(&xdr, args->fhandle, &hdr);
+       encode_delegreturn(&xdr, args->stateid, &hdr);
+       encode_getfattr(&xdr, args->bitmask, &hdr);
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -2138,20 +2001,17 @@ static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs
 {
        struct xdr_stream xdr;
        struct compound_hdr hdr = {
-               .nops = 3,
+               .nops = 0,
        };
        struct rpc_auth *auth = req->rq_task->tk_msg.rpc_cred->cr_auth;
        int replen;
-       int status;
 
        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
        encode_compound_hdr(&xdr, &hdr);
-       if ((status = encode_putfh(&xdr, args->dir_fh)) != 0)
-               goto out;
-       if ((status = encode_lookup(&xdr, args->name)) != 0)
-               goto out;
-       if ((status = encode_fs_locations(&xdr, args->bitmask)) != 0)
-               goto out;
+       encode_putfh(&xdr, args->dir_fh, &hdr);
+       encode_lookup(&xdr, args->name, &hdr);
+       encode_fs_locations(&xdr, args->bitmask, &hdr);
+
        /* set up reply
         *   toplevel_status + OP_PUTFH + status
         *   + OP_LOOKUP + status + OP_GETATTR + status = 7
@@ -2159,8 +2019,8 @@ static int nfs4_xdr_enc_fs_locations(struct rpc_rqst *req, __be32 *p, struct nfs
        replen = (RPC_REPHDRSIZE + auth->au_rslack + 7) << 2;
        xdr_inline_pages(&req->rq_rcv_buf, replen, &args->page,
                        0, PAGE_SIZE);
-out:
-       return status;
+       encode_nops(&hdr);
+       return 0;
 }
 
 /*
@@ -2217,11 +2077,13 @@ static int decode_compound_hdr(struct xdr_stream *xdr, struct compound_hdr *hdr)
        READ_BUF(8);
        READ32(hdr->status);
        READ32(hdr->taglen);
-       
+
        READ_BUF(hdr->taglen + 4);
        hdr->tag = (char *)p;
        p += XDR_QUADLEN(hdr->taglen);
        READ32(hdr->nops);
+       if (unlikely(hdr->nops < 1))
+               return nfs4_stat_to_errno(hdr->status);
        return 0;
 }
 
@@ -3047,8 +2909,7 @@ static int decode_create(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
 static int decode_server_caps(struct xdr_stream *xdr, struct nfs4_server_caps_res *res)
 {
        __be32 *savep;
-       uint32_t attrlen, 
-                bitmap[2] = {0};
+       uint32_t attrlen, bitmap[2] = {0};
        int status;
 
        if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
@@ -3070,14 +2931,13 @@ xdr_error:
        dprintk("%s: xdr returned %d!\n", __func__, -status);
        return status;
 }
-       
+
 static int decode_statfs(struct xdr_stream *xdr, struct nfs_fsstat *fsstat)
 {
        __be32 *savep;
-       uint32_t attrlen, 
-                bitmap[2] = {0};
+       uint32_t attrlen, bitmap[2] = {0};
        int status;
-       
+
        if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
                goto xdr_error;
        if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
@@ -3107,10 +2967,9 @@ xdr_error:
 static int decode_pathconf(struct xdr_stream *xdr, struct nfs_pathconf *pathconf)
 {
        __be32 *savep;
-       uint32_t attrlen, 
-                bitmap[2] = {0};
+       uint32_t attrlen, bitmap[2] = {0};
        int status;
-       
+
        if ((status = decode_op_hdr(xdr, OP_GETATTR)) != 0)
                goto xdr_error;
        if ((status = decode_attr_bitmap(xdr, bitmap)) != 0)
@@ -3256,7 +3115,7 @@ static int decode_getfh(struct xdr_stream *xdr, struct nfs_fh *fh)
 static int decode_link(struct xdr_stream *xdr, struct nfs4_change_info *cinfo)
 {
        int status;
-       
+
        status = decode_op_hdr(xdr, OP_LINK);
        if (status)
                return status;
@@ -3344,27 +3203,27 @@ static int decode_lookup(struct xdr_stream *xdr)
 /* This is too sick! */
 static int decode_space_limit(struct xdr_stream *xdr, u64 *maxsize)
 {
-        __be32 *p;
+       __be32 *p;
        uint32_t limit_type, nblocks, blocksize;
 
        READ_BUF(12);
        READ32(limit_type);
        switch (limit_type) {
-               case 1:
-                       READ64(*maxsize);
-                       break;
-               case 2:
-                       READ32(nblocks);
-                       READ32(blocksize);
-                       *maxsize = (uint64_t)nblocks * (uint64_t)blocksize;
+       case 1:
+               READ64(*maxsize);
+               break;
+       case 2:
+               READ32(nblocks);
+               READ32(blocksize);
+               *maxsize = (uint64_t)nblocks * (uint64_t)blocksize;
        }
        return 0;
 }
 
 static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
 {
-        __be32 *p;
-        uint32_t delegation_type;
+       __be32 *p;
+       uint32_t delegation_type;
 
        READ_BUF(4);
        READ32(delegation_type);
@@ -3375,13 +3234,14 @@ static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
        READ_BUF(NFS4_STATEID_SIZE+4);
        COPYMEM(res->delegation.data, NFS4_STATEID_SIZE);
        READ32(res->do_recall);
+
        switch (delegation_type) {
-               case NFS4_OPEN_DELEGATE_READ:
-                       res->delegation_type = FMODE_READ;
-                       break;
-               case NFS4_OPEN_DELEGATE_WRITE:
-                       res->delegation_type = FMODE_WRITE|FMODE_READ;
-                       if (decode_space_limit(xdr, &res->maxsize) < 0)
+       case NFS4_OPEN_DELEGATE_READ:
+               res->delegation_type = FMODE_READ;
+               break;
+       case NFS4_OPEN_DELEGATE_WRITE:
+               res->delegation_type = FMODE_WRITE|FMODE_READ;
+               if (decode_space_limit(xdr, &res->maxsize) < 0)
                                return -EIO;
        }
        return decode_ace(xdr, NULL, res->server->nfs_client);
@@ -3389,27 +3249,27 @@ static int decode_delegation(struct xdr_stream *xdr, struct nfs_openres *res)
 
 static int decode_open(struct xdr_stream *xdr, struct nfs_openres *res)
 {
-        __be32 *p;
+       __be32 *p;
        uint32_t savewords, bmlen, i;
-        int status;
+       int status;
 
-        status = decode_op_hdr(xdr, OP_OPEN);
+       status = decode_op_hdr(xdr, OP_OPEN);
        if (status != -EIO)
                nfs_increment_open_seqid(status, res->seqid);
-        if (status)
-                return status;
-        READ_BUF(NFS4_STATEID_SIZE);
-        COPYMEM(res->stateid.data, NFS4_STATEID_SIZE);
+       if (status)
+               return status;
+       READ_BUF(NFS4_STATEID_SIZE);
+       COPYMEM(res->stateid.data, NFS4_STATEID_SIZE);
 
-        decode_change_info(xdr, &res->cinfo);
+       decode_change_info(xdr, &res->cinfo);
 
-        READ_BUF(8);
-        READ32(res->rflags);
-        READ32(bmlen);
-        if (bmlen > 10)
-                goto xdr_error;
+       READ_BUF(8);
+       READ32(res->rflags);
+       READ32(bmlen);
+       if (bmlen > 10)
+               goto xdr_error;
 
-        READ_BUF(bmlen << 2);
+       READ_BUF(bmlen << 2);
        savewords = min_t(uint32_t, bmlen, NFS4_BITMAP_SIZE);
        for (i = 0; i < savewords; ++i)
                READ32(res->attrset[i]);
@@ -3424,17 +3284,17 @@ xdr_error:
 
 static int decode_open_confirm(struct xdr_stream *xdr, struct nfs_open_confirmres *res)
 {
-        __be32 *p;
+       __be32 *p;
        int status;
 
-        status = decode_op_hdr(xdr, OP_OPEN_CONFIRM);
+       status = decode_op_hdr(xdr, OP_OPEN_CONFIRM);
        if (status != -EIO)
                nfs_increment_open_seqid(status, res->seqid);
-        if (status)
-                return status;
-        READ_BUF(NFS4_STATEID_SIZE);
-        COPYMEM(res->stateid.data, NFS4_STATEID_SIZE);
-        return 0;
+       if (status)
+               return status;
+       READ_BUF(NFS4_STATEID_SIZE);
+       COPYMEM(res->stateid.data, NFS4_STATEID_SIZE);
+       return 0;
 }
 
 static int decode_open_downgrade(struct xdr_stream *xdr, struct nfs_closeres *res)
@@ -3562,7 +3422,7 @@ static int decode_readdir(struct xdr_stream *xdr, struct rpc_rqst *req, struct n
                dprintk("NFS: readdir reply truncated!\n");
                entry[1] = 1;
        }
-out:   
+out:
        kunmap_atomic(kaddr, KM_USER0);
        return 0;
 short_pkt:
@@ -3718,7 +3578,6 @@ static int decode_setattr(struct xdr_stream *xdr, struct nfs_setattrres *res)
        uint32_t bmlen;
        int status;
 
-        
        status = decode_op_hdr(xdr, OP_SETATTR);
        if (status)
                return status;
@@ -3738,7 +3597,7 @@ static int decode_setclientid(struct xdr_stream *xdr, struct nfs_client *clp)
        READ32(opnum);
        if (opnum != OP_SETCLIENTID) {
                dprintk("nfs: decode_setclientid: Server returned operation"
-                               " %d\n", opnum);
+                       " %d\n", opnum);
                return -EIO;
        }
        READ32(nfserr);
@@ -3791,34 +3650,34 @@ static int decode_delegreturn(struct xdr_stream *xdr)
        return decode_op_hdr(xdr, OP_DELEGRETURN);
 }
 
+/*
+ * END OF "GENERIC" DECODE ROUTINES.
+ */
+
 /*
  * Decode OPEN_DOWNGRADE response
  */
 static int nfs4_xdr_dec_open_downgrade(struct rpc_rqst *rqstp, __be32 *p, struct nfs_closeres *res)
 {
-        struct xdr_stream xdr;
-        struct compound_hdr hdr;
-        int status;
-
-        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
-        status = decode_compound_hdr(&xdr, &hdr);
-        if (status)
-                goto out;
-        status = decode_putfh(&xdr);
-        if (status)
-                goto out;
-        status = decode_open_downgrade(&xdr, res);
+       struct xdr_stream xdr;
+       struct compound_hdr hdr;
+       int status;
+
+       xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_putfh(&xdr);
+       if (status)
+               goto out;
+       status = decode_open_downgrade(&xdr, res);
        if (status != 0)
                goto out;
        decode_getfattr(&xdr, res->fattr, res->server);
 out:
-        return status;
+       return status;
 }
 
-/*
- * END OF "GENERIC" DECODE ROUTINES.
- */
-
 /*
  * Decode ACCESS response
  */
@@ -3827,7 +3686,7 @@ static int nfs4_xdr_dec_access(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_ac
        struct xdr_stream xdr;
        struct compound_hdr hdr;
        int status;
-       
+
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
                goto out;
@@ -3850,7 +3709,7 @@ static int nfs4_xdr_dec_lookup(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_lo
        struct xdr_stream xdr;
        struct compound_hdr hdr;
        int status;
-       
+
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
                goto out;
@@ -3873,7 +3732,7 @@ static int nfs4_xdr_dec_lookup_root(struct rpc_rqst *rqstp, __be32 *p, struct nf
        struct xdr_stream xdr;
        struct compound_hdr hdr;
        int status;
-       
+
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
                goto out;
@@ -3893,7 +3752,7 @@ static int nfs4_xdr_dec_remove(struct rpc_rqst *rqstp, __be32 *p, struct nfs_rem
        struct xdr_stream xdr;
        struct compound_hdr hdr;
        int status;
-       
+
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
                goto out;
@@ -3914,7 +3773,7 @@ static int nfs4_xdr_dec_rename(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_re
        struct xdr_stream xdr;
        struct compound_hdr hdr;
        int status;
-       
+
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
                goto out;
@@ -3944,7 +3803,7 @@ static int nfs4_xdr_dec_link(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_link
        struct xdr_stream xdr;
        struct compound_hdr hdr;
        int status;
-       
+
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
                goto out;
@@ -3977,7 +3836,7 @@ static int nfs4_xdr_dec_create(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_cr
        struct xdr_stream xdr;
        struct compound_hdr hdr;
        int status;
-       
+
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        if ((status = decode_compound_hdr(&xdr, &hdr)) != 0)
                goto out;
@@ -4014,7 +3873,7 @@ static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_g
        struct xdr_stream xdr;
        struct compound_hdr hdr;
        int status;
-       
+
        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
        status = decode_compound_hdr(&xdr, &hdr);
        if (status)
@@ -4025,7 +3884,6 @@ static int nfs4_xdr_dec_getattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs4_g
        status = decode_getfattr(&xdr, res->fattr, res->server);
 out:
        return status;
-
 }
 
 /*
@@ -4034,21 +3892,20 @@ out:
 static int
 nfs4_xdr_enc_setacl(struct rpc_rqst *req, __be32 *p, struct nfs_setaclargs *args)
 {
-        struct xdr_stream xdr;
-        struct compound_hdr hdr = {
-                .nops   = 2,
-        };
-        int status;
-
-        xdr_init_encode(&xdr, &req->rq_snd_buf, p);
-        encode_compound_hdr(&xdr, &hdr);
-        status = encode_putfh(&xdr, args->fh);
-        if (status)
-                goto out;
-        status = encode_setacl(&xdr, args);
-out:
-        return status;
+       struct xdr_stream xdr;
+       struct compound_hdr hdr = {
+               .nops   = 0,
+       };
+       int status;
+
+       xdr_init_encode(&xdr, &req->rq_snd_buf, p);
+       encode_compound_hdr(&xdr, &hdr);
+       encode_putfh(&xdr, args->fh, &hdr);
+       status = encode_setacl(&xdr, args, &hdr);
+       encode_nops(&hdr);
+       return status;
 }
+
 /*
  * Decode SETACL response
  */
@@ -4099,18 +3956,18 @@ out:
  */
 static int nfs4_xdr_dec_close(struct rpc_rqst *rqstp, __be32 *p, struct nfs_closeres *res)
 {
-        struct xdr_stream xdr;
-        struct compound_hdr hdr;
-        int status;
-
-        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
-        status = decode_compound_hdr(&xdr, &hdr);
-        if (status)
-                goto out;
-        status = decode_putfh(&xdr);
-        if (status)
-                goto out;
-        status = decode_close(&xdr, res);
+       struct xdr_stream xdr;
+       struct compound_hdr hdr;
+       int status;
+
+       xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_putfh(&xdr);
+       if (status)
+               goto out;
+       status = decode_close(&xdr, res);
        if (status != 0)
                goto out;
        /*
@@ -4121,7 +3978,7 @@ static int nfs4_xdr_dec_close(struct rpc_rqst *rqstp, __be32 *p, struct nfs_clos
         */
        decode_getfattr(&xdr, res->fattr, res->server);
 out:
-        return status;
+       return status;
 }
 
 /*
@@ -4129,23 +3986,23 @@ out:
  */
 static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, __be32 *p, struct nfs_openres *res)
 {
-        struct xdr_stream xdr;
-        struct compound_hdr hdr;
-        int status;
-
-        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
-        status = decode_compound_hdr(&xdr, &hdr);
-        if (status)
-                goto out;
-        status = decode_putfh(&xdr);
-        if (status)
-                goto out;
-        status = decode_savefh(&xdr);
+       struct xdr_stream xdr;
+       struct compound_hdr hdr;
+       int status;
+
+       xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_putfh(&xdr);
+       if (status)
+               goto out;
+       status = decode_savefh(&xdr);
+       if (status)
+               goto out;
+       status = decode_open(&xdr, res);
        if (status)
                goto out;
-        status = decode_open(&xdr, res);
-        if (status)
-                goto out;
        if (decode_getfh(&xdr, &res->fh) != 0)
                goto out;
        if (decode_getfattr(&xdr, res->f_attr, res->server) != 0)
@@ -4154,7 +4011,7 @@ static int nfs4_xdr_dec_open(struct rpc_rqst *rqstp, __be32 *p, struct nfs_openr
                goto out;
        decode_getfattr(&xdr, res->dir_attr, res->server);
 out:
-        return status;
+       return status;
 }
 
 /*
@@ -4162,20 +4019,20 @@ out:
  */
 static int nfs4_xdr_dec_open_confirm(struct rpc_rqst *rqstp, __be32 *p, struct nfs_open_confirmres *res)
 {
-        struct xdr_stream xdr;
-        struct compound_hdr hdr;
-        int status;
-
-        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
-        status = decode_compound_hdr(&xdr, &hdr);
-        if (status)
-                goto out;
-        status = decode_putfh(&xdr);
-        if (status)
-                goto out;
-        status = decode_open_confirm(&xdr, res);
+       struct xdr_stream xdr;
+       struct compound_hdr hdr;
+       int status;
+
+       xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_putfh(&xdr);
+       if (status)
+               goto out;
+       status = decode_open_confirm(&xdr, res);
 out:
-        return status;
+       return status;
 }
 
 /*
@@ -4183,23 +4040,23 @@ out:
  */
 static int nfs4_xdr_dec_open_noattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs_openres *res)
 {
-        struct xdr_stream xdr;
-        struct compound_hdr hdr;
-        int status;
-
-        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
-        status = decode_compound_hdr(&xdr, &hdr);
-        if (status)
-                goto out;
-        status = decode_putfh(&xdr);
-        if (status)
-                goto out;
-        status = decode_open(&xdr, res);
-        if (status)
-                goto out;
+       struct xdr_stream xdr;
+       struct compound_hdr hdr;
+       int status;
+
+       xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_putfh(&xdr);
+       if (status)
+               goto out;
+       status = decode_open(&xdr, res);
+       if (status)
+               goto out;
        decode_getfattr(&xdr, res->f_attr, res->server);
 out:
-        return status;
+       return status;
 }
 
 /*
@@ -4207,25 +4064,25 @@ out:
  */
 static int nfs4_xdr_dec_setattr(struct rpc_rqst *rqstp, __be32 *p, struct nfs_setattrres *res)
 {
-        struct xdr_stream xdr;
-        struct compound_hdr hdr;
-        int status;
-
-        xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
-        status = decode_compound_hdr(&xdr, &hdr);
-        if (status)
-                goto out;
-        status = decode_putfh(&xdr);
-        if (status)
-                goto out;
-        status = decode_setattr(&xdr, res);
-        if (status)
-                goto out;
+       struct xdr_stream xdr;
+       struct compound_hdr hdr;
+       int status;
+
+       xdr_init_decode(&xdr, &rqstp->rq_rcv_buf, p);
+       status = decode_compound_hdr(&xdr, &hdr);
+       if (status)
+               goto out;
+       status = decode_putfh(&xdr);
+       if (status)
+               goto out;
+       status = decode_setattr(&xdr, res);
+       if (status)
+               goto out;
        status = decode_getfattr(&xdr, res->fattr, res->server);
        if (status == NFS4ERR_DELAY)
                status = 0;
 out:
-        return status;
+       return status;
 }
 
 /*
@@ -4421,8 +4278,6 @@ static int nfs4_xdr_dec_fsinfo(struct rpc_rqst *req, __be32 *p, struct nfs_fsinf
                status = decode_putfh(&xdr);
        if (!status)
                status = decode_fsinfo(&xdr, fsinfo);
-       if (!status)
-               status = nfs4_stat_to_errno(hdr.status);
        return status;
 }
 
@@ -4511,8 +4366,6 @@ static int nfs4_xdr_dec_setclientid(struct rpc_rqst *req, __be32 *p,
        status = decode_compound_hdr(&xdr, &hdr);
        if (!status)
                status = decode_setclientid(&xdr, clp);
-       if (!status)
-               status = nfs4_stat_to_errno(hdr.status);
        return status;
 }
 
@@ -4533,8 +4386,6 @@ static int nfs4_xdr_dec_setclientid_confirm(struct rpc_rqst *req, __be32 *p, str
                status = decode_putrootfh(&xdr);
        if (!status)
                status = decode_fsinfo(&xdr, fsinfo);
-       if (!status)
-               status = nfs4_stat_to_errno(hdr.status);
        return status;
 }
 
@@ -4715,7 +4566,7 @@ nfs4_stat_to_errno(int stat)
        .p_replen = NFS4_##restype##_sz,                        \
        .p_statidx = NFSPROC4_CLNT_##proc,                      \
        .p_name   = #proc,                                      \
-    }
+}
 
 struct rpc_procinfo    nfs4_procedures[] = {
   PROC(READ,           enc_read,       dec_read),
index d74d16ce0d49920ed710d0602c4797213aa79d38..d9ef602fbc5a893f60d087fe00a2744b7fdcd9e4 100644 (file)
@@ -86,6 +86,8 @@
 #include <net/ipconfig.h>
 #include <linux/parser.h>
 
+#include "internal.h"
+
 /* Define this to allow debugging output */
 #undef NFSROOT_DEBUG
 #define NFSDBG_FACILITY NFSDBG_ROOT
@@ -100,7 +102,7 @@ static char nfs_root_name[256] __initdata = "";
 static __be32 servaddr __initdata = 0;
 
 /* Name of directory to mount */
-static char nfs_path[NFS_MAXPATHLEN] __initdata = { 0, };
+static char nfs_export_path[NFS_MAXPATHLEN] __initdata = { 0, };
 
 /* NFS-related data */
 static struct nfs_mount_data nfs_data __initdata = { 0, };/* NFS mount info */
@@ -312,7 +314,7 @@ static int __init root_nfs_name(char *name)
                printk(KERN_ERR "Root-NFS: Pathname for remote directory too long.\n");
                return -1;
        }
-       sprintf(nfs_path, buf, cp);
+       sprintf(nfs_export_path, buf, cp);
 
        return 1;
 }
@@ -340,7 +342,7 @@ static int __init root_nfs_addr(void)
 static void __init root_nfs_print(void)
 {
        printk(KERN_NOTICE "Root-NFS: Mounting %s on server %s as root\n",
-               nfs_path, nfs_data.hostname);
+               nfs_export_path, nfs_data.hostname);
        printk(KERN_NOTICE "Root-NFS:     rsize = %d, wsize = %d, timeo = %d, retrans = %d\n",
                nfs_data.rsize, nfs_data.wsize, nfs_data.timeo, nfs_data.retrans);
        printk(KERN_NOTICE "Root-NFS:     acreg (min,max) = (%d,%d), acdir (min,max) = (%d,%d)\n",
@@ -485,18 +487,23 @@ static int __init root_nfs_get_handle(void)
 {
        struct nfs_fh fh;
        struct sockaddr_in sin;
+       struct nfs_mount_request request = {
+               .sap            = (struct sockaddr *)&sin,
+               .salen          = sizeof(sin),
+               .dirpath        = nfs_export_path,
+               .version        = (nfs_data.flags & NFS_MOUNT_VER3) ?
+                                       NFS_MNT3_VERSION : NFS_MNT_VERSION,
+               .protocol       = (nfs_data.flags & NFS_MOUNT_TCP) ?
+                                       XPRT_TRANSPORT_TCP : XPRT_TRANSPORT_UDP,
+               .fh             = &fh,
+       };
        int status;
-       int protocol = (nfs_data.flags & NFS_MOUNT_TCP) ?
-                                       XPRT_TRANSPORT_TCP : XPRT_TRANSPORT_UDP;
-       int version = (nfs_data.flags & NFS_MOUNT_VER3) ?
-                                       NFS_MNT3_VERSION : NFS_MNT_VERSION;
 
        set_sockaddr(&sin, servaddr, htons(mount_port));
-       status = nfs_mount((struct sockaddr *) &sin, sizeof(sin), NULL,
-                          nfs_path, version, protocol, &fh);
+       status = nfs_mount(&request);
        if (status < 0)
                printk(KERN_ERR "Root-NFS: Server returned error %d "
-                               "while mounting %s\n", status, nfs_path);
+                               "while mounting %s\n", status, nfs_export_path);
        else {
                nfs_data.root.size = fh.size;
                memcpy(nfs_data.root.data, fh.data, fh.size);
index 40d17987d0e847a87fa4596a9a4c9f39a464288e..f856004bb7fa3c8ed4bfe99c3c61ccd6d5729861 100644 (file)
@@ -533,12 +533,6 @@ readpage_async_filler(void *data, struct page *page)
        unsigned int len;
        int error;
 
-       error = nfs_wb_page(inode, page);
-       if (error)
-               goto out_unlock;
-       if (PageUptodate(page))
-               goto out_unlock;
-
        len = nfs_page_length(page);
        if (len == 0)
                return nfs_return_empty_page(page);
index bb0313ac9e1fa1a6b94ddb51229298e88b955bfd..d6686f4786dc55b5d5f06afe913e2930f2b45b96 100644 (file)
@@ -75,6 +75,7 @@ enum {
        Opt_acl, Opt_noacl,
        Opt_rdirplus, Opt_nordirplus,
        Opt_sharecache, Opt_nosharecache,
+       Opt_resvport, Opt_noresvport,
 
        /* Mount options that take integer arguments */
        Opt_port,
@@ -129,6 +130,8 @@ static const match_table_t nfs_mount_option_tokens = {
        { Opt_nordirplus, "nordirplus" },
        { Opt_sharecache, "sharecache" },
        { Opt_nosharecache, "nosharecache" },
+       { Opt_resvport, "resvport" },
+       { Opt_noresvport, "noresvport" },
 
        { Opt_port, "port=%u" },
        { Opt_rsize, "rsize=%u" },
@@ -512,7 +515,8 @@ static void nfs_show_mount_options(struct seq_file *m, struct nfs_server *nfss,
                { NFS_MOUNT_NONLM, ",nolock", "" },
                { NFS_MOUNT_NOACL, ",noacl", "" },
                { NFS_MOUNT_NORDIRPLUS, ",nordirplus", "" },
-               { NFS_MOUNT_UNSHARED, ",nosharecache", ""},
+               { NFS_MOUNT_UNSHARED, ",nosharecache", "" },
+               { NFS_MOUNT_NORESVPORT, ",noresvport", "" },
                { 0, NULL, NULL }
        };
        const struct proc_nfs_info *nfs_infop;
@@ -1033,6 +1037,12 @@ static int nfs_parse_mount_options(char *raw,
                case Opt_nosharecache:
                        mnt->flags |= NFS_MOUNT_UNSHARED;
                        break;
+               case Opt_resvport:
+                       mnt->flags &= ~NFS_MOUNT_NORESVPORT;
+                       break;
+               case Opt_noresvport:
+                       mnt->flags |= NFS_MOUNT_NORESVPORT;
+                       break;
 
                /*
                 * options that take numeric values
@@ -1327,8 +1337,14 @@ out_security_failure:
 static int nfs_try_mount(struct nfs_parsed_mount_data *args,
                         struct nfs_fh *root_fh)
 {
-       struct sockaddr *sap = (struct sockaddr *)&args->mount_server.address;
-       char *hostname;
+       struct nfs_mount_request request = {
+               .sap            = (struct sockaddr *)
+                                               &args->mount_server.address,
+               .dirpath        = args->nfs_server.export_path,
+               .protocol       = args->mount_server.protocol,
+               .fh             = root_fh,
+               .noresvport     = args->flags & NFS_MOUNT_NORESVPORT,
+       };
        int status;
 
        if (args->mount_server.version == 0) {
@@ -1337,42 +1353,38 @@ static int nfs_try_mount(struct nfs_parsed_mount_data *args,
                else
                        args->mount_server.version = NFS_MNT_VERSION;
        }
+       request.version = args->mount_server.version;
 
        if (args->mount_server.hostname)
-               hostname = args->mount_server.hostname;
+               request.hostname = args->mount_server.hostname;
        else
-               hostname = args->nfs_server.hostname;
+               request.hostname = args->nfs_server.hostname;
 
        /*
         * Construct the mount server's address.
         */
        if (args->mount_server.address.ss_family == AF_UNSPEC) {
-               memcpy(sap, &args->nfs_server.address,
+               memcpy(request.sap, &args->nfs_server.address,
                       args->nfs_server.addrlen);
                args->mount_server.addrlen = args->nfs_server.addrlen;
        }
+       request.salen = args->mount_server.addrlen;
 
        /*
         * autobind will be used if mount_server.port == 0
         */
-       nfs_set_port(sap, args->mount_server.port);
+       nfs_set_port(request.sap, args->mount_server.port);
 
        /*
         * Now ask the mount server to map our export path
         * to a file handle.
         */
-       status = nfs_mount(sap,
-                          args->mount_server.addrlen,
-                          hostname,
-                          args->nfs_server.export_path,
-                          args->mount_server.version,
-                          args->mount_server.protocol,
-                          root_fh);
+       status = nfs_mount(&request);
        if (status == 0)
                return 0;
 
        dfprintk(MOUNT, "NFS: unable to mount server %s, error %d\n",
-                       hostname, status);
+                       request.hostname, status);
        return status;
 }
 
@@ -2419,7 +2431,7 @@ static void nfs4_kill_super(struct super_block *sb)
 {
        struct nfs_server *server = NFS_SB(sb);
 
-       nfs_return_all_delegations(sb);
+       nfs_super_return_all_delegations(sb);
        kill_anon_super(sb);
 
        nfs4_renewd_prepare_shutdown(server);
index c11f5375d7c11e7c0aa08b0f275310109bfa4f79..04133aacb1e519efe83d9ebe41d7ffe187616845 100644 (file)
@@ -29,8 +29,8 @@
 
 MODULE_LICENSE("GPL");
 
-EXPORT_SYMBOL(nfsacl_encode);
-EXPORT_SYMBOL(nfsacl_decode);
+EXPORT_SYMBOL_GPL(nfsacl_encode);
+EXPORT_SYMBOL_GPL(nfsacl_decode);
 
 struct nfsacl_encode_desc {
        struct xdr_array2_desc desc;
index 094747a1227c18b6ace1726d81ed97487c98169a..6d7d8c02c19711a38d4100f99eb539fb95e4daa4 100644 (file)
@@ -358,6 +358,7 @@ static struct rpc_program cb_program = {
                .nrvers         = ARRAY_SIZE(nfs_cb_version),
                .version        = nfs_cb_version,
                .stats          = &cb_stats,
+               .pipe_dir_name  = "/nfsd4_cb",
 };
 
 /* Reference counting, callback cleanup, etc., all look racy as heck.
@@ -382,8 +383,9 @@ static int do_probe_callback(void *data)
                .program        = &cb_program,
                .prognumber     = cb->cb_prog,
                .version        = nfs_cb_version[1]->number,
-               .authflavor     = RPC_AUTH_UNIX, /* XXX: need AUTH_GSS... */
+               .authflavor     = clp->cl_flavor,
                .flags          = (RPC_CLNT_CREATE_NOPING | RPC_CLNT_CREATE_QUIET),
+               .client_name    = clp->cl_principal,
        };
        struct rpc_message msg = {
                .rpc_proc       = &nfs4_cb_procedures[NFSPROC4_CLNT_CB_NULL],
@@ -392,6 +394,11 @@ static int do_probe_callback(void *data)
        struct rpc_clnt *client;
        int status;
 
+       if (!clp->cl_principal && (clp->cl_flavor >= RPC_AUTH_GSS_KRB5)) {
+               status = nfserr_cb_path_down;
+               goto out_err;
+       }
+
        /* Initialize address */
        memset(&addr, 0, sizeof(addr));
        addr.sin_family = AF_INET;
index bf4cd46a5a11489cfd4d726317362b1cc1c5a381..13e0e074dbb8154aa4cc49a45405478efe929cb7 100644 (file)
@@ -54,6 +54,7 @@
 #include <linux/mutex.h>
 #include <linux/lockd/bind.h>
 #include <linux/module.h>
+#include <linux/sunrpc/svcauth_gss.h>
 
 #define NFSDDBG_FACILITY                NFSDDBG_PROC
 
@@ -377,6 +378,7 @@ free_client(struct nfs4_client *clp)
        shutdown_callback_client(clp);
        if (clp->cl_cred.cr_group_info)
                put_group_info(clp->cl_cred.cr_group_info);
+       kfree(clp->cl_principal);
        kfree(clp->cl_name.data);
        kfree(clp);
 }
@@ -696,6 +698,7 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        unsigned int            strhashval;
        struct nfs4_client      *conf, *unconf, *new;
        __be32                  status;
+       char                    *princ;
        char                    dname[HEXDIR_LEN];
        
        if (!check_name(clname))
@@ -783,6 +786,15 @@ nfsd4_setclientid(struct svc_rqst *rqstp, struct nfsd4_compound_state *cstate,
        }
        copy_verf(new, &clverifier);
        new->cl_addr = sin->sin_addr.s_addr;
+       new->cl_flavor = rqstp->rq_flavor;
+       princ = svc_gss_principal(rqstp);
+       if (princ) {
+               new->cl_principal = kstrdup(princ, GFP_KERNEL);
+               if (new->cl_principal == NULL) {
+                       free_client(new);
+                       goto out;
+               }
+       }
        copy_cred(&new->cl_cred, &rqstp->rq_cred);
        gen_confirm(new);
        gen_callback(new, setclid);
index abb6ac639e8e19740c8bbd30148db72a1028d031..1a9cf78bfce545fe2495fa5fe248297fc038d6ce 100644 (file)
@@ -115,10 +115,20 @@ static inline u64 get_jiffies_64(void)
         ((long)(a) - (long)(b) >= 0))
 #define time_before_eq(a,b)    time_after_eq(b,a)
 
+/*
+ * Calculate whether a is in the range of [b, c].
+ */
 #define time_in_range(a,b,c) \
        (time_after_eq(a,b) && \
         time_before_eq(a,c))
 
+/*
+ * Calculate whether a is in the range of [b, c).
+ */
+#define time_in_range_open(a,b,c) \
+       (time_after_eq(a,b) && \
+        time_before(a,c))
+
 /* Same as above, but does so with platform independent 64bit types.
  * These must be used when utilizing jiffies_64 (i.e. return value of
  * get_jiffies_64() */
index e5872dc994c0a0761d601229ced0d77676756397..fbc48f898521c1a24492c8eb782c12301e341568 100644 (file)
@@ -41,6 +41,7 @@ struct nlmclnt_initdata {
        size_t                  addrlen;
        unsigned short          protocol;
        u32                     nfs_version;
+       int                     noresvport;
 };
 
 /*
index b56d5aa9b194c0d87296b303874d331b84246413..23da3fa69efa9bf261a4e5bad76b31b9bc2258ec 100644 (file)
@@ -49,6 +49,7 @@ struct nlm_host {
        unsigned short          h_proto;        /* transport proto */
        unsigned short          h_reclaiming : 1,
                                h_server     : 1, /* server side, not client side */
+                               h_noresvport : 1,
                                h_inuse      : 1;
        wait_queue_head_t       h_gracewait;    /* wait while reclaiming */
        struct rw_semaphore     h_rwsem;        /* Reboot recovery lock */
@@ -220,7 +221,8 @@ struct nlm_host  *nlmclnt_lookup_host(const struct sockaddr *sap,
                                        const size_t salen,
                                        const unsigned short protocol,
                                        const u32 version,
-                                       const char *hostname);
+                                       const char *hostname,
+                                       int noresvport);
 struct nlm_host  *nlmsvc_lookup_host(const struct svc_rqst *rqstp,
                                        const char *hostname,
                                        const size_t hostname_len);
index 4eaa8347a0d9ce08d09c7bb8436642e92c35fd0c..db867b04ac3c21b66e39793149e121b4569ee41f 100644 (file)
@@ -83,7 +83,7 @@ struct nfs_open_context {
        struct rpc_cred *cred;
        struct nfs4_state *state;
        fl_owner_t lockowner;
-       int mode;
+       fmode_t mode;
 
        unsigned long flags;
 #define NFS_CONTEXT_ERROR_WRITE                (0)
@@ -130,7 +130,10 @@ struct nfs_inode {
         *
         * We need to revalidate the cached attrs for this inode if
         *
-        *      jiffies - read_cache_jiffies > attrtimeo
+        *      jiffies - read_cache_jiffies >= attrtimeo
+        *
+        * Please note the comparison is greater than or equal
+        * so that zero timeout values can be specified.
         */
        unsigned long           read_cache_jiffies;
        unsigned long           attrtimeo;
@@ -180,7 +183,7 @@ struct nfs_inode {
         /* NFSv4 state */
        struct list_head        open_states;
        struct nfs_delegation   *delegation;
-       int                      delegation_state;
+       fmode_t                  delegation_state;
        struct rw_semaphore     rwsem;
 #endif /* CONFIG_NFS_V4*/
        struct inode            vfs_inode;
@@ -342,7 +345,7 @@ extern int nfs_setattr(struct dentry *, struct iattr *);
 extern void nfs_setattr_update_inode(struct inode *inode, struct iattr *attr);
 extern struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx);
 extern void put_nfs_open_context(struct nfs_open_context *ctx);
-extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, int mode);
+extern struct nfs_open_context *nfs_find_open_context(struct inode *inode, struct rpc_cred *cred, fmode_t mode);
 extern u64 nfs_compat_user_ino64(u64 fileid);
 extern void nfs_fattr_init(struct nfs_fattr *fattr);
 
@@ -532,12 +535,6 @@ static inline void nfs3_forget_cached_acls(struct inode *inode)
 }
 #endif /* CONFIG_NFS_V3_ACL */
 
-/*
- * linux/fs/mount_clnt.c
- */
-extern int  nfs_mount(struct sockaddr *, size_t, char *, char *,
-                     int, int, struct nfs_fh *);
-
 /*
  * inline functions
  */
index 4e477ae58699b59a3e4a011bb5447ccce1d5bc4b..9bb81aec91cf859de0591b4fb51b49ba9a622297 100644 (file)
@@ -42,12 +42,6 @@ struct nfs_client {
        struct rb_root          cl_openowner_id;
        struct rb_root          cl_lockowner_id;
 
-       /*
-        * The following rwsem ensures exclusive access to the server
-        * while we recover the state following a lease expiration.
-        */
-       struct rw_semaphore     cl_sem;
-
        struct list_head        cl_delegations;
        struct rb_root          cl_state_owners;
        spinlock_t              cl_lock;
index 6549a06ac16e352c4333509fd1531f1135c122ec..4499016e6d0d85884a0be970877d386976a95f9f 100644 (file)
@@ -45,7 +45,7 @@ struct nfs_mount_data {
        char            context[NFS_MAX_CONTEXT_LEN + 1];       /* 6 */
 };
 
-/* bits in the flags field */
+/* bits in the flags field visible to user space */
 
 #define NFS_MOUNT_SOFT         0x0001  /* 1 */
 #define NFS_MOUNT_INTR         0x0002  /* 1 */ /* now unused, but ABI */
@@ -68,5 +68,6 @@ struct nfs_mount_data {
 /* The following are for internal use only */
 #define NFS_MOUNT_LOOKUP_CACHE_NONEG   0x10000
 #define NFS_MOUNT_LOOKUP_CACHE_NONE    0x20000
+#define NFS_MOUNT_NORESVPORT           0x40000
 
 #endif
index c1c31acb8a2b97608f55902de126fbdf0b0ddd64..a550b528319f9db55eff12b3d84dcee03da4cc0e 100644 (file)
@@ -120,13 +120,14 @@ struct nfs_openargs {
        const struct nfs_fh *   fh;
        struct nfs_seqid *      seqid;
        int                     open_flags;
+       fmode_t                 fmode;
        __u64                   clientid;
        __u64                   id;
        union {
                struct iattr *  attrs;    /* UNCHECKED, GUARDED */
                nfs4_verifier   verifier; /* EXCLUSIVE */
                nfs4_stateid    delegation;             /* CLAIM_DELEGATE_CUR */
-               int             delegation_type;        /* CLAIM_PREVIOUS */
+               fmode_t         delegation_type;        /* CLAIM_PREVIOUS */
        } u;
        const struct qstr *     name;
        const struct nfs_server *server;         /* Needed for ID mapping */
@@ -143,7 +144,7 @@ struct nfs_openres {
        struct nfs_fattr *      dir_attr;
        struct nfs_seqid *      seqid;
        const struct nfs_server *server;
-       int                     delegation_type;
+       fmode_t                 delegation_type;
        nfs4_stateid            delegation;
        __u32                   do_recall;
        __u64                   maxsize;
@@ -171,7 +172,7 @@ struct nfs_closeargs {
        struct nfs_fh *         fh;
        nfs4_stateid *          stateid;
        struct nfs_seqid *      seqid;
-       int                     open_flags;
+       fmode_t                 fmode;
        const u32 *             bitmask;
 };
 
index d0fe2e378452bfb0050b559e58769870b8ce0de4..128298c0362d488e113ab34649dfd3643e319fa3 100644 (file)
@@ -124,6 +124,8 @@ struct nfs4_client {
        nfs4_verifier           cl_verifier;    /* generated by client */
        time_t                  cl_time;        /* time of last lease renewal */
        __be32                  cl_addr;        /* client ipaddress */
+       u32                     cl_flavor;      /* setclientid pseudoflavor */
+       char                    *cl_principal;  /* setclientid principal name */
        struct svc_cred         cl_cred;        /* setclientid principal */
        clientid_t              cl_clientid;    /* generated by server */
        nfs4_verifier           cl_confirm;     /* generated by server */
index 6f0ee1b84a4f5f64903f4c74af1004381c3d9dd1..c39a21040dcb64aabce55d51db9a56d52f62c289 100644 (file)
@@ -58,6 +58,7 @@ struct rpc_clnt {
        struct rpc_timeout      cl_timeout_default;
        struct rpc_program *    cl_program;
        char                    cl_inline_name[32];
+       char                    *cl_principal;  /* target to authenticate to */
 };
 
 /*
@@ -108,6 +109,7 @@ struct rpc_create_args {
        u32                     version;
        rpc_authflavor_t        authflavor;
        unsigned long           flags;
+       char                    *client_name;
 };
 
 /* Values for "flags" field */
index 51b977a4ca203eee00cb7b44f769a29c710064d5..cea764c2359fd743bd2d2342fd71492aabdd02a3 100644 (file)
@@ -15,6 +15,7 @@ struct rpc_pipe_ops {
        ssize_t (*upcall)(struct file *, struct rpc_pipe_msg *, char __user *, size_t);
        ssize_t (*downcall)(struct file *, const char __user *, size_t);
        void (*release_pipe)(struct inode *);
+       int (*open_pipe)(struct inode *);
        void (*destroy_msg)(struct rpc_pipe_msg *);
 };
 
index c9165d9771a800b5d2df9053a777cc2ca30468e2..ca7d725861fc0e885e396d50a5df9b895c7465ef 100644 (file)
@@ -20,6 +20,7 @@ int gss_svc_init(void);
 void gss_svc_shutdown(void);
 int svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name);
 u32 svcauth_gss_flavor(struct auth_domain *dom);
+char *svc_gss_principal(struct svc_rqst *);
 
 #endif /* __KERNEL__ */
 #endif /* _LINUX_SUNRPC_SVCAUTH_GSS_H */
index e4057d729f036c8a3b80e3100a704e03ef0c6d4a..49e1eb4544654630fce2a28ff539b97c1b6bb07c 100644 (file)
@@ -36,21 +36,6 @@ struct xdr_netobj {
  */
 typedef int    (*kxdrproc_t)(void *rqstp, __be32 *data, void *obj);
 
-/*
- * We're still requiring the BKL in the xdr code until it's been
- * more carefully audited, at which point this wrapper will become
- * unnecessary.
- */
-static inline int rpc_call_xdrproc(kxdrproc_t xdrproc, void *rqstp, __be32 *data, void *obj)
-{
-       int ret;
-
-       lock_kernel();
-       ret = xdrproc(rqstp, data, obj);
-       unlock_kernel();
-       return ret;
-}
-
 /*
  * Basic structure for transmission/reception of a client XDR message.
  * Features a header (for a linear buffer containing RPC headers
index 4d80a118d5383d073d64609c84a91249921d236c..11fc71d50c1e8aa0806eb7d9ce24cbfd291831eb 100644 (file)
@@ -76,8 +76,7 @@ struct rpc_rqst {
        struct list_head        rq_list;
 
        __u32 *                 rq_buffer;      /* XDR encode buffer */
-       size_t                  rq_bufsize,
-                               rq_callsize,
+       size_t                  rq_callsize,
                                rq_rcvsize;
 
        struct xdr_buf          rq_private_buf;         /* The receive buffer
index 0443f83494585a634dad0f0b55c75bd2401b7d4b..0c431c277af5be352e3c65b9fa4de99e25086e9b 100644 (file)
@@ -234,7 +234,7 @@ rpcauth_prune_expired(struct list_head *free, int nr_to_scan)
        list_for_each_entry_safe(cred, next, &cred_unused, cr_lru) {
 
                /* Enforce a 60 second garbage collection moratorium */
-               if (time_in_range(cred->cr_expire, expired, jiffies) &&
+               if (time_in_range_open(cred->cr_expire, expired, jiffies) &&
                    test_bit(RPCAUTH_CRED_HASHED, &cred->cr_flags) != 0)
                        continue;
 
@@ -515,7 +515,7 @@ rpcauth_wrap_req(struct rpc_task *task, kxdrproc_t encode, void *rqstp,
        if (cred->cr_ops->crwrap_req)
                return cred->cr_ops->crwrap_req(task, encode, rqstp, data, obj);
        /* By default, we encode the arguments normally. */
-       return rpc_call_xdrproc(encode, rqstp, data, obj);
+       return encode(rqstp, data, obj);
 }
 
 int
@@ -530,7 +530,7 @@ rpcauth_unwrap_resp(struct rpc_task *task, kxdrproc_t decode, void *rqstp,
                return cred->cr_ops->crunwrap_resp(task, decode, rqstp,
                                                   data, obj);
        /* By default, we decode the arguments normally. */
-       return rpc_call_xdrproc(decode, rqstp, data, obj);
+       return decode(rqstp, data, obj);
 }
 
 int
index 853a4142cea105bba2706a96094e7a8e2793d84e..e630b38a604718a57c80a93e7556452859cf35c1 100644 (file)
@@ -72,11 +72,25 @@ struct gss_auth {
        struct gss_api_mech *mech;
        enum rpc_gss_svc service;
        struct rpc_clnt *client;
-       struct dentry *dentry;
+       /*
+        * There are two upcall pipes; dentry[1], named "gssd", is used
+        * for the new text-based upcall; dentry[0] is named after the
+        * mechanism (for example, "krb5") and exists for
+        * backwards-compatibility with older gssd's.
+        */
+       struct dentry *dentry[2];
 };
 
+/* pipe_version >= 0 if and only if someone has a pipe open. */
+static int pipe_version = -1;
+static atomic_t pipe_users = ATOMIC_INIT(0);
+static DEFINE_SPINLOCK(pipe_version_lock);
+static struct rpc_wait_queue pipe_version_rpc_waitqueue;
+static DECLARE_WAIT_QUEUE_HEAD(pipe_version_waitqueue);
+
 static void gss_free_ctx(struct gss_cl_ctx *);
-static struct rpc_pipe_ops gss_upcall_ops;
+static struct rpc_pipe_ops gss_upcall_ops_v0;
+static struct rpc_pipe_ops gss_upcall_ops_v1;
 
 static inline struct gss_cl_ctx *
 gss_get_ctx(struct gss_cl_ctx *ctx)
@@ -220,6 +234,7 @@ err:
        return p;
 }
 
+#define UPCALL_BUF_LEN 128
 
 struct gss_upcall_msg {
        atomic_t count;
@@ -227,16 +242,41 @@ struct gss_upcall_msg {
        struct rpc_pipe_msg msg;
        struct list_head list;
        struct gss_auth *auth;
+       struct rpc_inode *inode;
        struct rpc_wait_queue rpc_waitqueue;
        wait_queue_head_t waitqueue;
        struct gss_cl_ctx *ctx;
+       char databuf[UPCALL_BUF_LEN];
 };
 
+static int get_pipe_version(void)
+{
+       int ret;
+
+       spin_lock(&pipe_version_lock);
+       if (pipe_version >= 0) {
+               atomic_inc(&pipe_users);
+               ret = pipe_version;
+       } else
+               ret = -EAGAIN;
+       spin_unlock(&pipe_version_lock);
+       return ret;
+}
+
+static void put_pipe_version(void)
+{
+       if (atomic_dec_and_lock(&pipe_users, &pipe_version_lock)) {
+               pipe_version = -1;
+               spin_unlock(&pipe_version_lock);
+       }
+}
+
 static void
 gss_release_msg(struct gss_upcall_msg *gss_msg)
 {
        if (!atomic_dec_and_test(&gss_msg->count))
                return;
+       put_pipe_version();
        BUG_ON(!list_empty(&gss_msg->list));
        if (gss_msg->ctx != NULL)
                gss_put_ctx(gss_msg->ctx);
@@ -266,8 +306,8 @@ __gss_find_upcall(struct rpc_inode *rpci, uid_t uid)
 static inline struct gss_upcall_msg *
 gss_add_msg(struct gss_auth *gss_auth, struct gss_upcall_msg *gss_msg)
 {
-       struct inode *inode = gss_auth->dentry->d_inode;
-       struct rpc_inode *rpci = RPC_I(inode);
+       struct rpc_inode *rpci = gss_msg->inode;
+       struct inode *inode = &rpci->vfs_inode;
        struct gss_upcall_msg *old;
 
        spin_lock(&inode->i_lock);
@@ -293,8 +333,7 @@ __gss_unhash_msg(struct gss_upcall_msg *gss_msg)
 static void
 gss_unhash_msg(struct gss_upcall_msg *gss_msg)
 {
-       struct gss_auth *gss_auth = gss_msg->auth;
-       struct inode *inode = gss_auth->dentry->d_inode;
+       struct inode *inode = &gss_msg->inode->vfs_inode;
 
        if (list_empty(&gss_msg->list))
                return;
@@ -310,7 +349,7 @@ gss_upcall_callback(struct rpc_task *task)
        struct gss_cred *gss_cred = container_of(task->tk_msg.rpc_cred,
                        struct gss_cred, gc_base);
        struct gss_upcall_msg *gss_msg = gss_cred->gc_upcall;
-       struct inode *inode = gss_msg->auth->dentry->d_inode;
+       struct inode *inode = &gss_msg->inode->vfs_inode;
 
        spin_lock(&inode->i_lock);
        if (gss_msg->ctx)
@@ -323,22 +362,75 @@ gss_upcall_callback(struct rpc_task *task)
        gss_release_msg(gss_msg);
 }
 
+static void gss_encode_v0_msg(struct gss_upcall_msg *gss_msg)
+{
+       gss_msg->msg.data = &gss_msg->uid;
+       gss_msg->msg.len = sizeof(gss_msg->uid);
+}
+
+static void gss_encode_v1_msg(struct gss_upcall_msg *gss_msg,
+                               struct rpc_clnt *clnt, int machine_cred)
+{
+       char *p = gss_msg->databuf;
+       int len = 0;
+
+       gss_msg->msg.len = sprintf(gss_msg->databuf, "mech=%s uid=%d ",
+                                  gss_msg->auth->mech->gm_name,
+                                  gss_msg->uid);
+       p += gss_msg->msg.len;
+       if (clnt->cl_principal) {
+               len = sprintf(p, "target=%s ", clnt->cl_principal);
+               p += len;
+               gss_msg->msg.len += len;
+       }
+       if (machine_cred) {
+               len = sprintf(p, "service=* ");
+               p += len;
+               gss_msg->msg.len += len;
+       } else if (!strcmp(clnt->cl_program->name, "nfs4_cb")) {
+               len = sprintf(p, "service=nfs ");
+               p += len;
+               gss_msg->msg.len += len;
+       }
+       len = sprintf(p, "\n");
+       gss_msg->msg.len += len;
+
+       gss_msg->msg.data = gss_msg->databuf;
+       BUG_ON(gss_msg->msg.len > UPCALL_BUF_LEN);
+}
+
+static void gss_encode_msg(struct gss_upcall_msg *gss_msg,
+                               struct rpc_clnt *clnt, int machine_cred)
+{
+       if (pipe_version == 0)
+               gss_encode_v0_msg(gss_msg);
+       else /* pipe_version == 1 */
+               gss_encode_v1_msg(gss_msg, clnt, machine_cred);
+}
+
 static inline struct gss_upcall_msg *
-gss_alloc_msg(struct gss_auth *gss_auth, uid_t uid)
+gss_alloc_msg(struct gss_auth *gss_auth, uid_t uid, struct rpc_clnt *clnt,
+               int machine_cred)
 {
        struct gss_upcall_msg *gss_msg;
+       int vers;
 
        gss_msg = kzalloc(sizeof(*gss_msg), GFP_NOFS);
-       if (gss_msg != NULL) {
-               INIT_LIST_HEAD(&gss_msg->list);
-               rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");
-               init_waitqueue_head(&gss_msg->waitqueue);
-               atomic_set(&gss_msg->count, 1);
-               gss_msg->msg.data = &gss_msg->uid;
-               gss_msg->msg.len = sizeof(gss_msg->uid);
-               gss_msg->uid = uid;
-               gss_msg->auth = gss_auth;
+       if (gss_msg == NULL)
+               return ERR_PTR(-ENOMEM);
+       vers = get_pipe_version();
+       if (vers < 0) {
+               kfree(gss_msg);
+               return ERR_PTR(vers);
        }
+       gss_msg->inode = RPC_I(gss_auth->dentry[vers]->d_inode);
+       INIT_LIST_HEAD(&gss_msg->list);
+       rpc_init_wait_queue(&gss_msg->rpc_waitqueue, "RPCSEC_GSS upcall waitq");
+       init_waitqueue_head(&gss_msg->waitqueue);
+       atomic_set(&gss_msg->count, 1);
+       gss_msg->uid = uid;
+       gss_msg->auth = gss_auth;
+       gss_encode_msg(gss_msg, clnt, machine_cred);
        return gss_msg;
 }
 
@@ -350,16 +442,13 @@ gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cr
        struct gss_upcall_msg *gss_new, *gss_msg;
        uid_t uid = cred->cr_uid;
 
-       /* Special case: rpc.gssd assumes that uid == 0 implies machine creds */
-       if (gss_cred->gc_machine_cred != 0)
-               uid = 0;
-
-       gss_new = gss_alloc_msg(gss_auth, uid);
-       if (gss_new == NULL)
-               return ERR_PTR(-ENOMEM);
+       gss_new = gss_alloc_msg(gss_auth, uid, clnt, gss_cred->gc_machine_cred);
+       if (IS_ERR(gss_new))
+               return gss_new;
        gss_msg = gss_add_msg(gss_auth, gss_new);
        if (gss_msg == gss_new) {
-               int res = rpc_queue_upcall(gss_auth->dentry->d_inode, &gss_new->msg);
+               struct inode *inode = &gss_new->inode->vfs_inode;
+               int res = rpc_queue_upcall(inode, &gss_new->msg);
                if (res) {
                        gss_unhash_msg(gss_new);
                        gss_msg = ERR_PTR(res);
@@ -369,6 +458,18 @@ gss_setup_upcall(struct rpc_clnt *clnt, struct gss_auth *gss_auth, struct rpc_cr
        return gss_msg;
 }
 
+static void warn_gssd(void)
+{
+       static unsigned long ratelimit;
+       unsigned long now = jiffies;
+
+       if (time_after(now, ratelimit)) {
+               printk(KERN_WARNING "RPC: AUTH_GSS upcall timed out.\n"
+                               "Please check user daemon is running.\n");
+               ratelimit = now + 15*HZ;
+       }
+}
+
 static inline int
 gss_refresh_upcall(struct rpc_task *task)
 {
@@ -378,16 +479,25 @@ gss_refresh_upcall(struct rpc_task *task)
        struct gss_cred *gss_cred = container_of(cred,
                        struct gss_cred, gc_base);
        struct gss_upcall_msg *gss_msg;
-       struct inode *inode = gss_auth->dentry->d_inode;
+       struct inode *inode;
        int err = 0;
 
        dprintk("RPC: %5u gss_refresh_upcall for uid %u\n", task->tk_pid,
                                                                cred->cr_uid);
        gss_msg = gss_setup_upcall(task->tk_client, gss_auth, cred);
+       if (IS_ERR(gss_msg) == -EAGAIN) {
+               /* XXX: warning on the first, under the assumption we
+                * shouldn't normally hit this case on a refresh. */
+               warn_gssd();
+               task->tk_timeout = 15*HZ;
+               rpc_sleep_on(&pipe_version_rpc_waitqueue, task, NULL);
+               return 0;
+       }
        if (IS_ERR(gss_msg)) {
                err = PTR_ERR(gss_msg);
                goto out;
        }
+       inode = &gss_msg->inode->vfs_inode;
        spin_lock(&inode->i_lock);
        if (gss_cred->gc_upcall != NULL)
                rpc_sleep_on(&gss_cred->gc_upcall->rpc_waitqueue, task, NULL);
@@ -414,18 +524,29 @@ out:
 static inline int
 gss_create_upcall(struct gss_auth *gss_auth, struct gss_cred *gss_cred)
 {
-       struct inode *inode = gss_auth->dentry->d_inode;
+       struct inode *inode;
        struct rpc_cred *cred = &gss_cred->gc_base;
        struct gss_upcall_msg *gss_msg;
        DEFINE_WAIT(wait);
        int err = 0;
 
        dprintk("RPC:       gss_upcall for uid %u\n", cred->cr_uid);
+retry:
        gss_msg = gss_setup_upcall(gss_auth->client, gss_auth, cred);
+       if (PTR_ERR(gss_msg) == -EAGAIN) {
+               err = wait_event_interruptible_timeout(pipe_version_waitqueue,
+                               pipe_version >= 0, 15*HZ);
+               if (err)
+                       goto out;
+               if (pipe_version < 0)
+                       warn_gssd();
+               goto retry;
+       }
        if (IS_ERR(gss_msg)) {
                err = PTR_ERR(gss_msg);
                goto out;
        }
+       inode = &gss_msg->inode->vfs_inode;
        for (;;) {
                prepare_to_wait(&gss_msg->waitqueue, &wait, TASK_INTERRUPTIBLE);
                spin_lock(&inode->i_lock);
@@ -543,6 +664,38 @@ out:
        return err;
 }
 
+static int gss_pipe_open(struct inode *inode, int new_version)
+{
+       int ret = 0;
+
+       spin_lock(&pipe_version_lock);
+       if (pipe_version < 0) {
+               /* First open of any gss pipe determines the version: */
+               pipe_version = new_version;
+               rpc_wake_up(&pipe_version_rpc_waitqueue);
+               wake_up(&pipe_version_waitqueue);
+       } else if (pipe_version != new_version) {
+               /* Trying to open a pipe of a different version */
+               ret = -EBUSY;
+               goto out;
+       }
+       atomic_inc(&pipe_users);
+out:
+       spin_unlock(&pipe_version_lock);
+       return ret;
+
+}
+
+static int gss_pipe_open_v0(struct inode *inode)
+{
+       return gss_pipe_open(inode, 0);
+}
+
+static int gss_pipe_open_v1(struct inode *inode)
+{
+       return gss_pipe_open(inode, 1);
+}
+
 static void
 gss_pipe_release(struct inode *inode)
 {
@@ -562,27 +715,22 @@ gss_pipe_release(struct inode *inode)
                spin_lock(&inode->i_lock);
        }
        spin_unlock(&inode->i_lock);
+
+       put_pipe_version();
 }
 
 static void
 gss_pipe_destroy_msg(struct rpc_pipe_msg *msg)
 {
        struct gss_upcall_msg *gss_msg = container_of(msg, struct gss_upcall_msg, msg);
-       static unsigned long ratelimit;
 
        if (msg->errno < 0) {
                dprintk("RPC:       gss_pipe_destroy_msg releasing msg %p\n",
                                gss_msg);
                atomic_inc(&gss_msg->count);
                gss_unhash_msg(gss_msg);
-               if (msg->errno == -ETIMEDOUT) {
-                       unsigned long now = jiffies;
-                       if (time_after(now, ratelimit)) {
-                               printk(KERN_WARNING "RPC: AUTH_GSS upcall timed out.\n"
-                                                   "Please check user daemon is running!\n");
-                               ratelimit = now + 15*HZ;
-                       }
-               }
+               if (msg->errno == -ETIMEDOUT)
+                       warn_gssd();
                gss_release_msg(gss_msg);
        }
 }
@@ -623,20 +771,38 @@ gss_create(struct rpc_clnt *clnt, rpc_authflavor_t flavor)
        atomic_set(&auth->au_count, 1);
        kref_init(&gss_auth->kref);
 
-       gss_auth->dentry = rpc_mkpipe(clnt->cl_dentry, gss_auth->mech->gm_name,
-                       clnt, &gss_upcall_ops, RPC_PIPE_WAIT_FOR_OPEN);
-       if (IS_ERR(gss_auth->dentry)) {
-               err = PTR_ERR(gss_auth->dentry);
+       /*
+        * Note: if we created the old pipe first, then someone who
+        * examined the directory at the right moment might conclude
+        * that we supported only the old pipe.  So we instead create
+        * the new pipe first.
+        */
+       gss_auth->dentry[1] = rpc_mkpipe(clnt->cl_dentry,
+                                        "gssd",
+                                        clnt, &gss_upcall_ops_v1,
+                                        RPC_PIPE_WAIT_FOR_OPEN);
+       if (IS_ERR(gss_auth->dentry[1])) {
+               err = PTR_ERR(gss_auth->dentry[1]);
                goto err_put_mech;
        }
 
+       gss_auth->dentry[0] = rpc_mkpipe(clnt->cl_dentry,
+                                        gss_auth->mech->gm_name,
+                                        clnt, &gss_upcall_ops_v0,
+                                        RPC_PIPE_WAIT_FOR_OPEN);
+       if (IS_ERR(gss_auth->dentry[0])) {
+               err = PTR_ERR(gss_auth->dentry[0]);
+               goto err_unlink_pipe_1;
+       }
        err = rpcauth_init_credcache(auth);
        if (err)
-               goto err_unlink_pipe;
+               goto err_unlink_pipe_0;
 
        return auth;
-err_unlink_pipe:
-       rpc_unlink(gss_auth->dentry);
+err_unlink_pipe_0:
+       rpc_unlink(gss_auth->dentry[0]);
+err_unlink_pipe_1:
+       rpc_unlink(gss_auth->dentry[1]);
 err_put_mech:
        gss_mech_put(gss_auth->mech);
 err_free:
@@ -649,8 +815,8 @@ out_dec:
 static void
 gss_free(struct gss_auth *gss_auth)
 {
-       rpc_unlink(gss_auth->dentry);
-       gss_auth->dentry = NULL;
+       rpc_unlink(gss_auth->dentry[1]);
+       rpc_unlink(gss_auth->dentry[0]);
        gss_mech_put(gss_auth->mech);
 
        kfree(gss_auth);
@@ -693,7 +859,7 @@ gss_destroying_context(struct rpc_cred *cred)
        struct rpc_task *task;
 
        if (gss_cred->gc_ctx == NULL ||
-           test_and_clear_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0)
+           test_bit(RPCAUTH_CRED_UPTODATE, &cred->cr_flags) == 0)
                return 0;
 
        gss_cred->gc_ctx->gc_proc = RPC_GSS_PROC_DESTROY;
@@ -757,14 +923,12 @@ gss_free_cred_callback(struct rcu_head *head)
 }
 
 static void
-gss_destroy_cred(struct rpc_cred *cred)
+gss_destroy_nullcred(struct rpc_cred *cred)
 {
        struct gss_cred *gss_cred = container_of(cred, struct gss_cred, gc_base);
        struct gss_auth *gss_auth = container_of(cred->cr_auth, struct gss_auth, rpc_auth);
        struct gss_cl_ctx *ctx = gss_cred->gc_ctx;
 
-       if (gss_destroying_context(cred))
-               return;
        rcu_assign_pointer(gss_cred->gc_ctx, NULL);
        call_rcu(&cred->cr_rcu, gss_free_cred_callback);
        if (ctx)
@@ -772,6 +936,15 @@ gss_destroy_cred(struct rpc_cred *cred)
        kref_put(&gss_auth->kref, gss_free_callback);
 }
 
+static void
+gss_destroy_cred(struct rpc_cred *cred)
+{
+
+       if (gss_destroying_context(cred))
+               return;
+       gss_destroy_nullcred(cred);
+}
+
 /*
  * Lookup RPCSEC_GSS cred for the current process
  */
@@ -1017,7 +1190,7 @@ gss_wrap_req_integ(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
        offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
        *p++ = htonl(rqstp->rq_seqno);
 
-       status = rpc_call_xdrproc(encode, rqstp, p, obj);
+       status = encode(rqstp, p, obj);
        if (status)
                return status;
 
@@ -1111,7 +1284,7 @@ gss_wrap_req_priv(struct rpc_cred *cred, struct gss_cl_ctx *ctx,
        offset = (u8 *)p - (u8 *)snd_buf->head[0].iov_base;
        *p++ = htonl(rqstp->rq_seqno);
 
-       status = rpc_call_xdrproc(encode, rqstp, p, obj);
+       status = encode(rqstp, p, obj);
        if (status)
                return status;
 
@@ -1170,12 +1343,12 @@ gss_wrap_req(struct rpc_task *task,
                /* The spec seems a little ambiguous here, but I think that not
                 * wrapping context destruction requests makes the most sense.
                 */
-               status = rpc_call_xdrproc(encode, rqstp, p, obj);
+               status = encode(rqstp, p, obj);
                goto out;
        }
        switch (gss_cred->gc_service) {
                case RPC_GSS_SVC_NONE:
-                       status = rpc_call_xdrproc(encode, rqstp, p, obj);
+                       status = encode(rqstp, p, obj);
                        break;
                case RPC_GSS_SVC_INTEGRITY:
                        status = gss_wrap_req_integ(cred, ctx, encode,
@@ -1291,7 +1464,7 @@ gss_unwrap_resp(struct rpc_task *task,
        cred->cr_auth->au_rslack = cred->cr_auth->au_verfsize + (p - savedp)
                                                + (savedlen - head->iov_len);
 out_decode:
-       status = rpc_call_xdrproc(decode, rqstp, p, obj);
+       status = decode(rqstp, p, obj);
 out:
        gss_put_ctx(ctx);
        dprintk("RPC: %5u gss_unwrap_resp returning %d\n", task->tk_pid,
@@ -1324,7 +1497,7 @@ static const struct rpc_credops gss_credops = {
 
 static const struct rpc_credops gss_nullops = {
        .cr_name        = "AUTH_GSS",
-       .crdestroy      = gss_destroy_cred,
+       .crdestroy      = gss_destroy_nullcred,
        .crbind         = rpcauth_generic_bind_cred,
        .crmatch        = gss_match,
        .crmarshal      = gss_marshal,
@@ -1334,10 +1507,19 @@ static const struct rpc_credops gss_nullops = {
        .crunwrap_resp  = gss_unwrap_resp,
 };
 
-static struct rpc_pipe_ops gss_upcall_ops = {
+static struct rpc_pipe_ops gss_upcall_ops_v0 = {
+       .upcall         = gss_pipe_upcall,
+       .downcall       = gss_pipe_downcall,
+       .destroy_msg    = gss_pipe_destroy_msg,
+       .open_pipe      = gss_pipe_open_v0,
+       .release_pipe   = gss_pipe_release,
+};
+
+static struct rpc_pipe_ops gss_upcall_ops_v1 = {
        .upcall         = gss_pipe_upcall,
        .downcall       = gss_pipe_downcall,
        .destroy_msg    = gss_pipe_destroy_msg,
+       .open_pipe      = gss_pipe_open_v1,
        .release_pipe   = gss_pipe_release,
 };
 
@@ -1354,6 +1536,7 @@ static int __init init_rpcsec_gss(void)
        err = gss_svc_init();
        if (err)
                goto out_unregister;
+       rpc_init_wait_queue(&pipe_version_rpc_waitqueue, "gss pipe version");
        return 0;
 out_unregister:
        rpcauth_unregister(&authgss_ops);
index d83b881685fe52d5c5e22e53c6c394f4a0fc4245..c0ba39c4f5f220de70711e92e350c6aa64002e9a 100644 (file)
@@ -152,7 +152,7 @@ g_token_size(struct xdr_netobj *mech, unsigned int body_size)
        return(1 + der_length_size(body_size) + body_size);
 }
 
-EXPORT_SYMBOL(g_token_size);
+EXPORT_SYMBOL_GPL(g_token_size);
 
 /* fills in a buffer with the token header.  The buffer is assumed to
    be the right size.  buf is advanced past the token header */
@@ -167,7 +167,7 @@ g_make_token_header(struct xdr_netobj *mech, int body_size, unsigned char **buf)
        TWRITE_STR(*buf, mech->data, ((int) mech->len));
 }
 
-EXPORT_SYMBOL(g_make_token_header);
+EXPORT_SYMBOL_GPL(g_make_token_header);
 
 /*
  * Given a buffer containing a token, reads and verifies the token,
@@ -231,5 +231,5 @@ g_verify_token_header(struct xdr_netobj *mech, int *body_size,
        return(ret);
 }
 
-EXPORT_SYMBOL(g_verify_token_header);
+EXPORT_SYMBOL_GPL(g_verify_token_header);
 
index bce9d527af08187c5ff5c590acc43464483fe90a..6efbb0cd3c7cd03d18b610fa3ee7a42a5ba97a5e 100644 (file)
@@ -117,7 +117,7 @@ gss_mech_register(struct gss_api_mech *gm)
        return 0;
 }
 
-EXPORT_SYMBOL(gss_mech_register);
+EXPORT_SYMBOL_GPL(gss_mech_register);
 
 void
 gss_mech_unregister(struct gss_api_mech *gm)
@@ -129,7 +129,7 @@ gss_mech_unregister(struct gss_api_mech *gm)
        gss_mech_free(gm);
 }
 
-EXPORT_SYMBOL(gss_mech_unregister);
+EXPORT_SYMBOL_GPL(gss_mech_unregister);
 
 struct gss_api_mech *
 gss_mech_get(struct gss_api_mech *gm)
@@ -138,7 +138,7 @@ gss_mech_get(struct gss_api_mech *gm)
        return gm;
 }
 
-EXPORT_SYMBOL(gss_mech_get);
+EXPORT_SYMBOL_GPL(gss_mech_get);
 
 struct gss_api_mech *
 gss_mech_get_by_name(const char *name)
@@ -158,7 +158,7 @@ gss_mech_get_by_name(const char *name)
 
 }
 
-EXPORT_SYMBOL(gss_mech_get_by_name);
+EXPORT_SYMBOL_GPL(gss_mech_get_by_name);
 
 static inline int
 mech_supports_pseudoflavor(struct gss_api_mech *gm, u32 pseudoflavor)
@@ -191,7 +191,7 @@ gss_mech_get_by_pseudoflavor(u32 pseudoflavor)
        return gm;
 }
 
-EXPORT_SYMBOL(gss_mech_get_by_pseudoflavor);
+EXPORT_SYMBOL_GPL(gss_mech_get_by_pseudoflavor);
 
 u32
 gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service)
@@ -205,7 +205,7 @@ gss_svc_to_pseudoflavor(struct gss_api_mech *gm, u32 service)
        }
        return RPC_AUTH_MAXFLAVOR; /* illegal value */
 }
-EXPORT_SYMBOL(gss_svc_to_pseudoflavor);
+EXPORT_SYMBOL_GPL(gss_svc_to_pseudoflavor);
 
 u32
 gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
@@ -219,7 +219,7 @@ gss_pseudoflavor_to_service(struct gss_api_mech *gm, u32 pseudoflavor)
        return 0;
 }
 
-EXPORT_SYMBOL(gss_pseudoflavor_to_service);
+EXPORT_SYMBOL_GPL(gss_pseudoflavor_to_service);
 
 char *
 gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
@@ -233,7 +233,7 @@ gss_service_to_auth_domain_name(struct gss_api_mech *gm, u32 service)
        return NULL;
 }
 
-EXPORT_SYMBOL(gss_service_to_auth_domain_name);
+EXPORT_SYMBOL_GPL(gss_service_to_auth_domain_name);
 
 void
 gss_mech_put(struct gss_api_mech * gm)
@@ -242,7 +242,7 @@ gss_mech_put(struct gss_api_mech * gm)
                module_put(gm->gm_owner);
 }
 
-EXPORT_SYMBOL(gss_mech_put);
+EXPORT_SYMBOL_GPL(gss_mech_put);
 
 /* The mech could probably be determined from the token instead, but it's just
  * as easy for now to pass it in. */
index 81ae3d62a0cc9010d4cc1b6e5de130ae64ca2a57..2278a50c64449ce35beca9f87ca9cf3ce248d5d3 100644 (file)
@@ -332,6 +332,7 @@ struct rsc {
        struct svc_cred         cred;
        struct gss_svc_seq_data seqdata;
        struct gss_ctx          *mechctx;
+       char                    *client_name;
 };
 
 static struct cache_head *rsc_table[RSC_HASHMAX];
@@ -346,6 +347,7 @@ static void rsc_free(struct rsc *rsci)
                gss_delete_sec_context(&rsci->mechctx);
        if (rsci->cred.cr_group_info)
                put_group_info(rsci->cred.cr_group_info);
+       kfree(rsci->client_name);
 }
 
 static void rsc_put(struct kref *ref)
@@ -383,6 +385,7 @@ rsc_init(struct cache_head *cnew, struct cache_head *ctmp)
        tmp->handle.data = NULL;
        new->mechctx = NULL;
        new->cred.cr_group_info = NULL;
+       new->client_name = NULL;
 }
 
 static void
@@ -397,6 +400,8 @@ update_rsc(struct cache_head *cnew, struct cache_head *ctmp)
        spin_lock_init(&new->seqdata.sd_lock);
        new->cred = tmp->cred;
        tmp->cred.cr_group_info = NULL;
+       new->client_name = tmp->client_name;
+       tmp->client_name = NULL;
 }
 
 static struct cache_head *
@@ -486,6 +491,15 @@ static int rsc_parse(struct cache_detail *cd,
                status = gss_import_sec_context(buf, len, gm, &rsci.mechctx);
                if (status)
                        goto out;
+
+               /* get client name */
+               len = qword_get(&mesg, buf, mlen);
+               if (len > 0) {
+                       rsci.client_name = kstrdup(buf, GFP_KERNEL);
+                       if (!rsci.client_name)
+                               goto out;
+               }
+
        }
        rsci.h.expiry_time = expiry;
        rscp = rsc_update(&rsci, rscp);
@@ -746,7 +760,7 @@ u32 svcauth_gss_flavor(struct auth_domain *dom)
        return gd->pseudoflavor;
 }
 
-EXPORT_SYMBOL(svcauth_gss_flavor);
+EXPORT_SYMBOL_GPL(svcauth_gss_flavor);
 
 int
 svcauth_gss_register_pseudoflavor(u32 pseudoflavor, char * name)
@@ -780,7 +794,7 @@ out:
        return stat;
 }
 
-EXPORT_SYMBOL(svcauth_gss_register_pseudoflavor);
+EXPORT_SYMBOL_GPL(svcauth_gss_register_pseudoflavor);
 
 static inline int
 read_u32_from_xdr_buf(struct xdr_buf *buf, int base, u32 *obj)
@@ -913,6 +927,16 @@ struct gss_svc_data {
        struct rsc                      *rsci;
 };
 
+char *svc_gss_principal(struct svc_rqst *rqstp)
+{
+       struct gss_svc_data *gd = (struct gss_svc_data *)rqstp->rq_auth_data;
+
+       if (gd && gd->rsci)
+               return gd->rsci->client_name;
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(svc_gss_principal);
+
 static int
 svcauth_gss_set_client(struct svc_rqst *rqstp)
 {
index 3ca518386d15d33959ea12a0fc1315d1e8499b98..836f15c0c4a3489b6e9d5ba5a7bfc59514a0db3d 100644 (file)
@@ -197,6 +197,12 @@ static struct rpc_clnt * rpc_new_client(const struct rpc_create_args *args, stru
 
        clnt->cl_rtt = &clnt->cl_rtt_default;
        rpc_init_rtt(&clnt->cl_rtt_default, clnt->cl_timeout->to_initval);
+       clnt->cl_principal = NULL;
+       if (args->client_name) {
+               clnt->cl_principal = kstrdup(args->client_name, GFP_KERNEL);
+               if (!clnt->cl_principal)
+                       goto out_no_principal;
+       }
 
        kref_init(&clnt->cl_kref);
 
@@ -226,6 +232,8 @@ out_no_auth:
                rpc_put_mount();
        }
 out_no_path:
+       kfree(clnt->cl_principal);
+out_no_principal:
        rpc_free_iostats(clnt->cl_metrics);
 out_no_stats:
        if (clnt->cl_server != clnt->cl_inline_name)
@@ -354,6 +362,11 @@ rpc_clone_client(struct rpc_clnt *clnt)
        new->cl_metrics = rpc_alloc_iostats(clnt);
        if (new->cl_metrics == NULL)
                goto out_no_stats;
+       if (clnt->cl_principal) {
+               new->cl_principal = kstrdup(clnt->cl_principal, GFP_KERNEL);
+               if (new->cl_principal == NULL)
+                       goto out_no_principal;
+       }
        kref_init(&new->cl_kref);
        err = rpc_setup_pipedir(new, clnt->cl_program->pipe_dir_name);
        if (err != 0)
@@ -366,6 +379,8 @@ rpc_clone_client(struct rpc_clnt *clnt)
        rpciod_up();
        return new;
 out_no_path:
+       kfree(new->cl_principal);
+out_no_principal:
        rpc_free_iostats(new->cl_metrics);
 out_no_stats:
        kfree(new);
@@ -417,6 +432,7 @@ rpc_free_client(struct kref *kref)
 out_free:
        rpc_unregister_client(clnt);
        rpc_free_iostats(clnt->cl_metrics);
+       kfree(clnt->cl_principal);
        clnt->cl_metrics = NULL;
        xprt_put(clnt->cl_xprt);
        rpciod_down();
index 23a2b8f6dc495094ec7c8de5a48c06bad2c3d4b8..19245324887011f358adab5baa5a28d38811549f 100644 (file)
@@ -113,7 +113,7 @@ out:
        wake_up(&rpci->waitq);
        return res;
 }
-EXPORT_SYMBOL(rpc_queue_upcall);
+EXPORT_SYMBOL_GPL(rpc_queue_upcall);
 
 static inline void
 rpc_inode_setowner(struct inode *inode, void *private)
@@ -126,13 +126,14 @@ rpc_close_pipes(struct inode *inode)
 {
        struct rpc_inode *rpci = RPC_I(inode);
        struct rpc_pipe_ops *ops;
+       int need_release;
 
        mutex_lock(&inode->i_mutex);
        ops = rpci->ops;
        if (ops != NULL) {
                LIST_HEAD(free_list);
-
                spin_lock(&inode->i_lock);
+               need_release = rpci->nreaders != 0 || rpci->nwriters != 0;
                rpci->nreaders = 0;
                list_splice_init(&rpci->in_upcall, &free_list);
                list_splice_init(&rpci->pipe, &free_list);
@@ -141,7 +142,7 @@ rpc_close_pipes(struct inode *inode)
                spin_unlock(&inode->i_lock);
                rpc_purge_list(rpci, &free_list, ops->destroy_msg, -EPIPE);
                rpci->nwriters = 0;
-               if (ops->release_pipe)
+               if (need_release && ops->release_pipe)
                        ops->release_pipe(inode);
                cancel_delayed_work_sync(&rpci->queue_timeout);
        }
@@ -169,16 +170,24 @@ static int
 rpc_pipe_open(struct inode *inode, struct file *filp)
 {
        struct rpc_inode *rpci = RPC_I(inode);
+       int first_open;
        int res = -ENXIO;
 
        mutex_lock(&inode->i_mutex);
-       if (rpci->ops != NULL) {
-               if (filp->f_mode & FMODE_READ)
-                       rpci->nreaders ++;
-               if (filp->f_mode & FMODE_WRITE)
-                       rpci->nwriters ++;
-               res = 0;
+       if (rpci->ops == NULL)
+               goto out;
+       first_open = rpci->nreaders == 0 && rpci->nwriters == 0;
+       if (first_open && rpci->ops->open_pipe) {
+               res = rpci->ops->open_pipe(inode);
+               if (res)
+                       goto out;
        }
+       if (filp->f_mode & FMODE_READ)
+               rpci->nreaders++;
+       if (filp->f_mode & FMODE_WRITE)
+               rpci->nwriters++;
+       res = 0;
+out:
        mutex_unlock(&inode->i_mutex);
        return res;
 }
@@ -188,6 +197,7 @@ rpc_pipe_release(struct inode *inode, struct file *filp)
 {
        struct rpc_inode *rpci = RPC_I(inode);
        struct rpc_pipe_msg *msg;
+       int last_close;
 
        mutex_lock(&inode->i_mutex);
        if (rpci->ops == NULL)
@@ -214,7 +224,8 @@ rpc_pipe_release(struct inode *inode, struct file *filp)
                                        rpci->ops->destroy_msg, -EAGAIN);
                }
        }
-       if (rpci->ops->release_pipe)
+       last_close = rpci->nwriters == 0 && rpci->nreaders == 0;
+       if (last_close && rpci->ops->release_pipe)
                rpci->ops->release_pipe(inode);
 out:
        mutex_unlock(&inode->i_mutex);
@@ -396,6 +407,7 @@ enum {
        RPCAUTH_nfs,
        RPCAUTH_portmap,
        RPCAUTH_statd,
+       RPCAUTH_nfsd4_cb,
        RPCAUTH_RootEOF
 };
 
@@ -429,6 +441,10 @@ static struct rpc_filelist files[] = {
                .name = "statd",
                .mode = S_IFDIR | S_IRUGO | S_IXUGO,
        },
+       [RPCAUTH_nfsd4_cb] = {
+               .name = "nfsd4_cb",
+               .mode = S_IFDIR | S_IRUGO | S_IXUGO,
+       },
 };
 
 enum {
@@ -748,7 +764,7 @@ rpc_rmdir(struct dentry *dentry)
  * @name: name of pipe
  * @private: private data to associate with the pipe, for the caller's use
  * @ops: operations defining the behavior of the pipe: upcall, downcall,
- *     release_pipe, and destroy_msg.
+ *     release_pipe, open_pipe, and destroy_msg.
  * @flags: rpc_inode flags
  *
  * Data is made available for userspace to read by calls to
@@ -808,7 +824,7 @@ err_dput:
                        -ENOMEM);
        goto out;
 }
-EXPORT_SYMBOL(rpc_mkpipe);
+EXPORT_SYMBOL_GPL(rpc_mkpipe);
 
 /**
  * rpc_unlink - remove a pipe
@@ -839,7 +855,7 @@ rpc_unlink(struct dentry *dentry)
        dput(parent);
        return error;
 }
-EXPORT_SYMBOL(rpc_unlink);
+EXPORT_SYMBOL_GPL(rpc_unlink);
 
 /*
  * populate the filesystem
index 79a55d56cc980dbe63d1caa913046414e870e926..406e26de584e732d3828d615be9b0b2344f682bb 100644 (file)
@@ -28,7 +28,7 @@ xdr_encode_netobj(__be32 *p, const struct xdr_netobj *obj)
        memcpy(p, obj->data, obj->len);
        return p + XDR_QUADLEN(obj->len);
 }
-EXPORT_SYMBOL(xdr_encode_netobj);
+EXPORT_SYMBOL_GPL(xdr_encode_netobj);
 
 __be32 *
 xdr_decode_netobj(__be32 *p, struct xdr_netobj *obj)
@@ -41,7 +41,7 @@ xdr_decode_netobj(__be32 *p, struct xdr_netobj *obj)
        obj->data = (u8 *) p;
        return p + XDR_QUADLEN(len);
 }
-EXPORT_SYMBOL(xdr_decode_netobj);
+EXPORT_SYMBOL_GPL(xdr_decode_netobj);
 
 /**
  * xdr_encode_opaque_fixed - Encode fixed length opaque data
@@ -71,7 +71,7 @@ __be32 *xdr_encode_opaque_fixed(__be32 *p, const void *ptr, unsigned int nbytes)
        }
        return p;
 }
-EXPORT_SYMBOL(xdr_encode_opaque_fixed);
+EXPORT_SYMBOL_GPL(xdr_encode_opaque_fixed);
 
 /**
  * xdr_encode_opaque - Encode variable length opaque data
@@ -86,14 +86,14 @@ __be32 *xdr_encode_opaque(__be32 *p, const void *ptr, unsigned int nbytes)
        *p++ = htonl(nbytes);
        return xdr_encode_opaque_fixed(p, ptr, nbytes);
 }
-EXPORT_SYMBOL(xdr_encode_opaque);
+EXPORT_SYMBOL_GPL(xdr_encode_opaque);
 
 __be32 *
 xdr_encode_string(__be32 *p, const char *string)
 {
        return xdr_encode_array(p, string, strlen(string));
 }
-EXPORT_SYMBOL(xdr_encode_string);
+EXPORT_SYMBOL_GPL(xdr_encode_string);
 
 __be32 *
 xdr_decode_string_inplace(__be32 *p, char **sp,
@@ -108,7 +108,7 @@ xdr_decode_string_inplace(__be32 *p, char **sp,
        *sp = (char *) p;
        return p + XDR_QUADLEN(len);
 }
-EXPORT_SYMBOL(xdr_decode_string_inplace);
+EXPORT_SYMBOL_GPL(xdr_decode_string_inplace);
 
 void
 xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base,
@@ -136,7 +136,7 @@ xdr_encode_pages(struct xdr_buf *xdr, struct page **pages, unsigned int base,
        xdr->buflen += len;
        xdr->len += len;
 }
-EXPORT_SYMBOL(xdr_encode_pages);
+EXPORT_SYMBOL_GPL(xdr_encode_pages);
 
 void
 xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
@@ -158,7 +158,7 @@ xdr_inline_pages(struct xdr_buf *xdr, unsigned int offset,
 
        xdr->buflen += len;
 }
-EXPORT_SYMBOL(xdr_inline_pages);
+EXPORT_SYMBOL_GPL(xdr_inline_pages);
 
 /*
  * Helper routines for doing 'memmove' like operations on a struct xdr_buf
@@ -428,7 +428,7 @@ xdr_shift_buf(struct xdr_buf *buf, size_t len)
 {
        xdr_shrink_bufhead(buf, len);
 }
-EXPORT_SYMBOL(xdr_shift_buf);
+EXPORT_SYMBOL_GPL(xdr_shift_buf);
 
 /**
  * xdr_init_encode - Initialize a struct xdr_stream for sending data.
@@ -465,7 +465,7 @@ void xdr_init_encode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p)
                iov->iov_len += len;
        }
 }
-EXPORT_SYMBOL(xdr_init_encode);
+EXPORT_SYMBOL_GPL(xdr_init_encode);
 
 /**
  * xdr_reserve_space - Reserve buffer space for sending
@@ -492,7 +492,7 @@ __be32 * xdr_reserve_space(struct xdr_stream *xdr, size_t nbytes)
        xdr->buf->len += nbytes;
        return p;
 }
-EXPORT_SYMBOL(xdr_reserve_space);
+EXPORT_SYMBOL_GPL(xdr_reserve_space);
 
 /**
  * xdr_write_pages - Insert a list of pages into an XDR buffer for sending
@@ -527,7 +527,7 @@ void xdr_write_pages(struct xdr_stream *xdr, struct page **pages, unsigned int b
        buf->buflen += len;
        buf->len += len;
 }
-EXPORT_SYMBOL(xdr_write_pages);
+EXPORT_SYMBOL_GPL(xdr_write_pages);
 
 /**
  * xdr_init_decode - Initialize an xdr_stream for decoding data.
@@ -547,7 +547,7 @@ void xdr_init_decode(struct xdr_stream *xdr, struct xdr_buf *buf, __be32 *p)
        xdr->p = p;
        xdr->end = (__be32 *)((char *)iov->iov_base + len);
 }
-EXPORT_SYMBOL(xdr_init_decode);
+EXPORT_SYMBOL_GPL(xdr_init_decode);
 
 /**
  * xdr_inline_decode - Retrieve non-page XDR data to decode
@@ -569,7 +569,7 @@ __be32 * xdr_inline_decode(struct xdr_stream *xdr, size_t nbytes)
        xdr->p = q;
        return p;
 }
-EXPORT_SYMBOL(xdr_inline_decode);
+EXPORT_SYMBOL_GPL(xdr_inline_decode);
 
 /**
  * xdr_read_pages - Ensure page-based XDR data to decode is aligned at current pointer position
@@ -613,7 +613,7 @@ void xdr_read_pages(struct xdr_stream *xdr, unsigned int len)
        xdr->p = (__be32 *)((char *)iov->iov_base + padding);
        xdr->end = (__be32 *)((char *)iov->iov_base + end);
 }
-EXPORT_SYMBOL(xdr_read_pages);
+EXPORT_SYMBOL_GPL(xdr_read_pages);
 
 /**
  * xdr_enter_page - decode data from the XDR page
@@ -638,7 +638,7 @@ void xdr_enter_page(struct xdr_stream *xdr, unsigned int len)
        xdr->p = (__be32 *)(kaddr + xdr->buf->page_base);
        xdr->end = (__be32 *)((char *)xdr->p + len);
 }
-EXPORT_SYMBOL(xdr_enter_page);
+EXPORT_SYMBOL_GPL(xdr_enter_page);
 
 static struct kvec empty_iov = {.iov_base = NULL, .iov_len = 0};
 
@@ -650,7 +650,7 @@ xdr_buf_from_iov(struct kvec *iov, struct xdr_buf *buf)
        buf->page_len = 0;