diff options
author | NeilBrown <neilb@suse.de> | 2014-07-13 21:28:20 -0400 |
---|---|---|
committer | Trond Myklebust <trond.myklebust@primarydata.com> | 2014-08-03 17:14:11 -0400 |
commit | d51ac1a8e9b86b2d17d349bb256869cab6522787 (patch) | |
tree | ddb2dc1a448c67d512465eaee7c73cfdbbe4bf23 /fs | |
parent | 49317a7fdaa462b09b9bb4942b64c3a3316bd564 (diff) |
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: NeilBrown <neilb@suse.de>
Signed-off-by: Trond Myklebust <trond.myklebust@primarydata.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfs/dir.c | 45 |
1 files changed, 33 insertions, 12 deletions
diff --git a/fs/nfs/dir.c b/fs/nfs/dir.c index 0090dae1acd3..ea12e58dfd85 100644 --- a/fs/nfs/dir.c +++ b/fs/nfs/dir.c | |||
@@ -1088,21 +1088,30 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags) | |||
1088 | struct nfs4_label *label = NULL; | 1088 | struct nfs4_label *label = NULL; |
1089 | int error; | 1089 | int error; |
1090 | 1090 | ||
1091 | if (flags & LOOKUP_RCU) | 1091 | if (flags & LOOKUP_RCU) { |
1092 | return -ECHILD; | 1092 | parent = rcu_dereference(dentry->d_parent); |
1093 | 1093 | dir = ACCESS_ONCE(parent->d_inode); | |
1094 | parent = dget_parent(dentry); | 1094 | if (!dir) |
1095 | dir = parent->d_inode; | 1095 | return -ECHILD; |
1096 | } else { | ||
1097 | parent = dget_parent(dentry); | ||
1098 | dir = parent->d_inode; | ||
1099 | } | ||
1096 | nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE); | 1100 | nfs_inc_stats(dir, NFSIOS_DENTRYREVALIDATE); |
1097 | inode = dentry->d_inode; | 1101 | inode = dentry->d_inode; |
1098 | 1102 | ||
1099 | if (!inode) { | 1103 | if (!inode) { |
1104 | if (flags & LOOKUP_RCU) | ||
1105 | return -ECHILD; | ||
1106 | |||
1100 | if (nfs_neg_need_reval(dir, dentry, flags)) | 1107 | if (nfs_neg_need_reval(dir, dentry, flags)) |
1101 | goto out_bad; | 1108 | goto out_bad; |
1102 | goto out_valid_noent; | 1109 | goto out_valid_noent; |
1103 | } | 1110 | } |
1104 | 1111 | ||
1105 | if (is_bad_inode(inode)) { | 1112 | if (is_bad_inode(inode)) { |
1113 | if (flags & LOOKUP_RCU) | ||
1114 | return -ECHILD; | ||
1106 | dfprintk(LOOKUPCACHE, "%s: %pd2 has dud inode\n", | 1115 | dfprintk(LOOKUPCACHE, "%s: %pd2 has dud inode\n", |
1107 | __func__, dentry); | 1116 | __func__, dentry); |
1108 | goto out_bad; | 1117 | goto out_bad; |
@@ -1111,6 +1120,9 @@ static int nfs_lookup_revalidate(struct dentry *dentry, unsigned int flags) | |||
1111 | if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ)) | 1120 | if (NFS_PROTO(dir)->have_delegation(inode, FMODE_READ)) |
1112 | goto out_set_verifier; | 1121 | goto out_set_verifier; |
1113 | 1122 | ||
1123 | if (flags & LOOKUP_RCU) | ||
1124 | return -ECHILD; | ||
1125 | |||
1114 | /* Force a full look up iff the parent directory has changed */ | 1126 | /* Force a full look up iff the parent directory has changed */ |
1115 | if (!nfs_is_exclusive_create(dir, flags) && nfs_check_verifier(dir, dentry)) { | 1127 | if (!nfs_is_exclusive_create(dir, flags) && nfs_check_verifier(dir, dentry)) { |
1116 | if (nfs_lookup_verify_inode(inode, flags)) | 1128 | if (nfs_lookup_verify_inode(inode, flags)) |
@@ -1153,13 +1165,18 @@ out_set_verifier: | |||
1153 | /* Success: notify readdir to use READDIRPLUS */ | 1165 | /* Success: notify readdir to use READDIRPLUS */ |
1154 | nfs_advise_use_readdirplus(dir); | 1166 | nfs_advise_use_readdirplus(dir); |
1155 | out_valid_noent: | 1167 | out_valid_noent: |
1156 | dput(parent); | 1168 | if (flags & LOOKUP_RCU) { |
1169 | if (parent != rcu_dereference(dentry->d_parent)) | ||
1170 | return -ECHILD; | ||
1171 | } else | ||
1172 | dput(parent); | ||
1157 | dfprintk(LOOKUPCACHE, "NFS: %s(%pd2) is valid\n", | 1173 | dfprintk(LOOKUPCACHE, "NFS: %s(%pd2) is valid\n", |
1158 | __func__, dentry); | 1174 | __func__, dentry); |
1159 | return 1; | 1175 | return 1; |
1160 | out_zap_parent: | 1176 | out_zap_parent: |
1161 | nfs_zap_caches(dir); | 1177 | nfs_zap_caches(dir); |
1162 | out_bad: | 1178 | out_bad: |
1179 | WARN_ON(flags & LOOKUP_RCU); | ||
1163 | nfs_free_fattr(fattr); | 1180 | nfs_free_fattr(fattr); |
1164 | nfs_free_fhandle(fhandle); | 1181 | nfs_free_fhandle(fhandle); |
1165 | nfs4_label_free(label); | 1182 | nfs4_label_free(label); |
@@ -1185,6 +1202,7 @@ out_zap_parent: | |||
1185 | __func__, dentry); | 1202 | __func__, dentry); |
1186 | return 0; | 1203 | return 0; |
1187 | out_error: | 1204 | out_error: |
1205 | WARN_ON(flags & LOOKUP_RCU); | ||
1188 | nfs_free_fattr(fattr); | 1206 | nfs_free_fattr(fattr); |
1189 | nfs_free_fhandle(fhandle); | 1207 | nfs_free_fhandle(fhandle); |
1190 | nfs4_label_free(label); | 1208 | nfs4_label_free(label); |
@@ -1532,9 +1550,6 @@ static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags) | |||
1532 | struct inode *inode; | 1550 | struct inode *inode; |
1533 | int ret = 0; | 1551 | int ret = 0; |
1534 | 1552 | ||
1535 | if (flags & LOOKUP_RCU) | ||
1536 | return -ECHILD; | ||
1537 | |||
1538 | if (!(flags & LOOKUP_OPEN) || (flags & LOOKUP_DIRECTORY)) | 1553 | if (!(flags & LOOKUP_OPEN) || (flags & LOOKUP_DIRECTORY)) |
1539 | goto no_open; | 1554 | goto no_open; |
1540 | if (d_mountpoint(dentry)) | 1555 | if (d_mountpoint(dentry)) |
@@ -1551,6 +1566,9 @@ static int nfs4_lookup_revalidate(struct dentry *dentry, unsigned int flags) | |||
1551 | struct dentry *parent; | 1566 | struct dentry *parent; |
1552 | struct inode *dir; | 1567 | struct inode *dir; |
1553 | 1568 | ||
1569 | if (flags & LOOKUP_RCU) | ||
1570 | return -ECHILD; | ||
1571 | |||
1554 | parent = dget_parent(dentry); | 1572 | parent = dget_parent(dentry); |
1555 | dir = parent->d_inode; | 1573 | dir = parent->d_inode; |
1556 | if (!nfs_neg_need_reval(dir, dentry, flags)) | 1574 | if (!nfs_neg_need_reval(dir, dentry, flags)) |
@@ -2348,9 +2366,6 @@ int nfs_permission(struct inode *inode, int mask) | |||
2348 | struct rpc_cred *cred; | 2366 | struct rpc_cred *cred; |
2349 | int res = 0; | 2367 | int res = 0; |
2350 | 2368 | ||
2351 | if (mask & MAY_NOT_BLOCK) | ||
2352 | return -ECHILD; | ||
2353 | |||
2354 | nfs_inc_stats(inode, NFSIOS_VFSACCESS); | 2369 | nfs_inc_stats(inode, NFSIOS_VFSACCESS); |
2355 | 2370 | ||
2356 | if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0) | 2371 | if ((mask & (MAY_READ | MAY_WRITE | MAY_EXEC)) == 0) |
@@ -2377,6 +2392,9 @@ force_lookup: | |||
2377 | if (!NFS_PROTO(inode)->access) | 2392 | if (!NFS_PROTO(inode)->access) |
2378 | goto out_notsup; | 2393 | goto out_notsup; |
2379 | 2394 | ||
2395 | if (mask & MAY_NOT_BLOCK) | ||
2396 | return -ECHILD; | ||
2397 | |||
2380 | cred = rpc_lookup_cred(); | 2398 | cred = rpc_lookup_cred(); |
2381 | if (!IS_ERR(cred)) { | 2399 | if (!IS_ERR(cred)) { |
2382 | res = nfs_do_access(inode, cred, mask); | 2400 | res = nfs_do_access(inode, cred, mask); |
@@ -2391,6 +2409,9 @@ out: | |||
2391 | inode->i_sb->s_id, inode->i_ino, mask, res); | 2409 | inode->i_sb->s_id, inode->i_ino, mask, res); |
2392 | return res; | 2410 | return res; |
2393 | out_notsup: | 2411 | out_notsup: |
2412 | if (mask & MAY_NOT_BLOCK) | ||
2413 | return -ECHILD; | ||
2414 | |||
2394 | res = nfs_revalidate_inode(NFS_SERVER(inode), inode); | 2415 | res = nfs_revalidate_inode(NFS_SERVER(inode), inode); |
2395 | if (res == 0) | 2416 | if (res == 0) |
2396 | res = generic_permission(inode, mask); | 2417 | res = generic_permission(inode, mask); |