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

Commit 73e2e0c9 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
Pull NFS client updates from Trond Myklebust:
 "Highlights include:

  Stable bugfixes:
   - Fix a pnfs deadlock between read resends and layoutreturn
   - Don't invalidate the layout stateid while a layout return is
     outstanding
   - Don't schedule a layoutreturn if the layout stateid is marked as
     invalid
   - On a pNFS error, do not send LAYOUTGET until the LAYOUTRETURN is
     complete
   - SUNRPC: fix refcounting problems with auth_gss messages.

  Features:
   - Add client support for the NFSv4 umask attribute.
   - NFSv4: Correct support for flock() stateids.
   - Add a LAYOUTRETURN operation to CLOSE and DELEGRETURN when
     return-on-close is specified
   - Allow the pNFS/flexfiles layoutstat information to piggyback on
     LAYOUTRETURN
   - Optimise away redundant GETATTR calls when doing state recovery
     and/or when not required by cache revalidation rules or
     close-to-open cache consistency.
   - Attribute cache improvements
   - RPC/RDMA support for SG_GAP devices

  Bugfixes:
   - NFS: Fix performance regressions in readdir
   - pNFS/flexfiles: Fix a deadlock on LAYOUTGET
   - NFSv4: Add missing nfs_put_lock_context()
   - NFSv4.1: Fix regression in callback retry handling
   - Fix false positive NFSv4.0 trunking detection.
   - pNFS/flexfiles: Only send layoutstats updates for mirrors that were
     updated
   - Various layout stateid related bugfixes
   - RPC/RDMA bugfixes"

* tag 'nfs-for-4.10-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (82 commits)
  SUNRPC: fix refcounting problems with auth_gss messages.
  nfs: add support for the umask attribute
  pNFS/flexfiles: Ensure we have enough buffer for layoutreturn
  pNFS/flexfiles: Remove a redundant parameter in ff_layout_encode_ioerr()
  pNFS/flexfiles: Fix a deadlock on LAYOUTGET
  pNFS: Layoutreturn must free the layout after the layout-private data
  pNFS/flexfiles: Fix ff_layout_add_ds_error_locked()
  NFSv4: Add missing nfs_put_lock_context()
  pNFS: Release NFS_LAYOUT_RETURN when invalidating the layout stateid
  NFSv4.1: Don't schedule lease recovery in nfs4_schedule_session_recovery()
  NFSv4.1: Handle NFS4ERR_BADSESSION/NFS4ERR_DEADSESSION replies to OP_SEQUENCE
  NFS: Only look at the change attribute cache state in nfs_check_verifier
  NFS: Fix incorrect size revalidation when holding a delegation
  NFS: Fix incorrect mapping revalidation when holding a delegation
  pNFS/flexfiles: Support sending layoutstats in layoutreturn
  pNFS/flexfiles: Minor refactoring before adding iostats to layoutreturn
  NFS: Fix up read of mirror stats
  pNFS/flexfiles: Clean up layoutstats
  pNFS/flexfiles: Refactor encoding of the layoutreturn payload
  pNFS: Add a layoutreturn callback to performa layout-private setup
  ...
parents ed3c5a0b 2549f307
Loading
Loading
Loading
Loading
+67 −32
Original line number Diff line number Diff line
@@ -110,20 +110,52 @@ __be32 nfs4_callback_recall(struct cb_recallargs *args, void *dummy,
#if defined(CONFIG_NFS_V4_1)

/*
 * Lookup a layout by filehandle.
 * Lookup a layout inode by stateid
 *
 * Note: gets a refcount on the layout hdr and on its respective inode.
 * Caller must put the layout hdr and the inode.
 * Note: returns a refcount on the inode and superblock
 */
static struct inode *nfs_layout_find_inode_by_stateid(struct nfs_client *clp,
		const nfs4_stateid *stateid)
{
	struct nfs_server *server;
	struct inode *inode;
	struct pnfs_layout_hdr *lo;

restart:
	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
		list_for_each_entry(lo, &server->layouts, plh_layouts) {
			if (stateid != NULL &&
			    !nfs4_stateid_match_other(stateid, &lo->plh_stateid))
				continue;
			inode = igrab(lo->plh_inode);
			if (!inode)
				continue;
			if (!nfs_sb_active(inode->i_sb)) {
				rcu_read_lock();
				spin_unlock(&clp->cl_lock);
				iput(inode);
				spin_lock(&clp->cl_lock);
				goto restart;
			}
			return inode;
		}
	}

	return NULL;
}

/*
 * Lookup a layout inode by filehandle.
 *
 * Note: returns a refcount on the inode and superblock
 *
 * TODO: keep track of all layouts (and delegations) in a hash table
 * hashed by filehandle.
 */
