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

Commit d51ac1a8 authored by NeilBrown's avatar NeilBrown Committed by Trond Myklebust
Browse files

NFS: prepare for RCU-walk support but pushing tests later in code.



nfs_lookup_revalidate, nfs4_lookup_revalidate, and nfs_permission
all need to understand and handle RCU-walk for NFS to gain the
benefits of RCU-walk for cached information.

Currently these functions all immediately return -ECHILD
if the relevant flag (LOOKUP_RCU or MAY_NOT_BLOCK) is set.

This patch pushes those tests later in the code so that we only abort
immediately before we enter rcu-unsafe code.  As subsequent patches
make that rcu-unsafe code rcu-safe, several of these new tests will
disappear.

With this patch there are several paths through the code which will no
longer return -ECHILD during an RCU-walk.  However these are mostly
error paths or other uninteresting cases.

A noteworthy change in nfs_lookup_revalidate is that we don't take
(or put) the reference to ->d_parent when LOOKUP_RCU is set.
Rather we rcu_dereference ->d_parent, and check that ->d_inode
is not NULL.  We also check that ->d_parent hasn't changed after
all the tests.

In nfs4_lookup_revalidate we simply avoid testing LOOKUP_RCU on the
path that only calls nfs_lookup_revalidate() as that function
already performs the required test.

Signed-off-by: default avatarNeilBrown <neilb@suse.de>
Signed-off-by: default avatarTrond Myklebust <trond.myklebust@primarydata.com>
parent 49317a7f
Loading
Loading
Loading
Loading
+33 −12
Original line number Original line Diff line number Diff line
@@ -1088,21 +1088,30 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
	struct nfs4_label *label = NULL;
	struct nfs4_label *label = NULL;
	int error;
	int error;


	if (flags & LOOKUP_RCU)
	if (flags & LOOKUP_RCU) {
		parent = rcu_dereference(dentry->d_parent);
		dir = ACCESS_ONCE(parent->d_inode);
		if (!dir)
			return -ECHILD;
			return -ECHILD;

	} else {
		parent = dget_parent(dentry);
		parent = dget_parent(dentry);
		dir = parent->d_inode;
		dir = parent->d_inode;
	}
	nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE);
	nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE);
	inode = dentry->d_inode;
	inode = dentry->d_inode;


	if (!inode) {
	if (!inode) {
		if (flags & LOOKUP_RCU)
			return -ECHILD;

		if (nfs_neg_need_reval(dir, dentry, flags))
		if (nfs_neg_need_reval(dir, dentry, flags))
			goto out_bad;
			goto out_bad;
		goto out_valid_noent;
		goto out_valid_noent;
	}
	}


	if (is_bad_inode(inode)) {
	if (is_bad_inode(inode)) {
		if (flags & LOOKUP_RCU)
			return -ECHILD;
		dfprintk(LOOKUPCACHE, "%s: %pd2 has dud inode\n",
		dfprintk(LOOKUPCACHE, "%s: %pd2 has dud inode\n",
				__func__, dentry);
				__func__, dentry);
		goto out_bad;
		goto out_bad;
@@ -1111,6 +1120,9 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags)
	if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ))
	if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ))
		goto out_set_verifier;
		goto out_set_verifier;


	if (flags & LOOKUP_RCU)
		return -ECHILD;

	/* Force a full look up iff the parent directory has changed */
	/* Force a full look up iff the parent directory has changed */
	if (!nfs_is_exclusive_create(dir, flags) && nfs_check_verifier(dir, dentry)) {
	if (!nfs_is_exclusive_create(dir, flags) && nfs_check_verifier(dir, dentry)) {
		if (nfs_lookup_verify_inode(inode, flags))
		if (nfs_lookup_verify_inode(inode, flags))
@@ -1153,6 +1165,10 @@ out_set_verifier:
	/* Success: notify readdir to use READDIRPLUS */
	/* Success: notify readdir to use READDIRPLUS */
	nfs_advise_use_readdirplus(dir);
	nfs_advise_use_readdirplus(dir);
 out_valid_noent:
 out_valid_noent:
	if (flags & LOOKUP_RCU) {
		if (parent != rcu_dereference(dentry->d_parent))
			return -ECHILD;
	} else
		dput(parent);
		dput(parent);
	dfprintk(LOOKUPCACHE, "NFS: %s(%pd2) is valid\n",
	dfprintk(LOOKUPCACHE, "NFS: %s(%pd2) is valid\n",
			__func__, dentry);
			__func__, dentry);
@@ -1160,6 +1176,7 @@ out_set_verifier:
out_zap_parent:
out_zap_parent:
	nfs_zap_caches(dir);
	nfs_zap_caches(dir);
 out_bad:
 out_bad:
	WARN_ON(flags & LOOKUP_RCU);
	nfs_free_fattr(fattr);
	nfs_free_fattr(fattr);
	nfs_free_fhandle(fhandle);
	nfs_free_fhandle(fhandle);
	nfs4_label_free(label);
	nfs4_label_free(label);
@@ -1185,6 +1202,7 @@ out_zap_parent:
			__func__, dentry);
			__func__, dentry);
	return 0;
	return 0;
