aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2013-08-15 16:55:26 -0400
committerJ. Bruce Fields <bfields@redhat.com>2013-08-30 17:30:52 -0400
commitbf7bd3e98be5c74813bee6ad496139fb0a011b3b (patch)
tree9106fb2a3bdb88298db7aa90235ba28766c64361
parent3477565e6a73da7bb50fce6ac718b31eddb37fbb (diff)
nfsd4: fix leak of inode reference on delegation failure
This fixes a regression from 68a3396178e6688ad7367202cdf0af8ed03c8727 "nfsd4: shut down more of delegation earlier". After that commit, nfs4_set_delegation() failures result in nfs4_put_delegation being called, but nfs4_put_delegation doesn't free the nfs4_file that has already been set by alloc_init_deleg(). This can result in an oops on later unmounting the exported filesystem. Note also delaying the fi_had_conflict check we're able to return a better error (hence give 4.1 clients a better idea why the delegation failed; though note CONFLICT isn't an exact match here, as that's supposed to indicate a current conflict, but all we know here is that there was one recently). Reported-by: Toralf Förster <toralf.foerster@gmx.de> Tested-by: Toralf Förster <toralf.foerster@gmx.de> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
-rw-r--r--fs/nfsd/nfs4state.c31
1 files changed, 20 insertions, 11 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index eb9cf818002a..0874998a49cd 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -368,11 +368,8 @@ static struct nfs4_delegation *
368alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svc_fh *current_fh) 368alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct svc_fh *current_fh)
369{ 369{
370 struct nfs4_delegation *dp; 370 struct nfs4_delegation *dp;
371 struct nfs4_file *fp = stp->st_file;
372 371
373 dprintk("NFSD alloc_init_deleg\n"); 372 dprintk("NFSD alloc_init_deleg\n");
374 if (fp->fi_had_conflict)
375 return NULL;
376 if (num_delegations > max_delegations) 373 if (num_delegations > max_delegations)
377 return NULL; 374 return NULL;
378 dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab)); 375 dp = delegstateid(nfs4_alloc_stid(clp, deleg_slab));
@@ -389,8 +386,7 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_ol_stateid *stp, struct sv
389 INIT_LIST_HEAD(&dp->dl_perfile); 386 INIT_LIST_HEAD(&dp->dl_perfile);
390 INIT_LIST_HEAD(&dp->dl_perclnt); 387 INIT_LIST_HEAD(&dp->dl_perclnt);
391 INIT_LIST_HEAD(&dp->dl_recall_lru); 388 INIT_LIST_HEAD(&dp->dl_recall_lru);
392 get_nfs4_file(fp); 389 dp->dl_file = NULL;
393 dp->dl_file = fp;
394 dp->dl_type = NFS4_OPEN_DELEGATE_READ; 390 dp->dl_type = NFS4_OPEN_DELEGATE_READ;
395 fh_copy_shallow(&dp->dl_fh, &current_fh->fh_handle); 391 fh_copy_shallow(&dp->dl_fh, &current_fh->fh_handle);
396 dp->dl_time = 0; 392 dp->dl_time = 0;
@@ -3044,22 +3040,35 @@ static int nfs4_setlease(struct nfs4_delegation *dp)
3044 return 0; 3040 return 0;
3045} 3041}
3046 3042
3047static int nfs4_set_delegation(struct nfs4_delegation *dp) 3043static int nfs4_set_delegation(struct nfs4_delegation *dp, struct nfs4_file *fp)
3048{ 3044{
3049 struct nfs4_file *fp = dp->dl_file; 3045 int status;
3050 3046
3051 if (!fp->fi_lease) 3047 if (fp->fi_had_conflict)
3052 return nfs4_setlease(dp); 3048 return -EAGAIN;
3049 get_nfs4_file(fp);
3050 dp->dl_file = fp;
3051 if (!fp->fi_lease) {
3052 status = nfs4_setlease(dp);
3053 if (status)
3054 goto out_free;
3055 return 0;
3056 }
3053 spin_lock(&recall_lock); 3057 spin_lock(&recall_lock);
3054 if (fp->fi_had_conflict) { 3058 if (fp->fi_had_conflict) {
3055 spin_unlock(&recall_lock); 3059 spin_unlock(&recall_lock);
3056 return -EAGAIN; 3060 status = -EAGAIN;
3061 goto out_free;
3057 } 3062 }
3058 atomic_inc(&fp->fi_delegees); 3063 atomic_inc(&fp->fi_delegees);
3059 list_add(&dp->dl_perfile, &fp->fi_delegations); 3064 list_add(&dp->dl_perfile, &fp->fi_delegations);
3060 spin_unlock(&recall_lock); 3065 spin_unlock(&recall_lock);
3061 list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations); 3066 list_add(&dp->dl_perclnt, &dp->dl_stid.sc_client->cl_delegations);
3062 return 0; 3067 return 0;
3068out_free:
3069 put_nfs4_file(fp);
3070 dp->dl_file = fp;
3071 return status;
3063} 3072}
3064 3073
3065static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status) 3074static void nfsd4_open_deleg_none_ext(struct nfsd4_open *open, int status)
@@ -3134,7 +3143,7 @@ nfs4_open_delegation(struct net *net, struct svc_fh *fh,
3134 dp = alloc_init_deleg(oo->oo_owner.so_client, stp, fh); 3143 dp = alloc_init_deleg(oo->oo_owner.so_client, stp, fh);
3135 if (dp == NULL) 3144 if (dp == NULL)
3136 goto out_no_deleg; 3145 goto out_no_deleg;
3137 status = nfs4_set_delegation(dp); 3146 status = nfs4_set_delegation(dp, stp->st_file);
3138 if (status) 3147 if (status)
3139 goto out_free; 3148 goto out_free;
3140 3149