static struct pnfs_layout_hdr * get_layout_by_fh_locked(struct nfs_client *clp,
		struct nfs_fh *fh)
static struct inode *nfs_layout_find_inode_by_fh(struct nfs_client *clp,
		const struct nfs_fh *fh)
{
	struct nfs_server *server;
	struct nfs_inode *nfsi;
	struct inode *ino;
	struct inode *inode;
	struct pnfs_layout_hdr *lo;

restart:
@@ -134,37 +166,38 @@ static struct pnfs_layout_hdr * get_layout_by_fh_locked(struct nfs_client *clp,
				continue;
			if (nfsi->layout != lo)
				continue;
			ino = igrab(lo->plh_inode);
			if (!ino)
				break;
			spin_lock(&ino->i_lock);
			/* Is this layout in the process of being freed? */
			if (nfsi->layout != lo) {
				spin_unlock(&ino->i_lock);
				iput(ino);
			inode = igrab(lo->plh_inode);
			if (!inode)
				continue;
			if (!nfs_sb_active(inode->i_sb)) {
				rcu_read_lock();
				spin_unlock(&clp->cl_lock);
				iput(inode);
				spin_lock(&clp->cl_lock);
				goto restart;
			}
			pnfs_get_layout_hdr(lo);
			spin_unlock(&ino->i_lock);
			return lo;
			return inode;
		}
	}

	return NULL;
}

static struct pnfs_layout_hdr * get_layout_by_fh(struct nfs_client *clp,
		struct nfs_fh *fh)
static struct inode *nfs_layout_find_inode(struct nfs_client *clp,
		const struct nfs_fh *fh,
		const nfs4_stateid *stateid)
{
	struct pnfs_layout_hdr *lo;
	struct inode *inode;

	spin_lock(&clp->cl_lock);
	rcu_read_lock();
	lo = get_layout_by_fh_locked(clp, fh);
	inode = nfs_layout_find_inode_by_stateid(clp, stateid);
	if (!inode)
		inode = nfs_layout_find_inode_by_fh(clp, fh);
	rcu_read_unlock();
	spin_unlock(&clp->cl_lock);

	return lo;
	return inode;
}

/*
@@ -213,18 +246,20 @@ static u32 initiate_file_draining(struct nfs_client *clp,
	u32 rv = NFS4ERR_NOMATCHING_LAYOUT;
	LIST_HEAD(free_me_list);

	lo = get_layout_by_fh(clp, &args->cbl_fh);
	if (!lo) {
		trace_nfs4_cb_layoutrecall_file(clp, &args->cbl_fh, NULL,
				&args->cbl_stateid, -rv);
	ino = nfs_layout_find_inode(clp, &args->cbl_fh, &args->cbl_stateid);
	if (!ino)
		goto out;
	}

	ino = lo->plh_inode;
	pnfs_layoutcommit_inode(ino, false);


	spin_lock(&ino->i_lock);
	lo = NFS_I(ino)->layout;
	if (!lo) {
		spin_unlock(&ino->i_lock);
		goto out;
	}
	pnfs_get_layout_hdr(lo);
	rv = pnfs_check_callback_stateid(lo, &args->cbl_stateid);
	if (rv != NFS_OK)
		goto unlock;
@@ -258,10 +293,10 @@ static u32 initiate_file_draining(struct nfs_client *clp,
	/* Free all lsegs that are attached to commit buckets */
	nfs_commit_inode(ino, 0);
	pnfs_put_layout_hdr(lo);
out:
	trace_nfs4_cb_layoutrecall_file(clp, &args->cbl_fh, ino,
			&args->cbl_stateid, -rv);
	iput(ino);
out:
	nfs_iput_and_deactive(ino);
	return rv;
}

