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

Commit f60c153d authored by Linus Torvalds's avatar Linus Torvalds
Browse files

Merge branch 'for-2.6.38' of git://linux-nfs.org/~bfields/linux

* 'for-2.6.38' of git://linux-nfs.org/~bfields/linux:
  nfsd: break lease on unlink due to rename
  nfsd4: acquire only one lease per file
  nfsd4: modify fi_delegations under recall_lock
  nfsd4: remove unused deleg dprintk's.
  nfsd4: split lease setting into separate function
  nfsd4: fix leak on allocation error
  nfsd4: add helper function for lease setup
  nfsd4: split up nfsd_break_deleg_cb
  NFSD: memory corruption due to writing beyond the stat array
  NFSD: use nfserr for status after decode_cb_op_status
  nfsd: don't leak dentry count on mnt_want_write failure
parents a1213b09 83f6b0c1
Loading
Loading
Loading
Loading
+2 −4
Original line number Diff line number Diff line
@@ -484,7 +484,7 @@ static int decode_cb_sequence4res(struct xdr_stream *xdr,
out:
	return status;
out_default:
	return nfs_cb_stat_to_errno(status);
	return nfs_cb_stat_to_errno(nfserr);
}

/*
@@ -564,11 +564,9 @@ static int nfs4_xdr_dec_cb_recall(struct rpc_rqst *rqstp,
	if (unlikely(status))
		goto out;
	if (unlikely(nfserr != NFS4_OK))
		goto out_default;
		status = nfs_cb_stat_to_errno(nfserr);
out:
	return status;
out_default:
	return nfs_cb_stat_to_errno(status);
}

/*
+106 −80
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,36 +248,30 @@ 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. */
static void
unhash_delegation(struct nfs4_delegation *dp)
{
	list_del_init(&dp->dl_perfile);
	list_del_init(&dp->dl_perclnt);
	spin_lock(&recall_lock);
	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);
}

@@ -958,8 +947,6 @@ expire_client(struct nfs4_client *clp)
	spin_lock(&recall_lock);
	while (!list_empty(&clp->cl_delegations)) {
		dp = list_entry(clp->cl_delegations.next, struct nfs4_delegation, dl_perclnt);
		dprintk("NFSD: expire client. dp %p, fp %p\n", dp,
				dp->dl_flock);
		list_del_init(&dp->dl_perclnt);
		list_move(&dp->dl_recall_lru, &reaplist);
	}
@@ -2078,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);
@@ -2329,23 +2317,8 @@ nfs4_file_downgrade(struct nfs4_file *fp, unsigned int share_access)
		nfs4_file_put_access(fp, O_RDONLY);
}

/*
 * Spawn a thread to perform a recall on the delegation represented
 * by the lease (file_lock)
 *
 * 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_one_deleg(struct nfs4_delegation *dp)
{
	struct nfs4_delegation *dp = (struct nfs4_delegation *)fl->fl_owner;

	dprintk("NFSD nfsd_break_deleg_cb: dp %p fl %p\n",dp,fl);
	if (!dp)
		return;

	/* We're assuming the state code never drops its reference
	 * without first removing the lease.  Since we're in this lease
	 * callback (and since the lease code is serialized by the kernel
@@ -2353,22 +2326,35 @@ void nfsd_break_deleg_cb(struct file_lock *fl)
	 * it's safe to take a reference: */
	atomic_inc(&dp->dl_count);

	spin_lock(&recall_lock);
	list_add_tail(&dp->dl_recall_lru, &del_recall_lru);
	spin_unlock(&recall_lock);

	/* only place dl_time is set. protected by lock_flocks*/
	dp->dl_time = get_seconds();

	nfsd4_cb_recall(dp);
}

/* Called from break_lease() with lock_flocks() held. */
static void nfsd_break_deleg_cb(struct file_lock *fl)
{
	struct nfs4_file *fp = (struct nfs4_file *)fl->fl_owner;
	struct nfs4_delegation *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
	 * in time.
	 * we'll remove it ourself if a delegation isn't returned
	 * in time:
	 */
	fl->fl_break_time = 0;

	dp->dl_file->fi_had_conflict = true;
	nfsd4_cb_recall(dp);
	spin_lock(&recall_lock);
	fp->fi_had_conflict = true;
	list_for_each_entry(dp, &fp->fi_delegations, dl_perfile)
		nfsd_break_one_deleg(dp);
	spin_unlock(&recall_lock);
}

static
@@ -2459,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)
@@ -2641,6 +2629,66 @@ static bool nfsd4_cb_channel_good(struct nfs4_client *clp)
	return clp->cl_minorversion && clp->cl_cb_state == NFSD4_CB_UNKNOWN;
}

