Commit acfdf5c3 authored by J. Bruce Fields's avatar J. Bruce Fields

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
c45821d2 "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: default avatarTakashi Iwai <tiwai@suse.de>
Signed-off-by: default avatarJ. Bruce Fields <bfields@redhat.com>
parent 5d926e8c
...@@ -230,9 +230,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f ...@@ -230,9 +230,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
dp->dl_client = clp; dp->dl_client = clp;
get_nfs4_file(fp); get_nfs4_file(fp);
dp->dl_file = fp; dp->dl_file = fp;
dp->dl_vfs_file = find_readable_file(fp);
get_file(dp->dl_vfs_file);
dp->dl_flock = NULL;
dp->dl_type = type; dp->dl_type = type;
dp->dl_stateid.si_boot = boot_time; dp->dl_stateid.si_boot = boot_time;
dp->dl_stateid.si_stateownerid = current_delegid++; 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,8 +238,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
fh_copy_shallow(&dp->dl_fh, &current_fh->fh_handle); fh_copy_shallow(&dp->dl_fh, &current_fh->fh_handle);
dp->dl_time = 0; dp->dl_time = 0;
atomic_set(&dp->dl_count, 1); atomic_set(&dp->dl_count, 1);
list_add(&dp->dl_perfile, &fp->fi_delegations);
list_add(&dp->dl_perclnt, &clp->cl_delegations);
INIT_WORK(&dp->dl_recall.cb_work, nfsd4_do_callback_rpc); INIT_WORK(&dp->dl_recall.cb_work, nfsd4_do_callback_rpc);
return dp; return dp;
} }
...@@ -253,24 +248,18 @@ nfs4_put_delegation(struct nfs4_delegation *dp) ...@@ -253,24 +248,18 @@ nfs4_put_delegation(struct nfs4_delegation *dp)
if (atomic_dec_and_test(&dp->dl_count)) { if (atomic_dec_and_test(&dp->dl_count)) {
dprintk("NFSD: freeing dp %p\n",dp); dprintk("NFSD: freeing dp %p\n",dp);
put_nfs4_file(dp->dl_file); put_nfs4_file(dp->dl_file);
fput(dp->dl_vfs_file);
kmem_cache_free(deleg_slab, dp); kmem_cache_free(deleg_slab, dp);
num_delegations--; num_delegations--;
} }
} }
/* Remove the associated file_lock first, then remove the delegation. static void nfs4_put_deleg_lease(struct nfs4_file *fp)
* lease_modify() is called to remove the FS_LEASE file_lock from
* the i_flock list, eventually calling nfsd's lock_manager
* fl_release_callback.
*/
static void
nfs4_close_delegation(struct nfs4_delegation *dp)
{ {
dprintk("NFSD: close_delegation dp %p\n",dp); if (atomic_dec_and_test(&fp->fi_delegees)) {
/* XXX: do we even need this check?: */ vfs_setlease(fp->fi_deleg_file, F_UNLCK, &fp->fi_lease);
if (dp->dl_flock) fp->fi_lease = NULL;
vfs_setlease(dp->dl_vfs_file, F_UNLCK, &dp->dl_flock); fp->fi_deleg_file = NULL;
}
} }
/* Called under the state lock. */ /* Called under the state lock. */
...@@ -282,7 +271,7 @@ unhash_delegation(struct nfs4_delegation *dp) ...@@ -282,7 +271,7 @@ unhash_delegation(struct nfs4_delegation *dp)
list_del_init(&dp->dl_perfile); list_del_init(&dp->dl_perfile);
list_del_init(&dp->dl_recall_lru); list_del_init(&dp->dl_recall_lru);
spin_unlock(&recall_lock); spin_unlock(&recall_lock);
nfs4_close_delegation(dp); nfs4_put_deleg_lease(dp->dl_file);
nfs4_put_delegation(dp); nfs4_put_delegation(dp);
} }
...@@ -2076,6 +2065,7 @@ alloc_init_file(struct inode *ino) ...@@ -2076,6 +2065,7 @@ alloc_init_file(struct inode *ino)
fp->fi_inode = igrab(ino); fp->fi_inode = igrab(ino);
fp->fi_id = current_fileid++; fp->fi_id = current_fileid++;
fp->fi_had_conflict = false; fp->fi_had_conflict = false;
fp->fi_lease = NULL;
memset(fp->fi_fds, 0, sizeof(fp->fi_fds)); memset(fp->fi_fds, 0, sizeof(fp->fi_fds));
memset(fp->fi_access, 0, sizeof(fp->fi_access)); memset(fp->fi_access, 0, sizeof(fp->fi_access));
spin_lock(&recall_lock); spin_lock(&recall_lock);
...@@ -2344,25 +2334,25 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp) ...@@ -2344,25 +2334,25 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
nfsd4_cb_recall(dp); nfsd4_cb_recall(dp);
} }
/* /* Called from break_lease() with lock_flocks() held. */
* Called from break_lease() with lock_flocks() held.
* Note: we assume break_lease will only call this *once* for any given
* lease.
*/
static void nfsd_break_deleg_cb(struct file_lock *fl) static void nfsd_break_deleg_cb(struct file_lock *fl)
{ {
struct nfs4_delegation *dp = (struct nfs4_delegation *)fl->fl_owner; struct nfs4_file *fp = (struct nfs4_file *)fl->fl_owner;
struct nfs4_delegation *dp;
BUG_ON(!dp); BUG_ON(!fp);
/* We assume break_lease is only called once per lease: */
BUG_ON(fp->fi_had_conflict);
/* /*
* We don't want the locks code to timeout the lease for us; * We don't want the locks code to timeout the lease for us;
* we'll remove it ourself if the delegation isn't returned * we'll remove it ourself if a delegation isn't returned
* in time: * in time:
*/ */
fl->fl_break_time = 0; fl->fl_break_time = 0;
spin_lock(&recall_lock); spin_lock(&recall_lock);
dp->dl_file->fi_had_conflict = true; fp->fi_had_conflict = true;
list_for_each_entry(dp, &fp->fi_delegations, dl_perfile)
nfsd_break_one_deleg(dp); nfsd_break_one_deleg(dp);
spin_unlock(&recall_lock); spin_unlock(&recall_lock);
} }
...@@ -2455,13 +2445,15 @@ nfs4_check_delegmode(struct nfs4_delegation *dp, int flags) ...@@ -2455,13 +2445,15 @@ nfs4_check_delegmode(struct nfs4_delegation *dp, int flags)
static struct nfs4_delegation * static struct nfs4_delegation *
find_delegation_file(struct nfs4_file *fp, stateid_t *stid) find_delegation_file(struct nfs4_file *fp, stateid_t *stid)
{ {
struct nfs4_delegation *dp; struct nfs4_delegation *dp = NULL;
spin_lock(&recall_lock);
list_for_each_entry(dp, &fp->fi_delegations, dl_perfile) { list_for_each_entry(dp, &fp->fi_delegations, dl_perfile) {
if (dp->dl_stateid.si_stateownerid == stid->si_stateownerid) if (dp->dl_stateid.si_stateownerid == stid->si_stateownerid)
return dp; break;
} }
return NULL; spin_unlock(&recall_lock);
return dp;
} }
int share_access_to_flags(u32 share_access) 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,28 +2641,51 @@ static struct file_lock *nfs4_alloc_init_lease(struct nfs4_delegation *dp, int f
fl->fl_flags = FL_LEASE; fl->fl_flags = FL_LEASE;
fl->fl_type = flag == NFS4_OPEN_DELEGATE_READ? F_RDLCK: F_WRLCK; fl->fl_type = flag == NFS4_OPEN_DELEGATE_READ? F_RDLCK: F_WRLCK;
fl->fl_end = OFFSET_MAX; fl->fl_end = OFFSET_MAX;
fl->fl_owner = (fl_owner_t)dp; fl->fl_owner = (fl_owner_t)(dp->dl_file);
fl->fl_file = dp->dl_vfs_file;
BUG_ON(!fl->fl_file);
fl->fl_pid = current->tgid; fl->fl_pid = current->tgid;
dp->dl_flock = fl;
return fl; return fl;
} }
static int nfs4_setlease(struct nfs4_delegation *dp, int flag) static int nfs4_setlease(struct nfs4_delegation *dp, int flag)
{ {
struct nfs4_file *fp = dp->dl_file;
struct file_lock *fl; struct file_lock *fl;
int status; int status;
fl = nfs4_alloc_init_lease(dp, flag); fl = nfs4_alloc_init_lease(dp, flag);
if (!fl) if (!fl)
return -ENOMEM; return -ENOMEM;
status = vfs_setlease(dp->dl_vfs_file, fl->fl_type, &fl); fl->fl_file = find_readable_file(fp);
list_add(&dp->dl_perclnt, &dp->dl_client->cl_delegations);
status = vfs_setlease(fl->fl_file, fl->fl_type, &fl);
if (status) { if (status) {
dp->dl_flock = NULL; list_del_init(&dp->dl_perclnt);
locks_free_lock(fl); locks_free_lock(fl);
return -ENOMEM; return -ENOMEM;
} }
fp->fi_lease = fl;
fp->fi_deleg_file = fl->fl_file;
get_file(fp->fi_deleg_file);
atomic_set(&fp->fi_delegees, 1);
list_add(&dp->dl_perfile, &fp->fi_delegations);
return 0;
}
static int nfs4_set_delegation(struct nfs4_delegation *dp, int flag)
{
struct nfs4_file *fp = dp->dl_file;
if (!fp->fi_lease)
return nfs4_setlease(dp, flag);
spin_lock(&recall_lock);
if (fp->fi_had_conflict) {
spin_unlock(&recall_lock);
return -EAGAIN;
}
atomic_inc(&fp->fi_delegees);
list_add(&dp->dl_perfile, &fp->fi_delegations);
spin_unlock(&recall_lock);
list_add(&dp->dl_perclnt, &dp->dl_client->cl_delegations);
return 0; return 0;
} }
...@@ -2715,7 +2730,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta ...@@ -2715,7 +2730,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta
dp = alloc_init_deleg(sop->so_client, stp, fh, flag); dp = alloc_init_deleg(sop->so_client, stp, fh, flag);
if (dp == NULL) if (dp == NULL)
goto out_no_deleg; goto out_no_deleg;
status = nfs4_setlease(dp, flag); status = nfs4_set_delegation(dp, flag);
if (status) if (status)
goto out_free; goto out_free;
...@@ -2731,7 +2746,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta ...@@ -2731,7 +2746,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta
open->op_delegate_type = flag; open->op_delegate_type = flag;
return; return;
out_free: out_free:
unhash_delegation(dp); nfs4_put_delegation(dp);
out_no_deleg: out_no_deleg:
flag = NFS4_OPEN_DELEGATE_NONE; flag = NFS4_OPEN_DELEGATE_NONE;
goto out; goto out;
...@@ -3139,7 +3154,7 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate, ...@@ -3139,7 +3154,7 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
goto out; goto out;
renew_client(dp->dl_client); renew_client(dp->dl_client);
if (filpp) { if (filpp) {
*filpp = find_readable_file(dp->dl_file); *filpp = dp->dl_file->fi_deleg_file;
BUG_ON(!*filpp); BUG_ON(!*filpp);
} }
} else { /* open or lock stateid */ } else { /* open or lock stateid */
......
...@@ -83,8 +83,6 @@ struct nfs4_delegation { ...@@ -83,8 +83,6 @@ struct nfs4_delegation {
atomic_t dl_count; /* ref count */ atomic_t dl_count; /* ref count */
struct nfs4_client *dl_client; struct nfs4_client *dl_client;
struct nfs4_file *dl_file; struct nfs4_file *dl_file;
struct file *dl_vfs_file;
struct file_lock *dl_flock;
u32 dl_type; u32 dl_type;
time_t dl_time; time_t dl_time;
/* For recall: */ /* For recall: */
...@@ -379,6 +377,9 @@ struct nfs4_file { ...@@ -379,6 +377,9 @@ struct nfs4_file {
*/ */
atomic_t fi_readers; atomic_t fi_readers;
atomic_t fi_writers; atomic_t fi_writers;
struct file *fi_deleg_file;
struct file_lock *fi_lease;
atomic_t fi_delegees;
struct inode *fi_inode; struct inode *fi_inode;
u32 fi_id; /* used with stateowner->so_id u32 fi_id; /* used with stateowner->so_id
* for stateid_hashtbl hash */ * for stateid_hashtbl hash */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment