diff options
author | J. Bruce Fields <bfields@redhat.com> | 2011-01-31 19:20:39 -0500 |
---|---|---|
committer | J. Bruce Fields <bfields@redhat.com> | 2011-02-14 10:35:19 -0500 |
commit | acfdf5c383b38f7f4dddae41b97c97f1ae058f49 (patch) | |
tree | f04f5f069191fa984a394154efdf766898bffcaf /fs/nfsd/nfs4state.c | |
parent | 5d926e8c2f46dc09f4ddde86644a5f1d0726a470 (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.c | 95 |
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, ¤t_fh->fh_handle); | 238 | fh_copy_shallow(&dp->dl_fh, ¤t_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. | 256 | static 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 | */ | ||
267 | static void | ||
268 | nfs4_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 | */ | ||
2352 | static void nfsd_break_deleg_cb(struct file_lock *fl) | 2338 | static 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) | |||
2455 | static struct nfs4_delegation * | 2445 | static struct nfs4_delegation * |
2456 | find_delegation_file(struct nfs4_file *fp, stateid_t *stid) | 2446 | find_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 | ||
2467 | int share_access_to_flags(u32 share_access) | 2459 | int 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 | ||
2660 | static int nfs4_setlease(struct nfs4_delegation *dp, int flag) | 2649 | static 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 | |||
2674 | static 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; |
2733 | out_free: | 2748 | out_free: |
2734 | unhash_delegation(dp); | 2749 | nfs4_put_delegation(dp); |
2735 | out_no_deleg: | 2750 | out_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 */ |