out_error:
out_error:
	WARN_ON(flags & LOOKUP_RCU);
	nfs_free_fattr(fattr);
	nfs_free_fattr(fattr);
	nfs_free_fhandle(fhandle);
	nfs_free_fhandle(fhandle);
	nfs4_label_free(label);
	nfs4_label_free(label);
@@ -1532,9 +1550,6 @@ static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags)
	struct inode *inode;
	struct inode *inode;
	int ret = 0;
	int ret = 0;


	if (flags & LOOKUP_RCU)
		return -ECHILD;

	if (!(flags & LOOKUP_OPEN) || (flags & LOOKUP_DIRECTORY))
	if (!(flags & LOOKUP_OPEN) || (flags & LOOKUP_DIRECTORY))
		goto no_open;
		goto no_open;
	if (d_mountpoint(dentry))
	if (d_mountpoint(dentry))
@@ -1551,6 +1566,9 @@ static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags)
		struct dentry *parent;
		struct dentry *parent;
		struct inode *dir;
		struct inode *dir;


		if (flags & LOOKUP_RCU)
			return -ECHILD;

		parent = dget_parent(dentry);
		parent = dget_parent(dentry);
		dir = parent->d_inode;
		dir = parent->d_inode;
		if (!nfs_neg_need_reval(dir, dentry, flags))
		if (!nfs_neg_need_reval(dir, dentry, flags))
@@ -2348,9 +2366,6 @@ int nfs_permission(struct inode *inode, int mask)
	struct rpc_cred *cred;
	struct rpc_cred *cred;
	int res = 0;
	int res = 0;


	if (mask & MAY_NOT_BLOCK)
		return -ECHILD;

	nfs_inc_stats(inode, NFSIOS_VFSACCESS);
	nfs_inc_stats(inode, NFSIOS_VFSACCESS);


	if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
	if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0)
@@ -2377,6 +2392,9 @@ force_lookup:
	if (!NFS_PROTO(inode)->access)
	if (!NFS_PROTO(inode)->access)
		goto out_notsup;
		goto out_notsup;


	if (mask & MAY_NOT_BLOCK)
		return -ECHILD;

	cred = rpc_lookup_cred();
	cred = rpc_lookup_cred();
	if (!IS_ERR(cred)) {
	if (!IS_ERR(cred)) {
		res = nfs_do_access(inode, cred, mask);
		res = nfs_do_access(inode, cred, mask);
@@ -2391,6 +2409,9 @@ out:
		inode->i_sb->s_id, inode->i_ino, mask, res);
		inode->i_sb->s_id, inode->i_ino, mask, res);
	return res;
	return res;
out_notsup:
out_notsup:
	if (mask & MAY_NOT_BLOCK)
		return -ECHILD;

	res = nfs_revalidate_inode(NFS_SERVER(inode), inode);
	res = nfs_revalidate_inode(NFS_SERVER(inode), inode);
	if (res == 0)
	if (res == 0)
		res = generic_permission(inode, mask);
		res = generic_permission(inode, mask);