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

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

  Stable fixes:

   - Fix a 1-byte stack overflow in nfs_idmap_read_and_verify_message

   - Fix a hang due to incorrect error returns in rpcrdma_convert_iovs()

   - Revert an incorrect change to the NFSv4.1 callback channel

   - Fix a bug in the NFSv4.1 sequence error handling

  Features and optimisations:

   - Support for piggybacking a LAYOUTGET operation to the OPEN compound

   - RDMA performance enhancements to deal with transport congestion

   - Add proper SPDX tags for NetApp-contributed RDMA source

   - Do not request delegated file attributes (size+change) from the
     server

   - Optimise away a GETATTR in the lookup revalidate code when doing
     NFSv4 OPEN

   - Optimise away unnecessary lookups for rename targets

   - Misc performance improvements when freeing NFSv4 delegations

  Bugfixes and cleanups:

   - Try to fail quickly if proto=rdma

   - Clean up RDMA receive trace points

   - Fix sillyrename to return the delegation when appropriate

   - Misc attribute revalidation fixes

   - Immediately clear the pNFS layout on a file when the server returns
     ESTALE

   - Return NFS4ERR_DELAY when delegation/layout recalls fail due to
     igrab()

   - Fix the client behaviour on NFS4ERR_SEQ_FALSE_RETRY"

* tag 'nfs-for-4.18-1' of git://git.linux-nfs.org/projects/trondmy/linux-nfs: (80 commits)
  skip LAYOUTRETURN if layout is invalid
  NFSv4.1: Fix the client behaviour on NFS4ERR_SEQ_FALSE_RETRY
  NFSv4: Fix a typo in nfs41_sequence_process
  NFSv4: Revert commit 5f83d86c ("NFSv4.x: Fix wraparound issues..")
  NFSv4: Return NFS4ERR_DELAY when a layout recall fails due to igrab()
  NFSv4: Return NFS4ERR_DELAY when a delegation recall fails due to igrab()
  NFSv4.0: Remove transport protocol name from non-UCS client ID
  NFSv4.0: Remove cl_ipaddr from non-UCS client ID
  NFSv4: Fix a compiler warning when CONFIG_NFS_V4_1 is undefined
  NFS: Filter cache invalidation when holding a delegation
  NFS: Ignore NFS_INO_REVAL_FORCED in nfs_check_inode_attributes()
  NFS: Improve caching while holding a delegation
  NFS: Fix attribute revalidation
  NFS: fix up nfs_setattr_update_inode
  NFSv4: Ensure the inode is clean when we set a delegation
  NFSv4: Ignore NFS_INO_REVAL_FORCED in nfs4_proc_access
  NFSv4: Don't ask for delegated attributes when adding a hard link
  NFSv4: Don't ask for delegated attributes when revalidating the inode
  NFS: Pass the inode down to the getattr() callback
  NFSv4: Don't request size+change attribute if they are delegated to us
  ...