+2 −4
Original line number Diff line number Diff line
@@ -369,9 +369,7 @@ nfs_found_client(const struct nfs_client_initdata *cl_init,
 * Look up a client by IP address and protocol version
 * - creates a new record if one doesn't yet exist
 */
struct nfs_client *
nfs_get_client(const struct nfs_client_initdata *cl_init,
	       rpc_authflavor_t authflavour)
struct nfs_client *nfs_get_client(const struct nfs_client_initdata *cl_init)
{
	struct nfs_client *clp, *new = NULL;
	struct nfs_net *nn = net_generic(cl_init->net, nfs_net_id);
@@ -655,7 +653,7 @@ static int nfs_init_server(struct nfs_server *server,
		set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags);

	/* Allocate or find a client reference we can use */
	clp = nfs_get_client(&cl_init, RPC_AUTH_UNIX);
	clp = nfs_get_client(&cl_init);
	if (IS_ERR(clp)) {
		dprintk("<-- nfs_init_server() = error %ld\n", PTR_ERR(clp));
		return PTR_ERR(clp);
+0 −4
Original line number Diff line number Diff line
@@ -391,10 +391,6 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct
	rcu_assign_pointer(nfsi->delegation, delegation);
	delegation = NULL;

	/* Ensure we revalidate the attributes and page cache! */
	spin_lock(&inode->i_lock);
	nfsi->cache_validity |= NFS_INO_REVAL_FORCED;
	spin_unlock(&inode->i_lock);
	trace_nfs4_set_delegation(inode, res->delegation_type);

out:
+36 −37
Original line number Diff line number Diff line
@@ -455,14 +455,17 @@ bool nfs_use_readdirplus(struct inode *dir, struct dir_context *ctx)
}

/*
 * This function is called by the lookup code to request the use of
 * readdirplus to accelerate any future lookups in the same
 * This function is called by the lookup and getattr code to request the
 * use of readdirplus to accelerate any future lookups in the same
 * directory.
 */
static
void nfs_advise_use_readdirplus(struct inode *dir)
{
	set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_I(dir)->flags);
	struct nfs_inode *nfsi = NFS_I(dir);

	if (nfs_server_capable(dir, NFS_CAP_READDIRPLUS) &&
	    !list_empty(&nfsi->open_files))
		set_bit(NFS_INO_ADVISE_RDPLUS, &nfsi->flags);
}

/*
@@ -475,9 +478,12 @@ void nfs_advise_use_readdirplus(struct inode *dir)
 */
void nfs_force_use_readdirplus(struct inode *dir)
{
	if (!list_empty(&NFS_I(dir)->open_files)) {
		nfs_advise_use_readdirplus(dir);
		nfs_zap_mapping(dir, dir->i_mapping);
	struct nfs_inode *nfsi = NFS_I(dir);

	if (nfs_server_capable(dir, NFS_CAP_READDIRPLUS) &&
	    !list_empty(&nfsi->open_files)) {
		set_bit(NFS_INO_ADVISE_RDPLUS, &nfsi->flags);
		invalidate_mapping_pages(dir->i_mapping, 0, -1);
	}
}

@@ -886,17 +892,6 @@ int uncached_readdir(nfs_readdir_descriptor_t *desc)
	goto out;
}

static bool nfs_dir_mapping_need_revalidate(struct inode *dir)
{
	struct nfs_inode *nfsi = NFS_I(dir);

	if (nfs_attribute_cache_expired(dir))
		return true;
	if (nfsi->cache_validity & NFS_INO_INVALID_DATA)
		return true;
	return false;
}

/* The file offset position represents the dirent entry number.  A
   last cookie cache takes care of the common case of reading the
   whole directory.
@@ -928,7 +923,7 @@ static int nfs_readdir(struct file *file, struct dir_context *ctx)
	desc->decode = NFS_PROTO(inode)->decode_dirent;
	desc->plus = nfs_use_readdirplus(inode, ctx) ? 1 : 0;

	if (ctx->pos == 0 || nfs_dir_mapping_need_revalidate(inode))
	if (ctx->pos == 0 || nfs_attribute_cache_expired(inode))
		res = nfs_revalidate_mapping(inode, file->f_mapping);
	if (res < 0)
		goto out;
@@ -1035,8 +1030,6 @@ EXPORT_SYMBOL_GPL(nfs_force_lookup_revalidate);
static int nfs_check_verifier(struct inode *dir, struct dentry *dentry,
			      int rcu_walk)
{
	int ret;

	if (IS_ROOT(dentry))
		return 1;
	if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONE)
@@ -1044,12 +1037,12 @@ static int nfs_check_verifier(struct inode *dir, struct dentry *dentry,
	if (!nfs_verify_change_attribute(dir, dentry->d_time))
		return 0;
	/* Revalidate nfsi->cache_change_attribute before we declare a match */
	if (nfs_mapping_need_revalidate_inode(dir)) {
		if (rcu_walk)
		ret = nfs_revalidate_inode_rcu(NFS_SERVER(dir), dir);
	else
		ret = nfs_revalidate_inode(NFS_SERVER(dir), dir);
	if (ret < 0)
			return 0;
		if (__nfs_revalidate_inode(NFS_SERVER(dir), dir) < 0)
			return 0;
	}
	if (!nfs_verify_change_attribute(dir, dentry->d_time))
		return 0;
	return 1;
@@ -1161,7 +1154,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
				return -ECHILD;
			goto out_bad;
		}
		goto out_valid_noent;
		goto out_valid;
	}

	if (is_bad_inode(inode)) {
@@ -1184,6 +1177,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
				return -ECHILD;
			goto out_zap_parent;
		}
		nfs_advise_use_readdirplus(dir);
		goto out_valid;
	}

