diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-07-26 12:06:17 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2007-08-07 15:13:17 -0400 |
commit | 5e11934d13c9a3bcb0cadad6c7a7de5c32660422 (patch) | |
tree | 639e5660e9081bc16afccf0c509ff41c413b483d | |
parent | b247bbf1da69ce376aa1ceb8057331214589e366 (diff) |
NFS: Fix put_nfs_open_context
We need to grab the inode->i_lock atomically with the last reference put in
order to remove the open context that is being freed from the
nfsi->open_files list.
Fix by converting the kref to a standard atomic counter and then using
atomic_dec_and_lock()...
Thanks to Arnd Bergmann for pointing out the problem.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
-rw-r--r-- | fs/nfs/inode.c | 24 | ||||
-rw-r--r-- | include/linux/nfs_fs.h | 2 |
2 files changed, 9 insertions, 17 deletions
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index bca6cdcb9f0d..71a49c3acabd 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -468,7 +468,7 @@ static struct nfs_open_context *alloc_nfs_open_context(struct vfsmount *mnt, str | |||
468 | ctx->lockowner = current->files; | 468 | ctx->lockowner = current->files; |
469 | ctx->error = 0; | 469 | ctx->error = 0; |
470 | ctx->dir_cookie = 0; | 470 | ctx->dir_cookie = 0; |
471 | kref_init(&ctx->kref); | 471 | atomic_set(&ctx->count, 1); |
472 | } | 472 | } |
473 | return ctx; | 473 | return ctx; |
474 | } | 474 | } |
@@ -476,21 +476,18 @@ static struct nfs_open_context *alloc_nfs_open_context(struct vfsmount *mnt, str | |||
476 | struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx) | 476 | struct nfs_open_context *get_nfs_open_context(struct nfs_open_context *ctx) |
477 | { | 477 | { |
478 | if (ctx != NULL) | 478 | if (ctx != NULL) |
479 | kref_get(&ctx->kref); | 479 | atomic_inc(&ctx->count); |
480 | return ctx; | 480 | return ctx; |
481 | } | 481 | } |
482 | 482 | ||
483 | static void nfs_free_open_context(struct kref *kref) | 483 | void put_nfs_open_context(struct nfs_open_context *ctx) |
484 | { | 484 | { |
485 | struct nfs_open_context *ctx = container_of(kref, | 485 | struct inode *inode = ctx->path.dentry->d_inode; |
486 | struct nfs_open_context, kref); | ||
487 | 486 | ||
488 | if (!list_empty(&ctx->list)) { | 487 | if (!atomic_dec_and_lock(&ctx->count, &inode->i_lock)) |
489 | struct inode *inode = ctx->path.dentry->d_inode; | 488 | return; |
490 | spin_lock(&inode->i_lock); | 489 | list_del(&ctx->list); |
491 | list_del(&ctx->list); | 490 | spin_unlock(&inode->i_lock); |
492 | spin_unlock(&inode->i_lock); | ||
493 | } | ||
494 | if (ctx->state != NULL) | 491 | if (ctx->state != NULL) |
495 | nfs4_close_state(&ctx->path, ctx->state, ctx->mode); | 492 | nfs4_close_state(&ctx->path, ctx->state, ctx->mode); |
496 | if (ctx->cred != NULL) | 493 | if (ctx->cred != NULL) |
@@ -500,11 +497,6 @@ static void nfs_free_open_context(struct kref *kref) | |||
500 | kfree(ctx); | 497 | kfree(ctx); |
501 | } | 498 | } |
502 | 499 | ||
503 | void put_nfs_open_context(struct nfs_open_context *ctx) | ||
504 | { | ||
505 | kref_put(&ctx->kref, nfs_free_open_context); | ||
506 | } | ||
507 | |||
508 | /* | 500 | /* |
509 | * Ensure that mmap has a recent RPC credential for use when writing out | 501 | * Ensure that mmap has a recent RPC credential for use when writing out |
510 | * shared pages | 502 | * shared pages |
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 9ba4aec37c50..157dcb055b5c 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h | |||
@@ -71,7 +71,7 @@ struct nfs_access_entry { | |||
71 | 71 | ||
72 | struct nfs4_state; | 72 | struct nfs4_state; |
73 | struct nfs_open_context { | 73 | struct nfs_open_context { |
74 | struct kref kref; | 74 | atomic_t count; |
75 | struct path path; | 75 | struct path path; |
76 | struct rpc_cred *cred; | 76 | struct rpc_cred *cred; |
77 | struct nfs4_state *state; | 77 | struct nfs4_state *state; |