diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-05-01 12:36:18 -0400 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2010-05-01 12:36:18 -0400 |
commit | 8f649c376254755f2261a693b3d48d09126218dc (patch) | |
tree | 818338eca6dea5c7e022a3a6df51c3b2aa9e54b7 /fs | |
parent | be1066bbcd443a65df312fdecea7e4959adedb45 (diff) |
NFSv4: Fix the locking in nfs_inode_reclaim_delegation()
Ensure that we correctly rcu-dereference the delegation itself, and that we
protect against removal while we're changing the contents.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Signed-off-by: David Howells <dhowells@redhat.com>
Signed-off-by: Paul E. McKenney <paulmck@linux.vnet.ibm.com>
Diffstat (limited to 'fs')
-rw-r--r-- | fs/nfs/delegation.c | 42 |
1 files changed, 28 insertions, 14 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 15671245c6ee..8d9ec494a944 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c | |||
@@ -129,21 +129,35 @@ again: | |||
129 | */ | 129 | */ |
130 | void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) | 130 | void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) |
131 | { | 131 | { |
132 | struct nfs_delegation *delegation = NFS_I(inode)->delegation; | 132 | struct nfs_delegation *delegation; |
133 | struct rpc_cred *oldcred; | 133 | struct rpc_cred *oldcred = NULL; |
134 | 134 | ||
135 | if (delegation == NULL) | 135 | rcu_read_lock(); |
136 | return; | 136 | delegation = rcu_dereference(NFS_I(inode)->delegation); |
137 | memcpy(delegation->stateid.data, res->delegation.data, | 137 | if (delegation != NULL) { |
138 | sizeof(delegation->stateid.data)); | 138 | spin_lock(&delegation->lock); |
139 | delegation->type = res->delegation_type; | 139 | if (delegation->inode != NULL) { |
140 | delegation->maxsize = res->maxsize; | 140 | memcpy(delegation->stateid.data, res->delegation.data, |
141 | oldcred = delegation->cred; | 141 | sizeof(delegation->stateid.data)); |
142 | delegation->cred = get_rpccred(cred); | 142 | delegation->type = res->delegation_type; |
143 | clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); | 143 | delegation->maxsize = res->maxsize; |
144 | NFS_I(inode)->delegation_state = delegation->type; | 144 | oldcred = delegation->cred; |
145 | smp_wmb(); | 145 | delegation->cred = get_rpccred(cred); |
146 | put_rpccred(oldcred); | 146 | clear_bit(NFS_DELEGATION_NEED_RECLAIM, |
147 | &delegation->flags); | ||
148 | NFS_I(inode)->delegation_state = delegation->type; | ||
149 | spin_unlock(&delegation->lock); | ||
150 | put_rpccred(oldcred); | ||
151 | rcu_read_unlock(); | ||
152 | } else { | ||
153 | /* We appear to have raced with a delegation return. */ | ||
154 | spin_unlock(&delegation->lock); | ||
155 | rcu_read_unlock(); | ||
156 | nfs_inode_set_delegation(inode, cred, res); | ||
157 | } | ||
158 | } else { | ||
159 | rcu_read_unlock(); | ||
160 | } | ||
147 | } | 161 | } |
148 | 162 | ||
149 | static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) | 163 | static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) |