diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-07-06 15:12:04 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-07-10 23:40:41 -0400 |
commit | 8383e4602c89857ef926f29ca61ac0a83a614443 (patch) | |
tree | c60d44dbbfccfa6976d61fb7b10f9f97cc6cda06 | |
parent | 13437e12fb43cb7e285ff59248f781c91578eafe (diff) |
NFSv4: Use RCU to protect delegations
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/nfs/delegation.c | 114 | ||||
-rw-r--r-- | fs/nfs/delegation.h | 14 |
2 files changed, 73 insertions, 55 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 93a9f4bd9bd0..56f4f6a99d4e 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c | |||
@@ -27,6 +27,13 @@ static void nfs_free_delegation(struct nfs_delegation *delegation) | |||
27 | kfree(delegation); | 27 | kfree(delegation); |
28 | } | 28 | } |
29 | 29 | ||
30 | static void nfs_free_delegation_callback(struct rcu_head *head) | ||
31 | { | ||
32 | struct nfs_delegation *delegation = container_of(head, struct nfs_delegation, rcu); | ||
33 | |||
34 | nfs_free_delegation(delegation); | ||
35 | } | ||
36 | |||
30 | static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state) | 37 | static int nfs_delegation_claim_locks(struct nfs_open_context *ctx, struct nfs4_state *state) |
31 | { | 38 | { |
32 | struct inode *inode = state->inode; | 39 | struct inode *inode = state->inode; |
@@ -133,10 +140,10 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
133 | delegation->inode = inode; | 140 | delegation->inode = inode; |
134 | 141 | ||
135 | spin_lock(&clp->cl_lock); | 142 | spin_lock(&clp->cl_lock); |
136 | if (nfsi->delegation == NULL) { | 143 | if (rcu_dereference(nfsi->delegation) == NULL) { |
137 | list_add(&delegation->super_list, &clp->cl_delegations); | 144 | list_add_rcu(&delegation->super_list, &clp->cl_delegations); |
138 | nfsi->delegation = delegation; | ||
139 | nfsi->delegation_state = delegation->type; | 145 | nfsi->delegation_state = delegation->type; |
146 | rcu_assign_pointer(nfsi->delegation, delegation); | ||
140 | delegation = NULL; | 147 | delegation = NULL; |
141 | } else { | 148 | } else { |
142 | if (memcmp(&delegation->stateid, &nfsi->delegation->stateid, | 149 | if (memcmp(&delegation->stateid, &nfsi->delegation->stateid, |
@@ -157,7 +164,7 @@ static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation * | |||
157 | int res = 0; | 164 | int res = 0; |
158 | 165 | ||
159 | res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid); | 166 | res = nfs4_proc_delegreturn(inode, delegation->cred, &delegation->stateid); |
160 | nfs_free_delegation(delegation); | 167 | call_rcu(&delegation->rcu, nfs_free_delegation_callback); |
161 | return res; | 168 | return res; |
162 | } | 169 | } |
163 | 170 | ||
@@ -191,16 +198,16 @@ static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegat | |||
191 | 198 | ||
192 | static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) | 199 | static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) |
193 | { | 200 | { |
194 | struct nfs_delegation *delegation = nfsi->delegation; | 201 | struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); |
195 | 202 | ||
196 | if (delegation == NULL) | 203 | if (delegation == NULL) |
197 | goto nomatch; | 204 | goto nomatch; |
198 | if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data, | 205 | if (stateid != NULL && memcmp(delegation->stateid.data, stateid->data, |
199 | sizeof(delegation->stateid.data)) != 0) | 206 | sizeof(delegation->stateid.data)) != 0) |
200 | goto nomatch; | 207 | goto nomatch; |
201 | list_del_init(&delegation->super_list); | 208 | list_del_rcu(&delegation->super_list); |
202 | nfsi->delegation = NULL; | ||
203 | nfsi->delegation_state = 0; | 209 | nfsi->delegation_state = 0; |
210 | rcu_assign_pointer(nfsi->delegation, NULL); | ||
204 | return delegation; | 211 | return delegation; |
205 | nomatch: | 212 | nomatch: |
206 | return NULL; | 213 | return NULL; |
@@ -213,7 +220,7 @@ int nfs_inode_return_delegation(struct inode *inode) | |||
213 | struct nfs_delegation *delegation; | 220 | struct nfs_delegation *delegation; |
214 | int err = 0; | 221 | int err = 0; |
215 | 222 | ||
216 | if (nfsi->delegation_state != 0) { | 223 | if (rcu_dereference(nfsi->delegation) != NULL) { |
217 | spin_lock(&clp->cl_lock); | 224 | spin_lock(&clp->cl_lock); |
218 | delegation = nfs_detach_delegation_locked(nfsi, NULL); | 225 | delegation = nfs_detach_delegation_locked(nfsi, NULL); |
219 | spin_unlock(&clp->cl_lock); | 226 | spin_unlock(&clp->cl_lock); |
@@ -235,20 +242,23 @@ void nfs_return_all_delegations(struct super_block *sb) | |||
235 | if (clp == NULL) | 242 | if (clp == NULL) |
236 | return; | 243 | return; |
237 | restart: | 244 | restart: |
238 | spin_lock(&clp->cl_lock); | 245 | rcu_read_lock(); |
239 | list_for_each_entry(delegation, &clp->cl_delegations, super_list) { | 246 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { |
240 | if (delegation->inode->i_sb != sb) | 247 | if (delegation->inode->i_sb != sb) |
241 | continue; | 248 | continue; |
242 | inode = igrab(delegation->inode); | 249 | inode = igrab(delegation->inode); |
243 | if (inode == NULL) | 250 | if (inode == NULL) |
244 | continue; | 251 | continue; |
245 | nfs_detach_delegation_locked(NFS_I(inode), NULL); | 252 | spin_lock(&clp->cl_lock); |
253 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); | ||
246 | spin_unlock(&clp->cl_lock); | 254 | spin_unlock(&clp->cl_lock); |
247 | __nfs_inode_return_delegation(inode, delegation); | 255 | rcu_read_unlock(); |
256 | if (delegation != NULL) | ||
257 | __nfs_inode_return_delegation(inode, delegation); | ||
248 | iput(inode); | 258 | iput(inode); |
249 | goto restart; | 259 | goto restart; |
250 | } | 260 | } |
251 | spin_unlock(&clp->cl_lock); | 261 | rcu_read_unlock(); |
252 | } | 262 | } |
253 | 263 | ||
254 | static int nfs_do_expire_all_delegations(void *ptr) | 264 | static int nfs_do_expire_all_delegations(void *ptr) |
@@ -259,23 +269,26 @@ static int nfs_do_expire_all_delegations(void *ptr) | |||
259 | 269 | ||
260 | allow_signal(SIGKILL); | 270 | allow_signal(SIGKILL); |
261 | restart: | 271 | restart: |
262 | spin_lock(&clp->cl_lock); | ||
263 | if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) != 0) | 272 | if (test_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) != 0) |
264 | goto out; | 273 | goto out; |
265 | if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) | 274 | if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state) == 0) |
266 | goto out; | 275 | goto out; |
267 | list_for_each_entry(delegation, &clp->cl_delegations, super_list) { | 276 | rcu_read_lock(); |
277 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { | ||
268 | inode = igrab(delegation->inode); | 278 | inode = igrab(delegation->inode); |
269 | if (inode == NULL) | 279 | if (inode == NULL) |
270 | continue; | 280 | continue; |
271 | nfs_detach_delegation_locked(NFS_I(inode), NULL); | 281 | spin_lock(&clp->cl_lock); |
282 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); | ||
272 | spin_unlock(&clp->cl_lock); | 283 | spin_unlock(&clp->cl_lock); |
273 | __nfs_inode_return_delegation(inode, delegation); | 284 | rcu_read_unlock(); |
285 | if (delegation) | ||
286 | __nfs_inode_return_delegation(inode, delegation); | ||
274 | iput(inode); | 287 | iput(inode); |
275 | goto restart; | 288 | goto restart; |
276 | } | 289 | } |
290 | rcu_read_unlock(); | ||
277 | out: | 291 | out: |
278 | spin_unlock(&clp->cl_lock); | ||
279 | nfs_put_client(clp); | 292 | nfs_put_client(clp); |
280 | module_put_and_exit(0); | 293 | module_put_and_exit(0); |
281 | } | 294 | } |
@@ -306,18 +319,21 @@ void nfs_handle_cb_pathdown(struct nfs_client *clp) | |||
306 | if (clp == NULL) | 319 | if (clp == NULL) |
307 | return; | 320 | return; |
308 | restart: | 321 | restart: |
309 | spin_lock(&clp->cl_lock); | 322 | rcu_read_lock(); |
310 | list_for_each_entry(delegation, &clp->cl_delegations, super_list) { | 323 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { |
311 | inode = igrab(delegation->inode); | 324 | inode = igrab(delegation->inode); |
312 | if (inode == NULL) | 325 | if (inode == NULL) |
313 | continue; | 326 | continue; |
314 | nfs_detach_delegation_locked(NFS_I(inode), NULL); | 327 | spin_lock(&clp->cl_lock); |
328 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); | ||
315 | spin_unlock(&clp->cl_lock); | 329 | spin_unlock(&clp->cl_lock); |
316 | __nfs_inode_return_delegation(inode, delegation); | 330 | rcu_read_unlock(); |
331 | if (delegation != NULL) | ||
332 | __nfs_inode_return_delegation(inode, delegation); | ||
317 | iput(inode); | 333 | iput(inode); |
318 | goto restart; | 334 | goto restart; |
319 | } | 335 | } |
320 | spin_unlock(&clp->cl_lock); | 336 | rcu_read_unlock(); |
321 | } | 337 | } |
322 | 338 | ||
323 | struct recall_threadargs { | 339 | struct recall_threadargs { |
@@ -391,14 +407,14 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs | |||
391 | { | 407 | { |
392 | struct nfs_delegation *delegation; | 408 | struct nfs_delegation *delegation; |
393 | struct inode *res = NULL; | 409 | struct inode *res = NULL; |
394 | spin_lock(&clp->cl_lock); | 410 | rcu_read_lock(); |
395 | list_for_each_entry(delegation, &clp->cl_delegations, super_list) { | 411 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { |
396 | if (nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { | 412 | if (nfs_compare_fh(fhandle, &NFS_I(delegation->inode)->fh) == 0) { |
397 | res = igrab(delegation->inode); | 413 | res = igrab(delegation->inode); |
398 | break; | 414 | break; |
399 | } | 415 | } |
400 | } | 416 | } |
401 | spin_unlock(&clp->cl_lock); | 417 | rcu_read_unlock(); |
402 | return res; | 418 | return res; |
403 | } | 419 | } |
404 | 420 | ||
@@ -408,10 +424,10 @@ struct inode *nfs_delegation_find_inode(struct nfs_client *clp, const struct nfs | |||
408 | void nfs_delegation_mark_reclaim(struct nfs_client *clp) | 424 | void nfs_delegation_mark_reclaim(struct nfs_client *clp) |
409 | { | 425 | { |
410 | struct nfs_delegation *delegation; | 426 | struct nfs_delegation *delegation; |
411 | spin_lock(&clp->cl_lock); | 427 | rcu_read_lock(); |
412 | list_for_each_entry(delegation, &clp->cl_delegations, super_list) | 428 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) |
413 | delegation->flags |= NFS_DELEGATION_NEED_RECLAIM; | 429 | delegation->flags |= NFS_DELEGATION_NEED_RECLAIM; |
414 | spin_unlock(&clp->cl_lock); | 430 | rcu_read_unlock(); |
415 | } | 431 | } |
416 | 432 | ||
417 | /* | 433 | /* |
@@ -419,39 +435,35 @@ void nfs_delegation_mark_reclaim(struct nfs_client *clp) | |||
419 | */ | 435 | */ |
420 | void nfs_delegation_reap_unclaimed(struct nfs_client *clp) | 436 | void nfs_delegation_reap_unclaimed(struct nfs_client *clp) |
421 | { | 437 | { |
422 | struct nfs_delegation *delegation, *n; | 438 | struct nfs_delegation *delegation; |
423 | LIST_HEAD(head); | 439 | restart: |
424 | spin_lock(&clp->cl_lock); | 440 | rcu_read_lock(); |
425 | list_for_each_entry_safe(delegation, n, &clp->cl_delegations, super_list) { | 441 | list_for_each_entry_rcu(delegation, &clp->cl_delegations, super_list) { |
426 | if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0) | 442 | if ((delegation->flags & NFS_DELEGATION_NEED_RECLAIM) == 0) |
427 | continue; | 443 | continue; |
428 | list_move(&delegation->super_list, &head); | 444 | spin_lock(&clp->cl_lock); |
429 | NFS_I(delegation->inode)->delegation = NULL; | 445 | delegation = nfs_detach_delegation_locked(NFS_I(delegation->inode), NULL); |
430 | NFS_I(delegation->inode)->delegation_state = 0; | 446 | spin_unlock(&clp->cl_lock); |
431 | } | 447 | rcu_read_unlock(); |
432 | spin_unlock(&clp->cl_lock); | 448 | if (delegation != NULL) |
433 | while(!list_empty(&head)) { | 449 | call_rcu(&delegation->rcu, nfs_free_delegation_callback); |
434 | delegation = list_entry(head.next, struct nfs_delegation, super_list); | 450 | goto restart; |
435 | list_del(&delegation->super_list); | ||
436 | nfs_free_delegation(delegation); | ||
437 | } | 451 | } |
452 | rcu_read_unlock(); | ||
438 | } | 453 | } |
439 | 454 | ||
440 | int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode) | 455 | int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode) |
441 | { | 456 | { |
442 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; | ||
443 | struct nfs_inode *nfsi = NFS_I(inode); | 457 | struct nfs_inode *nfsi = NFS_I(inode); |
444 | struct nfs_delegation *delegation; | 458 | struct nfs_delegation *delegation; |
445 | int res = 0; | 459 | int ret = 0; |
446 | 460 | ||
447 | if (nfsi->delegation_state == 0) | 461 | rcu_read_lock(); |
448 | return 0; | 462 | delegation = rcu_dereference(nfsi->delegation); |
449 | spin_lock(&clp->cl_lock); | ||
450 | delegation = nfsi->delegation; | ||
451 | if (delegation != NULL) { | 463 | if (delegation != NULL) { |
452 | memcpy(dst->data, delegation->stateid.data, sizeof(dst->data)); | 464 | memcpy(dst->data, delegation->stateid.data, sizeof(dst->data)); |
453 | res = 1; | 465 | ret = 1; |
454 | } | 466 | } |
455 | spin_unlock(&clp->cl_lock); | 467 | rcu_read_unlock(); |
456 | return res; | 468 | return ret; |
457 | } | 469 | } |
diff --git a/fs/nfs/delegation.h b/fs/nfs/delegation.h index 8f79a3135167..5874ce7fdbae 100644 --- a/fs/nfs/delegation.h +++ b/fs/nfs/delegation.h | |||
@@ -22,6 +22,7 @@ struct nfs_delegation { | |||
22 | long flags; | 22 | long flags; |
23 | loff_t maxsize; | 23 | loff_t maxsize; |
24 | __u64 change_attr; | 24 | __u64 change_attr; |
25 | struct rcu_head rcu; | ||
25 | }; | 26 | }; |
26 | 27 | ||
27 | int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); | 28 | int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res); |
@@ -45,11 +46,16 @@ int nfs4_copy_delegation_stateid(nfs4_stateid *dst, struct inode *inode); | |||
45 | 46 | ||
46 | static inline int nfs_have_delegation(struct inode *inode, int flags) | 47 | static inline int nfs_have_delegation(struct inode *inode, int flags) |
47 | { | 48 | { |
49 | struct nfs_delegation *delegation; | ||
50 | int ret = 0; | ||
51 | |||
48 | flags &= FMODE_READ|FMODE_WRITE; | 52 | flags &= FMODE_READ|FMODE_WRITE; |
49 | smp_rmb(); | 53 | rcu_read_lock(); |
50 | if ((NFS_I(inode)->delegation_state & flags) == flags) | 54 | delegation = rcu_dereference(NFS_I(inode)->delegation); |
51 | return 1; | 55 | if (delegation != NULL && (delegation->type & flags) == flags) |
52 | return 0; | 56 | ret = 1; |
57 | rcu_read_unlock(); | ||
58 | return ret; | ||
53 | } | 59 | } |
54 | 60 | ||
55 | #else | 61 | #else |