parents 89e25567 93b7f7ad
Loading
Loading
Loading
Loading
+24 −19
Original line number Diff line number Diff line
@@ -40,7 +40,9 @@ __be32 nfs4_callback_getattr(void *argp, void *resp,
		rpc_peeraddr2str(cps->clp->cl_rpcclient, RPC_DISPLAY_ADDR));

	inode = nfs_delegation_find_inode(cps->clp, &args->fh);
	if (inode == NULL) {
	if (IS_ERR(inode)) {
		if (inode == ERR_PTR(-EAGAIN))
			res->status = htonl(NFS4ERR_DELAY);
		trace_nfs4_cb_getattr(cps->clp, &args->fh, NULL,
				-ntohl(res->status));
		goto out;
@@ -86,7 +88,9 @@ __be32 nfs4_callback_recall(void *argp, void *resp,

	res = htonl(NFS4ERR_BADHANDLE);
	inode = nfs_delegation_find_inode(cps->clp, &args->fh);
	if (inode == NULL) {
	if (IS_ERR(inode)) {
		if (inode == ERR_PTR(-EAGAIN))
			res = htonl(NFS4ERR_DELAY);
		trace_nfs4_cb_recall(cps->clp, &args->fh, NULL,
				&args->stateid, -ntohl(res));
		goto out;
@@ -124,7 +128,6 @@ static struct inode *nfs_layout_find_inode_by_stateid(struct nfs_client *clp,
	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 &&
@@ -132,20 +135,20 @@ static struct inode *nfs_layout_find_inode_by_stateid(struct nfs_client *clp,
				continue;
			inode = igrab(lo->plh_inode);
			if (!inode)
				continue;
				return ERR_PTR(-EAGAIN);
			if (!nfs_sb_active(inode->i_sb)) {
				rcu_read_unlock();
				spin_unlock(&clp->cl_lock);
				iput(inode);
				spin_lock(&clp->cl_lock);
				rcu_read_lock();
				goto restart;
				return ERR_PTR(-EAGAIN);
			}
			return inode;
		}
	}

	return NULL;
	return ERR_PTR(-ENOENT);
}

/*
@@ -162,7 +165,6 @@ static struct inode *nfs_layout_find_inode_by_fh(struct nfs_client *clp,
	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) {
			nfsi = NFS_I(lo->plh_inode);
@@ -172,20 +174,20 @@ static struct inode *nfs_layout_find_inode_by_fh(struct nfs_client *clp,
				continue;
			inode = igrab(lo->plh_inode);
			if (!inode)
				continue;
				return ERR_PTR(-EAGAIN);
			if (!nfs_sb_active(inode->i_sb)) {
				rcu_read_unlock();
				spin_unlock(&clp->cl_lock);
				iput(inode);
				spin_lock(&clp->cl_lock);
				rcu_read_lock();
				goto restart;
				return ERR_PTR(-EAGAIN);
			}
			return inode;
		}
	}

	return NULL;
	return ERR_PTR(-ENOENT);
}

static struct inode *nfs_layout_find_inode(struct nfs_client *clp,
@@ -197,7 +199,7 @@ static struct inode *nfs_layout_find_inode(struct nfs_client *clp,
	spin_lock(&clp->cl_lock);
	rcu_read_lock();
	inode = nfs_layout_find_inode_by_stateid(clp, stateid);
	if (!inode)
	if (inode == ERR_PTR(-ENOENT))
		inode = nfs_layout_find_inode_by_fh(clp, fh);
	rcu_read_unlock();
	spin_unlock(&clp->cl_lock);
@@ -252,8 +254,11 @@ static u32 initiate_file_draining(struct nfs_client *clp,
	LIST_HEAD(free_me_list);

	ino = nfs_layout_find_inode(clp, &args->cbl_fh, &args->cbl_stateid);
	if (!ino)
		goto out;
	if (IS_ERR(ino)) {
		if (ino == ERR_PTR(-EAGAIN))
			rv = NFS4ERR_DELAY;
		goto out_noput;
	}

	pnfs_layoutcommit_inode(ino, false);

@@ -299,9 +304,10 @@ static u32 initiate_file_draining(struct nfs_client *clp,
	nfs_commit_inode(ino, 0);
	pnfs_put_layout_hdr(lo);
out:
	nfs_iput_and_deactive(ino);
out_noput:
	trace_nfs4_cb_layoutrecall_file(clp, &args->cbl_fh, ino,
			&args->cbl_stateid, -rv);
	nfs_iput_and_deactive(ino);
	return rv;
}

@@ -322,6 +328,8 @@ static u32 initiate_bulk_draining(struct nfs_client *clp,
static u32 do_callback_layoutrecall(struct nfs_client *clp,
				    struct cb_layoutrecallargs *args)
{
	write_seqcount_begin(&clp->cl_callback_count);
	write_seqcount_end(&clp->cl_callback_count);
	if (args->cbl_recall_type == RETURN_FILE)
		return initiate_file_draining(clp, args);
	return initiate_bulk_draining(clp, args);
@@ -420,11 +428,8 @@ validate_seqid(const struct nfs4_slot_table *tbl, const struct nfs4_slot *slot,
		return htonl(NFS4ERR_SEQ_FALSE_RETRY);
	}

	/* Wraparound */
	if (unlikely(slot->seq_nr == 0xFFFFFFFFU)) {
		if (args->csa_sequenceid == 1)
			return htonl(NFS4_OK);
	} else if (likely(args->csa_sequenceid == slot->seq_nr + 1))
	/* Note: wraparound relies on seq_nr being of type u32 */
	if (likely(args->csa_sequenceid == slot->seq_nr + 1))
		return htonl(NFS4_OK);

	/* Misordered request */
+2 −1
Original line number Diff line number Diff line
@@ -969,7 +969,8 @@ struct nfs_server *nfs_create_server(struct nfs_mount_info *mount_info,
	}

	if (!(fattr->valid & NFS_ATTR_FATTR)) {
		error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh, fattr, NULL);
		error = nfs_mod->rpc_ops->getattr(server, mount_info->mntfh,
				fattr, NULL, NULL);
		if (error < 0) {
			dprintk("nfs_create_server: getattr error = %d\n", -error);
			goto error;
+72 −14
Original line number Diff line number Diff line
@@ -404,6 +404,10 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred,

	trace_nfs4_set_delegation(inode, type);

	spin_lock(&inode->i_lock);
	if (NFS_I(inode)->cache_validity & (NFS_INO_INVALID_ATTR|NFS_INO_INVALID_ATIME))
		NFS_I(inode)->cache_validity |= NFS_INO_REVAL_FORCED;
	spin_unlock(&inode->i_lock);
out:
	spin_unlock(&clp->cl_lock);
	if (delegation != NULL)
@@ -483,38 +487,88 @@ static bool nfs_delegation_need_return(struct nfs_delegation *delegation)
int nfs_client_return_marked_delegations(struct nfs_client *clp)
{
	struct nfs_delegation *delegation;
	struct nfs_delegation *prev;
	struct nfs_server *server;
	struct inode *inode;
	struct inode *place_holder = NULL;
	struct nfs_delegation *place_holder_deleg = NULL;
	int err = 0;

restart:
	/*
	 * To avoid quadratic looping we hold a reference
	 * to an inode place_holder.  Each time we restart, we
	 * list nfs_servers from the server of that inode, and
	 * delegation in the server from the delegations of that
	 * inode.
	 * prev is an RCU-protected pointer to a delegation which
	 * wasn't marked for return and might be a good choice for
	 * the next place_holder.
	 */
	rcu_read_lock();
	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
		list_for_each_entry_rcu(delegation, &server->delegations,
								super_list) {
			if (!nfs_delegation_need_return(delegation))
	prev = NULL;
	if (place_holder)
		server = NFS_SERVER(place_holder);
	else
		server = list_entry_rcu(clp->cl_superblocks.next,
					struct nfs_server, client_link);
	list_for_each_entry_from_rcu(server, &clp->cl_superblocks, client_link) {
		delegation = NULL;
		if (place_holder && server == NFS_SERVER(place_holder))
			delegation = rcu_dereference(NFS_I(place_holder)->delegation);
		if (!delegation || delegation != place_holder_deleg)
			delegation = list_entry_rcu(server->delegations.next,
						    struct nfs_delegation, super_list);
		list_for_each_entry_from_rcu(delegation, &server->delegations, super_list) {
			struct inode *to_put = NULL;

			if (!nfs_delegation_need_return(delegation)) {
				prev = delegation;
				continue;
			}
			if (!nfs_sb_active(server->super))
				continue;
				break; /* continue in outer loop */

			if (prev) {
				struct inode *tmp;

				tmp = nfs_delegation_grab_inode(prev);
				if (tmp) {
					to_put = place_holder;
					place_holder = tmp;
					place_holder_deleg = prev;
				}
			}

			inode = nfs_delegation_grab_inode(delegation);
			if (inode == NULL) {
				rcu_read_unlock();
				if (to_put)
					iput(to_put);
				nfs_sb_deactive(server->super);
				goto restart;
			}
			delegation = nfs_start_delegation_return_locked(NFS_I(inode));
			rcu_read_unlock();

			if (to_put)
				iput(to_put);

			err = nfs_end_delegation_return(inode, delegation, 0);
			iput(inode);
			nfs_sb_deactive(server->super);
			cond_resched();
			if (!err)
				goto restart;
			set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state);
			if (place_holder)
				iput(place_holder);
			return err;
		}
	}
	rcu_read_unlock();
	if (place_holder)
		iput(place_holder);
	return 0;
}

@@ -802,12 +856,14 @@ nfs_delegation_find_inode_server(struct nfs_server *server,
		if (delegation->inode != NULL &&
		    nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) {
			res = igrab(delegation->inode);
		}
			spin_unlock(&delegation->lock);
			if (res != NULL)
			break;
	}
				return res;
			return ERR_PTR(-EAGAIN);
		}
		spin_unlock(&delegation->lock);
	}
	return ERR_PTR(-ENOENT);
}

/**
@@ -822,16 +878,16 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp,
					const struct nfs_fh *fhandle)
{
	struct nfs_server *server;
	struct inode *res = NULL;
	struct inode *res;

	rcu_read_lock();
	list_for_each_entry_rcu(server, &clp->cl_superblocks, client_link) {
		res = nfs_delegation_find_inode_server(server, fhandle);
		if (res != NULL)
			break;
		if (res != ERR_PTR(-ENOENT))
			return res;
	}
	rcu_read_unlock();
	return res;
	return ERR_PTR(-ENOENT);
}

static void nfs_delegation_mark_reclaim_server(struct nfs_server *server)
@@ -887,7 +943,7 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
						&delegation->flags) == 0)
				continue;
			if (!nfs_sb_active(server->super))
				continue;
				break; /* continue in outer loop */
			inode = nfs_delegation_grab_inode(delegation);
			if (inode == NULL) {
				rcu_read_unlock();
@@ -904,6 +960,7 @@ void nfs_delegation_reap_unclaimed(struct nfs_client *clp)
			}
			iput(inode);
			nfs_sb_deactive(server->super);
			cond_resched();
			goto restart;
		}
	}