@@ -1219,12 +1213,12 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
	nfs_free_fhandle(fhandle);
	nfs4_label_free(label);

	/* set a readdirplus hint that we had a cache miss */
	nfs_force_use_readdirplus(dir);

out_set_verifier:
	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
 out_valid:
	/* Success: notify readdir to use READDIRPLUS */
	nfs_advise_use_readdirplus(dir);
 out_valid_noent:
	if (flags & LOOKUP_RCU) {
		if (parent != ACCESS_ONCE(dentry->d_parent))
			return -ECHILD;
@@ -1424,8 +1418,8 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
	if (IS_ERR(res))
		goto out_label;

	/* Success: notify readdir to use READDIRPLUS */
	nfs_advise_use_readdirplus(dir);
	/* Notify readdir to use READDIRPLUS */
	nfs_force_use_readdirplus(dir);

no_entry:
	res = d_splice_alias(inode, dentry);
@@ -1467,9 +1461,9 @@ static fmode_t flags_to_mode(int flags)
	return res;
}

static struct nfs_open_context *create_nfs_open_context(struct dentry *dentry, int open_flags)
static struct nfs_open_context *create_nfs_open_context(struct dentry *dentry, int open_flags, struct file *filp)
{
	return alloc_nfs_open_context(dentry, flags_to_mode(open_flags));
	return alloc_nfs_open_context(dentry, flags_to_mode(open_flags), filp);
}

static int do_open(struct inode *inode, struct file *filp)
@@ -1535,8 +1529,13 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
		return -ENAMETOOLONG;

	if (open_flags & O_CREAT) {
		struct nfs_server *server = NFS_SERVER(dir);

		if (!(server->attr_bitmask[2] & FATTR4_WORD2_MODE_UMASK))
			mode &= ~current_umask();

		attr.ia_valid |= ATTR_MODE;
		attr.ia_mode = mode & ~current_umask();
		attr.ia_mode = mode;
	}
	if (open_flags & O_TRUNC) {
		attr.ia_valid |= ATTR_SIZE;
@@ -1554,7 +1553,7 @@ int nfs_atomic_open(struct inode *dir, struct dentry *dentry,
			return finish_no_open(file, dentry);
	}

	ctx = create_nfs_open_context(dentry, open_flags);
	ctx = create_nfs_open_context(dentry, open_flags, file);
	err = PTR_ERR(ctx);
	if (IS_ERR(ctx))
		goto out;
+6 −6
Original line number Diff line number Diff line
@@ -105,7 +105,7 @@ struct nfs_direct_req {

static const struct nfs_pgio_completion_ops nfs_direct_write_completion_ops;
static const struct nfs_commit_completion_ops nfs_direct_commit_completion_ops;
static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode);
static void nfs_direct_write_complete(struct nfs_direct_req *dreq);
static void nfs_direct_write_schedule_work(struct work_struct *work);

static inline void get_dreq(struct nfs_direct_req *dreq)
@@ -684,7 +684,7 @@ static void nfs_direct_write_reschedule(struct nfs_direct_req *dreq)
	}

	if (put_dreq(dreq))
		nfs_direct_write_complete(dreq, dreq->inode);
		nfs_direct_write_complete(dreq);
}

static void nfs_direct_commit_complete(struct nfs_commit_data *data)
@@ -717,7 +717,7 @@ static void nfs_direct_commit_complete(struct nfs_commit_data *data)
	}

	if (atomic_dec_and_test(&cinfo.mds->rpcs_out))
		nfs_direct_write_complete(dreq, data->inode);
		nfs_direct_write_complete(dreq);
}

static void nfs_direct_resched_write(struct nfs_commit_info *cinfo,
@@ -768,7 +768,7 @@ static void nfs_direct_write_schedule_work(struct work_struct *work)
	}
}

static void nfs_direct_write_complete(struct nfs_direct_req *dreq, struct inode *inode)
static void nfs_direct_write_complete(struct nfs_direct_req *dreq)
{
	schedule_work(&dreq->work); /* Calls nfs_direct_write_schedule_work */
}
@@ -824,7 +824,7 @@ static void nfs_direct_write_completion(struct nfs_pgio_header *hdr)

out_put:
	if (put_dreq(dreq))
		nfs_direct_write_complete(dreq, hdr->inode);
		nfs_direct_write_complete(dreq);
	hdr->release(hdr);
}

@@ -953,7 +953,7 @@ static ssize_t nfs_direct_write_schedule_iovec(struct nfs_direct_req *dreq,
	}

	if (put_dreq(dreq))
		nfs_direct_write_complete(dreq, dreq->inode);
		nfs_direct_write_complete(dreq);
	return 0;
}

Loading