Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+55 −40
Original line number Diff line number Diff line
@@ -230,9 +230,6 @@ alloc_init_deleg(struct nfs4_client *clp, struct nfs4_stateid *stp, struct svc_f
	dp->dl_client = clp;
	get_nfs4_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_stateid.si_boot = boot_time;
	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
	fh_copy_shallow(&dp->dl_fh, &current_fh->fh_handle);
	dp->dl_time = 0;
	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);
	return dp;
}
@@ -253,24 +248,18 @@ nfs4_put_delegation(struct nfs4_delegation *dp)
	if (atomic_dec_and_test(&dp->dl_count)) {
		dprintk("NFSD: freeing dp %p\n",dp);
		put_nfs4_file(dp->dl_file);
		fput(dp->dl_vfs_file);
		kmem_cache_free(deleg_slab, dp);
		num_delegations--;
	}
}

/* Remove the associated file_lock first, then remove the delegation.
 * 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)
static void nfs4_put_deleg_lease(struct nfs4_file *fp)
{
	dprintk("NFSD: close_delegation dp %p\n",dp);
	/* XXX: do we even need this check?: */
	if (dp->dl_flock)
		vfs_setlease(dp->dl_vfs_file, F_UNLCK, &dp->dl_flock);
	if (atomic_dec_and_test(&fp->fi_delegees)) {
		vfs_setlease(fp->fi_deleg_file, F_UNLCK, &fp->fi_lease);
		fp->fi_lease = NULL;
		fp->fi_deleg_file = NULL;
	}
}

/* Called under the state lock. */
@@ -282,7 +271,7 @@ unhash_delegation(struct nfs4_delegation *dp)
	list_del_init(&dp->dl_perfile);
	list_del_init(&dp->dl_recall_lru);
	spin_unlock(&recall_lock);
	nfs4_close_delegation(dp);
	nfs4_put_deleg_lease(dp->dl_file);
	nfs4_put_delegation(dp);
}

@@ -2076,6 +2065,7 @@ alloc_init_file(struct inode *ino)
		fp->fi_inode = igrab(ino);
		fp->fi_id = current_fileid++;
		fp->fi_had_conflict = false;
		fp->fi_lease = NULL;
		memset(fp->fi_fds, 0, sizeof(fp->fi_fds));
		memset(fp->fi_access, 0, sizeof(fp->fi_access));
		spin_lock(&recall_lock);
@@ -2344,25 +2334,25 @@ static void nfsd_break_one_deleg(struct nfs4_delegation *dp)
	nfsd4_cb_recall(dp);
}

/*
 * Called from break_lease() with lock_flocks() held.
 * Note: we assume break_lease will only call this *once* for any given
 * lease.
 */
/* Called from break_lease() with lock_flocks() held. */
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'll remove it ourself if the delegation isn't returned
	 * we'll remove it ourself if a delegation isn't returned
	 * in time:
	 */
	fl->fl_break_time = 0;

	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);
	spin_unlock(&recall_lock);
}
@@ -2455,13 +2445,15 @@ nfs4_check_delegmode(struct nfs4_delegation *dp, int flags)
static struct nfs4_delegation *
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) {
		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)
@@ -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_type = flag == NFS4_OPEN_DELEGATE_READ? F_RDLCK: F_WRLCK;
	fl->fl_end = OFFSET_MAX;
	fl->fl_owner = (fl_owner_t)dp;
	fl->fl_file = dp->dl_vfs_file;
	BUG_ON(!fl->fl_file);
	fl->fl_owner = (fl_owner_t)(dp->dl_file);
	fl->fl_pid = current->tgid;
	dp->dl_flock = fl;
	return fl;
}

static int nfs4_setlease(struct nfs4_delegation *dp, int flag)
{
	struct nfs4_file *fp = dp->dl_file;
	struct file_lock *fl;
	int status;

	fl = nfs4_alloc_init_lease(dp, flag);
	if (!fl)
		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) {
		dp->dl_flock = NULL;
		list_del_init(&dp->dl_perclnt);
		locks_free_lock(fl);
		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;
}

@@ -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);
	if (dp == NULL)
		goto out_no_deleg;
	status = nfs4_setlease(dp, flag);
	status = nfs4_set_delegation(dp, flag);
	if (status)
		goto out_free;

@@ -2731,7 +2746,7 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta
	open->op_delegate_type = flag;
	return;
out_free:
	unhash_delegation(dp);
	nfs4_put_delegation(dp);
out_no_deleg:
	flag = NFS4_OPEN_DELEGATE_NONE;
	goto out;
@@ -3139,7 +3154,7 @@ nfs4_preprocess_stateid_op(struct nfsd4_compound_state *cstate,
			goto out;
		renew_client(dp->dl_client);
		if (filpp) {
			*filpp = find_readable_file(dp->dl_file);
			*filpp = dp->dl_file->fi_deleg_file;
			BUG_ON(!*filpp);
		}
	} else { /* open or lock stateid */
+3 −2
Original line number Diff line number Diff line
@@ -83,8 +83,6 @@ struct nfs4_delegation {
	atomic_t		dl_count;       /* ref count */
	struct nfs4_client	*dl_client;
	struct nfs4_file	*dl_file;
	struct file		*dl_vfs_file;
	struct file_lock	*dl_flock;
	u32			dl_type;
	time_t			dl_time;
/* For recall: */
@@ -379,6 +377,9 @@ struct nfs4_file {
	 */
	atomic_t		fi_readers;
	atomic_t		fi_writers;
	struct file		*fi_deleg_file;
	struct file_lock	*fi_lease;
	atomic_t		fi_delegees;
	struct inode		*fi_inode;
	u32                     fi_id;      /* used with stateowner->so_id 
					     * for stateid_hashtbl hash */