@@ -995,7 +1052,7 @@ void nfs_reap_expired_delegations(struct nfs_client *clp)
						&delegation->flags) == 0)
				continue;
			if (!nfs_sb_active(server->super))
				continue;
				break; /* continue in outer loop */
			inode = nfs_delegation_grab_inode(delegation);
			if (inode == NULL) {
				rcu_read_unlock();
@@ -1020,6 +1077,7 @@ void nfs_reap_expired_delegations(struct nfs_client *clp)
			}
			iput(inode);
			nfs_sb_deactive(server->super);
			cond_resched();
			goto restart;
		}
	}
+38 −13
Original line number Diff line number Diff line
@@ -1012,13 +1012,25 @@ int nfs_lookup_verify_inode(struct inode *inode, unsigned int flags)

	if (IS_AUTOMOUNT(inode))
		return 0;

	if (flags & LOOKUP_OPEN) {
		switch (inode->i_mode & S_IFMT) {
		case S_IFREG:
			/* A NFSv4 OPEN will revalidate later */
			if (server->caps & NFS_CAP_ATOMIC_OPEN)
				goto out;
			/* Fallthrough */
		case S_IFDIR:
			if (server->flags & NFS_MOUNT_NOCTO)
				break;
			/* NFS close-to-open cache consistency validation */
			goto out_force;
		}
	}

	/* VFS wants an on-the-wire revalidation */
	if (flags & LOOKUP_REVAL)
		goto out_force;
	/* This is an open(2) */
	if ((flags & LOOKUP_OPEN) && !(server->flags & NFS_MOUNT_NOCTO) &&
	    (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
		goto out_force;
out:
	return (inode->i_nlink == 0) ? -ENOENT : 0;
out_force:
@@ -1039,13 +1051,15 @@ int nfs_lookup_verify_inode(struct inode *inode, unsigned int flags)
 *
 * If LOOKUP_RCU prevents us from performing a full check, return 1
 * suggesting a reval is needed.
 *
 * Note that when creating a new file, or looking up a rename target,
 * then it shouldn't be necessary to revalidate a negative dentry.
 */
static inline
int nfs_neg_need_reval(struct inode *dir, struct dentry *dentry,
		       unsigned int flags)
{
	/* Don't revalidate a negative dentry if we're creating a new file */
	if (flags & LOOKUP_CREATE)
	if (flags & (LOOKUP_CREATE | LOOKUP_RENAME_TARGET))
		return 0;
	if (NFS_SERVER(dir)->flags & NFS_MOUNT_LOOKUP_CACHE_NONEG)
		return 1;
@@ -1106,7 +1120,7 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
		goto out_set_verifier;

	/* Force a full look up iff the parent directory has changed */
	if (!nfs_is_exclusive_create(dir, flags) &&
	if (!(flags & (LOOKUP_EXCL | LOOKUP_REVAL)) &&
	    nfs_check_verifier(dir, dentry, flags & LOOKUP_RCU)) {
		error = nfs_lookup_verify_inode(inode, flags);
		if (error) {
@@ -1270,11 +1284,13 @@ static void nfs_drop_nlink(struct inode *inode)
{
	spin_lock(&inode->i_lock);
	/* drop the inode if we're reasonably sure this is the last link */
	if (inode->i_nlink == 1)
		clear_nlink(inode);
	if (inode->i_nlink > 0)
		drop_nlink(inode);
	NFS_I(inode)->attr_gencount = nfs_inc_attr_generation_counter();
	NFS_I(inode)->cache_validity |= NFS_INO_INVALID_CHANGE
		| NFS_INO_INVALID_CTIME
		| NFS_INO_INVALID_OTHER;
		| NFS_INO_INVALID_OTHER
		| NFS_INO_REVAL_FORCED;
	spin_unlock(&inode->i_lock);
}

@@ -1335,7 +1351,7 @@ struct dentry *nfs_lookup(struct inode *dir, struct dentry * dentry, unsigned in
	 * If we're doing an exclusive create, optimize away the lookup
	 * but don't hash the dentry.
	 */
	if (nfs_is_exclusive_create(dir, flags))
	if (nfs_is_exclusive_create(dir, flags) || flags & LOOKUP_RENAME_TARGET)
		return NULL;

	res = ERR_PTR(-ENOMEM);
@@ -1640,7 +1656,8 @@ int nfs_instantiate(struct dentry *dentry, struct nfs_fh *fhandle,
	nfs_set_verifier(dentry, nfs_save_change_attribute(dir));
	if (!(fattr->valid & NFS_ATTR_FATTR)) {
		struct nfs_server *server = NFS_SB(dentry->d_sb);
		error = server->nfs_client->rpc_ops->getattr(server, fhandle, fattr, NULL);
		error = server->nfs_client->rpc_ops->getattr(server, fhandle,
				fattr, NULL, NULL);
		if (error < 0)
			goto out_error;
	}
@@ -2036,7 +2053,15 @@ int nfs_rename(struct inode *old_dir, struct dentry *old_dentry,
	} else
		error = task->tk_status;
	rpc_put_task(task);
	nfs_mark_for_revalidate(old_inode);
	/* Ensure the inode attributes are revalidated */
	if (error == 0) {
		spin_lock(&old_inode->i_lock);
		NFS_I(old_inode)->attr_gencount = nfs_inc_attr_generation_counter();
		NFS_I(old_inode)->cache_validity |= NFS_INO_INVALID_CHANGE
			| NFS_INO_INVALID_CTIME
			| NFS_INO_REVAL_FORCED;
		spin_unlock(&old_inode->i_lock);
	}
out:
	if (rehash)
		d_rehash(rehash);
+1 −1
Original line number Diff line number Diff line
@@ -102,7 +102,7 @@ nfs_fh_to_dentry(struct super_block *sb, struct fid *fid,
	}

	rpc_ops = NFS_SB(sb)->nfs_client->rpc_ops;
	ret = rpc_ops->getattr(NFS_SB(sb), server_fh, fattr, label);
	ret = rpc_ops->getattr(NFS_SB(sb), server_fh, fattr, label, NULL);
	if (ret) {
		dprintk("%s: getattr failed %d\n", __func__, ret);
		dentry = ERR_PTR(ret);
Loading