aboutsummaryrefslogtreecommitdiffstats
path: root/fs/nfsd/nfs4state.c
diff options
context:
space:
mode:
authorJ. Bruce Fields <bfields@redhat.com>2011-01-31 19:20:39 -0500
committerJ. Bruce Fields <bfields@redhat.com>2011-02-14 10:35:19 -0500
commitacfdf5c383b38f7f4dddae41b97c97f1ae058f49 (patch)
treef04f5f069191fa984a394154efdf766898bffcaf /fs/nfsd/nfs4state.c
parent5d926e8c2f46dc09f4ddde86644a5f1d0726a470 (diff)
nfsd4: acquire only one lease per file
Instead of acquiring one lease each time another client opens a file, nfsd can acquire just one lease to represent all of them, and reference count it to determine when to release it. This fixes a regression introduced by c45821d263a8a5109d69a9e8942b8d65bcd5f31a "locks: eliminate fl_mylease callback": after that patch, only the struct file * is used to determine who owns a given lease. But since we recently converted the server to share a single struct file per open, if we acquire multiple leases on the same file from nfsd, it then becomes impossible on unlocking a lease to determine which of those leases (all of whom share the same struct file *) we meant to remove. Thanks to Takashi Iwai <tiwai@suse.de> for catching a bug in a previous version of this patch. Tested-by: Takashi Iwai <tiwai@suse.de> Signed-off-by: J. Bruce Fields <bfields@redhat.com>
Diffstat (limited to 'fs/nfsd/nfs4state.c')
-rw-r--r--fs/nfsd/nfs4state.c95
1 files changed, 55 insertions, 40 deletions
diff --git a/fs/nfsd/nfs4state.c b/fs/nfsd/nfs4state.c
index 8b6cd3cf4835..54b60bfceb8d 100644
--- a/fs/nfsd/nfs4state.c
+++ b/fs/nfsd/nfs4state.c
@@ -230,9 +230,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
230 dp->dl_client = clp; 230 dp->dl_client = clp;
231 get_nfs4_file(fp); 231 get_nfs4_file(fp);
232 dp->dl_file = fp; 232 dp->dl_file = fp;
233 dp->dl_vfs_file = find_readable_file(fp);
234 get_file(dp->dl_vfs_file);
235 dp->dl_flock = NULL;
236 dp->dl_type = type; 233 dp->dl_type = type;
237 dp->dl_stateid.si_boot = boot_time; 234 dp->dl_stateid.si_boot = boot_time;
238 dp->dl_stateid.si_stateownerid = current_delegid++; 235 dp->dl_stateid.si_stateownerid = current_delegid++;
@@ -241,8 +238,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
241 fh_copy_shallow(&dp->dl_fh, &current_fh->fh_handle); 238 fh_copy_shallow(&dp->dl_fh, &current_fh->fh_handle);
242 dp->dl_time = 0; 239 dp->dl_time = 0;
243 atomic_set(&dp->dl_count, 1); 240 atomic_set(&dp->dl_count, 1);
244 list_add(&dp->dl_perfile, &fp->fi_delegations);
245 list_add(&dp->dl_perclnt, &clp->cl_delegations);
246 INIT_WORK(&dp->dl_recall.cb_work, nfsd4_do_callback_rpc); 241 INIT_WORK(&dp->dl_recall.cb_work, nfsd4_do_callback_rpc);
247 return dp; 242 return dp;
248} 243}
@@ -253,24 +248,18 @@ nfs4_put_delegation(struct nfs4_delegation *dp)
253 if (atomic_dec_and_test(&dp->dl_count)) { 248 if (atomic_dec_and_test(&dp->dl_count)) {
254 dprintk("NFSD: freeing dp %p\n",dp); 249 dprintk("NFSD: freeing dp %p\n",dp);
255 put_nfs4_file(dp->dl_file); 250 put_nfs4_file(dp->dl_file);
256 fput(dp->dl_vfs_file);
257 kmem_cache_free(deleg_slab, dp); 251 kmem_cache_free(deleg_slab, dp);
258 num_delegations--; 252 num_delegations--;
259 } 253 }
260} 254}
261 255
262/* Remove the associated file_lock first, then remove the delegation. 256static void nfs4_put_deleg_lease(struct nfs4_file *fp)
263 * lease_modify() is called to remove the FS_LEASE file_lock from
264 * the i_flock list, eventually calling nfsd's lock_manager
265 * fl_release_callback.
266 */
267static void
268nfs4_close_delegation(struct nfs4_delegation *dp)
269{ 257{
270 dprintk("NFSD: close_delegation dp %p\n",dp); 258 if (atomic_dec_and_test(&fp->fi_delegees)) {
271 /* XXX: do we even need this check?: */ 259 vfs_setlease(fp->fi_deleg_file, F_UNLCK, &fp->fi_lease);
272 if (dp->dl_flock) 260 fp->fi_lease = NULL;
273 vfs_setlease(dp->dl_vfs_file, F_UNLCK, &dp->dl_flock); 261 fp->fi_deleg_file = NULL;
262 }
274} 263}
275 264
276/* Called under the state lock. */ 265/* Called under the state lock. */
@@ -282,7 +271,7 @@ unhash_delegation(struct nfs4_delegation *dp)
282 list_del_init(&dp->dl_perfile); 271 list_del_init(&dp->dl_perfile);
283 list_del_init(&dp->dl_recall_lru); 272 list_del_init(&dp->dl_recall_lru);
284 spin_unlock(&recall_lock); 273 spin_unlock(&recall_lock);
285 nfs4_close_delegation(dp); 274 nfs4_put_deleg_lease(dp->dl_file);
286 nfs4_put_delegation(dp); 275 nfs4_put_delegation(dp);
287} 276}
288 277
@@ -2076,6 +2065,7 @@ alloc_init_file(struct inode *ino)
2076 fp->fi_inode = igrab(ino); 2065 fp->fi_inode = igrab(ino);
2077 fp->fi_id = current_fileid++; 2066 fp->fi_id = current_fileid++;
2078 fp->fi_had_conflict = false; 2067 fp->fi_had_conflict = false;
2068 fp->fi_lease = NULL;
2079 memset(fp->fi_fds, 0, sizeof(fp->fi_fds)); 2069 memset(fp->fi_fds, 0, sizeof(fp->fi_fds));
2080 memset(fp->fi_access, 0, sizeof(fp->fi_access)); 2070 memset(fp->fi_access, 0, sizeof(fp->fi_access));
2081 spin_lock(&recall_lock); 2071 spin_lock(&recall_lock);
@@ -2344,26 +2334,26 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
2344 nfsd4_cb_recall(dp); 2334 nfsd4_cb_recall(dp);
2345} 2335}
2346 2336
2347/* 2337/* Called from break_lease() with lock_flocks() held. */
2348 * Called from break_lease() with lock_flocks() held.
2349 * Note: we assume break_lease will only call this *once* for any given
2350 * lease.
2351 */
2352static void nfsd_break_deleg_cb(struct file_lock *fl) 2338static void nfsd_break_deleg_cb(struct file_lock *fl)
2353{ 2339{
2354 struct nfs4_delegation *dp = (struct nfs4_delegation *)fl->fl_owner; 2340 struct nfs4_file *fp = (struct nfs4_file *)fl->fl_owner;
2341 struct nfs4_delegation *dp;
2355 2342
2356 BUG_ON(!dp); 2343 BUG_ON(!fp);
2344 /* We assume break_lease is only called once per lease: */
2345 BUG_ON(fp->fi_had_conflict);
2357 /* 2346 /*
2358 * We don't want the locks code to timeout the lease for us; 2347 * We don't want the locks code to timeout the lease for us;
2359 * we'll remove it ourself if the delegation isn't returned 2348 * we'll remove it ourself if a delegation isn't returned
2360 * in time: 2349 * in time:
2361 */ 2350 */
2362 fl->fl_break_time = 0; 2351 fl->fl_break_time = 0;
2363 2352
2364 spin_lock(&recall_lock); 2353 spin_lock(&recall_lock);
2365 dp->dl_file->fi_had_conflict = true; 2354 fp->fi_had_conflict = true;
2366 nfsd_break_one_deleg(dp); 2355 list_for_each_entry(dp, &fp->fi_delegations, dl_perfile)
2356 nfsd_break_one_deleg(dp);
2367 spin_unlock(&recall_lock); 2357 spin_unlock(&recall_lock);
2368} 2358}
2369 2359
@@ -2455,13 +2445,15 @@ nfs4_check_delegmode(struct nfs4_delegation *dp, int flags)
2455static struct nfs4_delegation * 2445static struct nfs4_delegation *
2456find_delegation_file(struct nfs4_file *fp, stateid_t *stid) 2446find_delegation_file(struct nfs4_file *fp, stateid_t *stid)
2457{ 2447{
2458 struct nfs4_delegation *dp; 2448 struct nfs4_delegation *dp = NULL;
2459 2449
2450 spin_lock(&recall_lock);
2460 list_for_each_entry(dp, &fp->fi_delegations, dl_perfile) { 2451 list_for_each_entry(dp, &fp->fi_delegations, dl_perfile) {
2461 if (dp->dl_stateid.si_stateownerid == stid->si_stateownerid) 2452 if (dp->dl_stateid.si_stateownerid == stid->si_stateownerid)
2462 return dp; 2453 break;
2463 } 2454 }
2464 return NULL; 2455 spin_unlock(&recall_lock);
2456 return dp;
2465} 2457}
2466 2458
2467int share_access_to_flags(u32 share_access) 2459int share_access_to_flags(u32 share_access)
@@ -2649,28 +2641,51 @@ static struct file_lock *nfs4_alloc_init_lease(struct nfs4_delegation *dp, int f
2649 fl->fl_flags = FL_LEASE; 2641 fl->fl_flags = FL_LEASE;
2650 fl->fl_type = flag == NFS4_OPEN_DELEGATE_READ? F_RDLCK: F_WRLCK; 2642 fl->fl_type = flag == NFS4_OPEN_DELEGATE_READ? F_RDLCK: F_WRLCK;
2651 fl->fl_end = OFFSET_MAX; 2643 fl->fl_end = OFFSET_MAX;
2652 fl->fl_owner = (fl_owner_t)dp; 2644 fl->fl_owner = (fl_owner_t)(dp->dl_file);
2653 fl->fl_file = dp->dl_vfs_file;
2654 BUG_ON(!fl->fl_file);
2655 fl->fl_pid = current->tgid; 2645 fl->fl_pid = current->tgid;
2656 dp->dl_flock = fl;
2657 return fl; 2646 return fl;
2658} 2647}
2659 2648
2660static int nfs4_setlease(struct nfs4_delegation *dp, int flag) 2649static int nfs4_setlease(struct nfs4_delegation *dp, int flag)
2661{ 2650{
2651 struct nfs4_file *fp = dp->dl_file;
2662 struct file_lock *fl; 2652 struct file_lock *fl;
2663 int status; 2653 int status;
2664 2654
2665 fl = nfs4_alloc_init_lease(dp, flag); 2655 fl = nfs4_alloc_init_lease(dp, flag);
2666 if (!fl) 2656 if (!fl)
2667 return -ENOMEM; 2657 return -ENOMEM;
2668 status = vfs_setlease(dp->dl_vfs_file, fl->fl_type, &fl); 2658 fl->fl_file = find_readable_file(fp);
2659 list_add(&dp->dl_perclnt, &dp->dl_client->cl_delegations);
2660 status = vfs_setlease(fl->fl_file, fl->fl_type, &fl);
2669 if (status) { 2661 if (status) {
2670 dp->dl_flock = NULL; 2662 list_del_init(&dp->dl_perclnt);
2671 locks_free_lock(fl); 2663 locks_free_lock(fl);
2672 return -ENOMEM; 2664 return -ENOMEM;
2673 } 2665 }
2666 fp->fi_lease = fl;
2667 fp->fi_deleg_file = fl->fl_file;
2668 get_file(fp->fi_deleg_file);
2669 atomic_set(&fp->fi_delegees, 1);
2670 list_add(&dp->dl_perfile, &fp->fi_delegations);
2671 return 0;
2672}
2673
2674static int nfs4_set_delegation(struct nfs4_delegation *dp, int flag)
2675{
2676 struct nfs4_file *fp = dp->dl_file;
2677
2678 if (!fp->fi_lease)
2679 return nfs4_setlease(dp, flag);
2680 spin_lock(&recall_lock);
2681 if (fp->fi_had_conflict) {
2682 spin_unlock(&recall_lock);
2683 return -EAGAIN;
2684 }
2685 atomic_inc(&fp->fi_delegees);
2686 list_add(&dp->dl_perfile, &fp->fi_delegations);
2687 spin_unlock(&recall_lock);
2688 list_add(&dp->dl_perclnt, &dp->dl_client->cl_delegations);
2674 return 0; 2689 return 0;
2675} 2690}
2676 2691
@@ -2715,7 +2730,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta
2715 dp = alloc_init_deleg(sop->so_client, stp, fh, flag); 2730 dp = alloc_init_deleg(sop->so_client, stp, fh, flag);
2716 if (dp == NULL) 2731 if (dp == NULL)
2717 goto out_no_deleg; 2732 goto out_no_deleg;
2718 status = nfs4_setlease(dp, flag); 2733 status = nfs4_set_delegation(dp, flag);
2719 if (status) 2734 if (status)
2720 goto out_free; 2735 goto out_free;
2721 2736
@@ -2731,7 +2746,7 @@ out:
2731 open->op_delegate_type = flag; 2746 open->op_delegate_type = flag;
2732 return; 2747 return;
2733out_free: 2748out_free:
2734 unhash_delegation(dp); 2749 nfs4_put_delegation(dp);
2735out_no_deleg: 2750out_no_deleg:
2736 flag = NFS4_OPEN_DELEGATE_NONE; 2751 flag = NFS4_OPEN_DELEGATE_NONE;
2737 goto out; 2752 goto out;
@@ -3139,7 +3154,7 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
3139 goto out; 3154 goto out;
3140 renew_client(dp->dl_client); 3155 renew_client(dp->dl_client);
3141 if (filpp) { 3156 if (filpp) {
3142 *filpp = find_readable_file(dp->dl_file); 3157 *filpp = dp->dl_file->fi_deleg_file;
3143 BUG_ON(!*filpp); 3158 BUG_ON(!*filpp);
3144 } 3159 }
3145 } else { /* open or lock stateid */ 3160 } else { /* open or lock stateid */