static struct file_lock *nfs4_alloc_init_lease(struct nfs4_delegation *dp, int flag)
{
	struct file_lock *fl;

	fl = locks_alloc_lock();
	if (!fl)
		return NULL;
	locks_init_lock(fl);
	fl->fl_lmops = &nfsd_lease_mng_ops;
	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->dl_file);
	fl->fl_pid = current->tgid;
	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;
	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) {
		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;
}

/*
 * Attempt to hand out a delegation.
 */
@@ -2650,7 +2698,6 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta
	struct nfs4_delegation *dp;
	struct nfs4_stateowner *sop = stp->st_stateowner;
	int cb_up;
	struct file_lock *fl;
	int status, flag = 0;

	cb_up = nfsd4_cb_channel_good(sop->so_client);
@@ -2681,36 +2728,11 @@ 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) {
		flag = NFS4_OPEN_DELEGATE_NONE;
		goto out;
	}
	status = -ENOMEM;
	fl = locks_alloc_lock();
	if (!fl)
		goto out;
	locks_init_lock(fl);
	fl->fl_lmops = &nfsd_lease_mng_ops;
	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 = find_readable_file(stp->st_file);
	BUG_ON(!fl->fl_file);
	fl->fl_pid = current->tgid;
	dp->dl_flock = fl;

	/* vfs_setlease checks to see if delegation should be handed out.
	 * the lock_manager callback fl_change is used
	 */
	if ((status = vfs_setlease(fl->fl_file, fl->fl_type, &fl))) {
		dprintk("NFSD: setlease failed [%d], no delegation\n", status);
		dp->dl_flock = NULL;
		locks_free_lock(fl);
		unhash_delegation(dp);
		flag = NFS4_OPEN_DELEGATE_NONE;
		goto out;
	}
	if (dp == NULL)
		goto out_no_deleg;
	status = nfs4_set_delegation(dp, flag);
	if (status)
		goto out_free;

	memcpy(&open->op_delegate_stateid, &dp->dl_stateid, sizeof(dp->dl_stateid));

@@ -2722,6 +2744,12 @@ nfs4_open_delegation(struct svc_fh *fh, struct nfsd4_open *open, struct nfs4_sta
			&& open->op_delegate_type != NFS4_OPEN_DELEGATE_NONE)
		dprintk("NFSD: WARNING: refusing delegation reclaim\n");
	open->op_delegate_type = flag;
	return;
out_free:
	nfs4_put_delegation(dp);
out_no_deleg:
	flag = NFS4_OPEN_DELEGATE_NONE;
	goto out;
}

/*
@@ -2916,8 +2944,6 @@ nfs4_laundromat(void)
				test_val = u;
			break;
		}
		dprintk("NFSD: purging unused delegation dp %p, fp %p\n",
			            dp, dp->dl_flock);
		list_move(&dp->dl_recall_lru, &reaplist);
	}
	spin_unlock(&recall_lock);
@@ -3128,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 */
+14 −7
Original line number Diff line number Diff line
@@ -808,7 +808,7 @@ nfsd_get_raparms(dev_t dev, ino_t ino)
		if (ra->p_count == 0)
			frap = rap;
	}
	depth = nfsdstats.ra_size*11/10;
	depth = nfsdstats.ra_size;
	if (!frap) {	
		spin_unlock(&rab->pb_lock);
		return NULL;
@@ -1742,6 +1742,13 @@ nfsd_rename(struct svc_rqst *rqstp, struct svc_fh *ffhp, char *fname, int flen,
		goto out_dput_new;

	host_err = nfsd_break_lease(odentry->d_inode);
	if (host_err)
		goto out_drop_write;
	if (ndentry->d_inode) {
		host_err = nfsd_break_lease(ndentry->d_inode);
		if (host_err)
			goto out_drop_write;
	}
	if (host_err)
		goto out_drop_write;
	host_err = vfs_rename(fdir, odentry, tdir, ndentry);
@@ -1812,22 +1819,22 @@ nfsd_unlink(struct svc_rqst *rqstp, struct svc_fh *fhp, int type,

	host_err = mnt_want_write(fhp->fh_export->ex_path.mnt);
	if (host_err)
		goto out_nfserr;
		goto out_put;

	host_err = nfsd_break_lease(rdentry->d_inode);
	if (host_err)
		goto out_put;
		goto out_drop_write;
	if (type != S_IFDIR)
		host_err = vfs_unlink(dirp, rdentry);
	else
		host_err = vfs_rmdir(dirp, rdentry);
out_put:
	dput(rdentry);

	if (!host_err)
		host_err = commit_metadata(fhp);

out_drop_write:
	mnt_drop_write(fhp->fh_export->ex_path.mnt);
out_put:
	dput(rdentry);

out_nfserr:
	err = nfserrno(host_err);
out: