| From 4ae19c2dd713edb7b8ad3d4ab9d234ed5dcb6b98 Mon Sep 17 00:00:00 2001 |
| From: Trond Myklebust <Trond.Myklebust@netapp.com> |
| Date: Fri, 18 Jan 2013 22:41:53 -0500 |
| Subject: NFSv4: Fix NFSv4 reference counting for trunked sessions |
| |
| From: Trond Myklebust <Trond.Myklebust@netapp.com> |
| |
| commit 4ae19c2dd713edb7b8ad3d4ab9d234ed5dcb6b98 upstream. |
| |
| The reference counting in nfs4_init_client assumes wongly that it |
| is safe for nfs4_discover_server_trunking() to return a pointer to a |
| nfs_client prior to bumping the reference count. |
| |
| Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com> |
| Cc: Chuck Lever <chuck.lever@oracle.com> |
| Cc: Ben Greear <greearb@candelatech.com> |
| Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> |
| |
| --- |
| fs/nfs/nfs4client.c | 31 +++++++++++++++---------------- |
| 1 file changed, 15 insertions(+), 16 deletions(-) |
| |
| --- a/fs/nfs/nfs4client.c |
| +++ b/fs/nfs/nfs4client.c |
| @@ -235,11 +235,10 @@ struct nfs_client *nfs4_init_client(stru |
| error = nfs4_discover_server_trunking(clp, &old); |
| if (error < 0) |
| goto error; |
| + nfs_put_client(clp); |
| if (clp != old) { |
| clp->cl_preserve_clid = true; |
| - nfs_put_client(clp); |
| clp = old; |
| - atomic_inc(&clp->cl_count); |
| } |
| |
| return clp; |
| @@ -305,7 +304,7 @@ int nfs40_walk_client_list(struct nfs_cl |
| .clientid = new->cl_clientid, |
| .confirm = new->cl_confirm, |
| }; |
| - int status; |
| + int status = -NFS4ERR_STALE_CLIENTID; |
| |
| spin_lock(&nn->nfs_client_lock); |
| list_for_each_entry_safe(pos, n, &nn->nfs_client_list, cl_share_link) { |
| @@ -331,28 +330,28 @@ int nfs40_walk_client_list(struct nfs_cl |
| |
| if (prev) |
| nfs_put_client(prev); |
| + prev = pos; |
| |
| status = nfs4_proc_setclientid_confirm(pos, &clid, cred); |
| - if (status == 0) { |
| + switch (status) { |
| + case -NFS4ERR_STALE_CLIENTID: |
| + break; |
| + case 0: |
| nfs4_swap_callback_idents(pos, new); |
| |
| - nfs_put_client(pos); |
| + prev = NULL; |
| *result = pos; |
| dprintk("NFS: <-- %s using nfs_client = %p ({%d})\n", |
| __func__, pos, atomic_read(&pos->cl_count)); |
| - return 0; |
| - } |
| - if (status != -NFS4ERR_STALE_CLIENTID) { |
| - nfs_put_client(pos); |
| - dprintk("NFS: <-- %s status = %d, no result\n", |
| - __func__, status); |
| - return status; |
| + default: |
| + goto out; |
| } |
| |
| spin_lock(&nn->nfs_client_lock); |
| - prev = pos; |
| } |
| + spin_unlock(&nn->nfs_client_lock); |
| |
| +out: |
| /* |
| * No matching nfs_client found. This should be impossible, |
| * because the new nfs_client has already been added to |
| @@ -362,9 +361,8 @@ int nfs40_walk_client_list(struct nfs_cl |
| */ |
| if (prev) |
| nfs_put_client(prev); |
| - spin_unlock(&nn->nfs_client_lock); |
| - pr_err("NFS: %s Error: no matching nfs_client found\n", __func__); |
| - return -NFS4ERR_STALE_CLIENTID; |
| + dprintk("NFS: <-- %s status = %d\n", __func__, status); |
| + return status; |
| } |
| |
| #ifdef CONFIG_NFS_V4_1 |
| @@ -472,6 +470,7 @@ int nfs41_walk_client_list(struct nfs_cl |
| if (!nfs4_match_serverowners(pos, new)) |
| continue; |
| |
| + atomic_inc(&pos->cl_count); |
| spin_unlock(&nn->nfs_client_lock); |
| dprintk("NFS: <-- %s using nfs_client = %p ({%d})\n", |
| __func__, pos, atomic_read(&pos->cl_count)); |