diff options
| author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-07 16:59:48 -0400 |
|---|---|---|
| committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-07 16:59:48 -0400 |
| commit | 91677467163b847f1a5497789a893ada97f375b6 (patch) | |
| tree | 2b505e413ad1fd2f5237b968747a87a7e9983695 | |
| parent | 4a2253313653bcc4126287818b676f95ac26dc09 (diff) | |
| parent | 17d2c0a0c4d4e074f0a2a5c0090ff6d88f5e1d44 (diff) | |
Merge branch 'bugfixes' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6
* 'bugfixes' of git://git.linux-nfs.org/projects/trondmy/nfs-2.6:
NFS: Fix RCU issues in the NFSv4 delegation code
NFSv4: Fix the locking in nfs_inode_reclaim_delegation()
| -rw-r--r-- | fs/nfs/delegation.c | 86 |
1 files changed, 51 insertions, 35 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 15671245c6ee..ea61d26e7871 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c | |||
| @@ -24,6 +24,8 @@ | |||
| 24 | 24 | ||
| 25 | static void nfs_do_free_delegation(struct nfs_delegation *delegation) | 25 | static void nfs_do_free_delegation(struct nfs_delegation *delegation) |
| 26 | { | 26 | { |
| 27 | if (delegation->cred) | ||
| 28 | put_rpccred(delegation->cred); | ||
| 27 | kfree(delegation); | 29 | kfree(delegation); |
| 28 | } | 30 | } |
| 29 | 31 | ||
| @@ -36,13 +38,7 @@ static void nfs_free_delegation_callback(struct rcu_head *head) | |||
| 36 | 38 | ||
| 37 | static void nfs_free_delegation(struct nfs_delegation *delegation) | 39 | static void nfs_free_delegation(struct nfs_delegation *delegation) |
| 38 | { | 40 | { |
| 39 | struct rpc_cred *cred; | ||
| 40 | |||
| 41 | cred = rcu_dereference(delegation->cred); | ||
| 42 | rcu_assign_pointer(delegation->cred, NULL); | ||
| 43 | call_rcu(&delegation->rcu, nfs_free_delegation_callback); | 41 | call_rcu(&delegation->rcu, nfs_free_delegation_callback); |
| 44 | if (cred) | ||
| 45 | put_rpccred(cred); | ||
| 46 | } | 42 | } |
| 47 | 43 | ||
| 48 | void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) | 44 | void nfs_mark_delegation_referenced(struct nfs_delegation *delegation) |
| @@ -129,21 +125,35 @@ again: | |||
| 129 | */ | 125 | */ |
| 130 | void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) | 126 | void nfs_inode_reclaim_delegation(struct inode *inode, struct rpc_cred *cred, struct nfs_openres *res) |
| 131 | { | 127 | { |
| 132 | struct nfs_delegation *delegation = NFS_I(inode)->delegation; | 128 | struct nfs_delegation *delegation; |
| 133 | struct rpc_cred *oldcred; | 129 | struct rpc_cred *oldcred = NULL; |
| 134 | 130 | ||
| 135 | if (delegation == NULL) | 131 | rcu_read_lock(); |
| 136 | return; | 132 | delegation = rcu_dereference(NFS_I(inode)->delegation); |
| 137 | memcpy(delegation->stateid.data, res->delegation.data, | 133 | if (delegation != NULL) { |
| 138 | sizeof(delegation->stateid.data)); | 134 | spin_lock(&delegation->lock); |
| 139 | delegation->type = res->delegation_type; | 135 | if (delegation->inode != NULL) { |
| 140 | delegation->maxsize = res->maxsize; | 136 | memcpy(delegation->stateid.data, res->delegation.data, |
| 141 | oldcred = delegation->cred; | 137 | sizeof(delegation->stateid.data)); |
| 142 | delegation->cred = get_rpccred(cred); | 138 | delegation->type = res->delegation_type; |
| 143 | clear_bit(NFS_DELEGATION_NEED_RECLAIM, &delegation->flags); | 139 | delegation->maxsize = res->maxsize; |
| 144 | NFS_I(inode)->delegation_state = delegation->type; | 140 | oldcred = delegation->cred; |
| 145 | smp_wmb(); | 141 | delegation->cred = get_rpccred(cred); |
| 146 | put_rpccred(oldcred); | 142 | clear_bit(NFS_DELEGATION_NEED_RECLAIM, |
| 143 | &delegation->flags); | ||
| 144 | NFS_I(inode)->delegation_state = delegation->type; | ||
| 145 | spin_unlock(&delegation->lock); | ||
| 146 | put_rpccred(oldcred); | ||
| 147 | rcu_read_unlock(); | ||
| 148 | } else { | ||
| 149 | /* We appear to have raced with a delegation return. */ | ||
| 150 | spin_unlock(&delegation->lock); | ||
| 151 | rcu_read_unlock(); | ||
| 152 | nfs_inode_set_delegation(inode, cred, res); | ||
| 153 | } | ||
| 154 | } else { | ||
| 155 | rcu_read_unlock(); | ||
| 156 | } | ||
| 147 | } | 157 | } |
| 148 | 158 | ||
| 149 | static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) | 159 | static int nfs_do_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) |
| @@ -166,9 +176,13 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation | |||
| 166 | return inode; | 176 | return inode; |
| 167 | } | 177 | } |
| 168 | 178 | ||
| 169 | static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, const nfs4_stateid *stateid) | 179 | static struct nfs_delegation *nfs_detach_delegation_locked(struct nfs_inode *nfsi, |
| 180 | const nfs4_stateid *stateid, | ||
| 181 | struct nfs_client *clp) | ||
| 170 | { | 182 | { |
| 171 | struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); | 183 | struct nfs_delegation *delegation = |
| 184 | rcu_dereference_protected(nfsi->delegation, | ||
| 185 | lockdep_is_held(&clp->cl_lock)); | ||
| 172 | 186 | ||
| 173 | if (delegation == NULL) | 187 | if (delegation == NULL) |
| 174 | goto nomatch; | 188 | goto nomatch; |
| @@ -195,7 +209,7 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
| 195 | { | 209 | { |
| 196 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; | 210 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; |
| 197 | struct nfs_inode *nfsi = NFS_I(inode); | 211 | struct nfs_inode *nfsi = NFS_I(inode); |
| 198 | struct nfs_delegation *delegation; | 212 | struct nfs_delegation *delegation, *old_delegation; |
| 199 | struct nfs_delegation *freeme = NULL; | 213 | struct nfs_delegation *freeme = NULL; |
| 200 | int status = 0; | 214 | int status = 0; |
| 201 | 215 | ||
| @@ -213,10 +227,12 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
| 213 | spin_lock_init(&delegation->lock); | 227 | spin_lock_init(&delegation->lock); |
| 214 | 228 | ||
| 215 | spin_lock(&clp->cl_lock); | 229 | spin_lock(&clp->cl_lock); |
| 216 | if (rcu_dereference(nfsi->delegation) != NULL) { | 230 | old_delegation = rcu_dereference_protected(nfsi->delegation, |
| 217 | if (memcmp(&delegation->stateid, &nfsi->delegation->stateid, | 231 | lockdep_is_held(&clp->cl_lock)); |
| 218 | sizeof(delegation->stateid)) == 0 && | 232 | if (old_delegation != NULL) { |
| 219 | delegation->type == nfsi->delegation->type) { | 233 | if (memcmp(&delegation->stateid, &old_delegation->stateid, |
| 234 | sizeof(old_delegation->stateid)) == 0 && | ||
| 235 | delegation->type == old_delegation->type) { | ||
| 220 | goto out; | 236 | goto out; |
| 221 | } | 237 | } |
| 222 | /* | 238 | /* |
| @@ -226,12 +242,12 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
| 226 | dfprintk(FILE, "%s: server %s handed out " | 242 | dfprintk(FILE, "%s: server %s handed out " |
| 227 | "a duplicate delegation!\n", | 243 | "a duplicate delegation!\n", |
| 228 | __func__, clp->cl_hostname); | 244 | __func__, clp->cl_hostname); |
| 229 | if (delegation->type <= nfsi->delegation->type) { | 245 | if (delegation->type <= old_delegation->type) { |
| 230 | freeme = delegation; | 246 | freeme = delegation; |
| 231 | delegation = NULL; | 247 | delegation = NULL; |
| 232 | goto out; | 248 | goto out; |
| 233 | } | 249 | } |
| 234 | freeme = nfs_detach_delegation_locked(nfsi, NULL); | 250 | freeme = nfs_detach_delegation_locked(nfsi, NULL, clp); |
| 235 | } | 251 | } |
| 236 | list_add_rcu(&delegation->super_list, &clp->cl_delegations); | 252 | list_add_rcu(&delegation->super_list, &clp->cl_delegations); |
| 237 | nfsi->delegation_state = delegation->type; | 253 | nfsi->delegation_state = delegation->type; |
| @@ -301,7 +317,7 @@ restart: | |||
| 301 | if (inode == NULL) | 317 | if (inode == NULL) |
| 302 | continue; | 318 | continue; |
| 303 | spin_lock(&clp->cl_lock); | 319 | spin_lock(&clp->cl_lock); |
| 304 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); | 320 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp); |
| 305 | spin_unlock(&clp->cl_lock); | 321 | spin_unlock(&clp->cl_lock); |
| 306 | rcu_read_unlock(); | 322 | rcu_read_unlock(); |
| 307 | if (delegation != NULL) { | 323 | if (delegation != NULL) { |
| @@ -330,9 +346,9 @@ void nfs_inode_return_delegation_noreclaim(struct inode *inode) | |||
| 330 | struct nfs_inode *nfsi = NFS_I(inode); | 346 | struct nfs_inode *nfsi = NFS_I(inode); |
| 331 | struct nfs_delegation *delegation; | 347 | struct nfs_delegation *delegation; |
| 332 | 348 | ||
| 333 | if (rcu_dereference(nfsi->delegation) != NULL) { | 349 | if (rcu_access_pointer(nfsi->delegation) != NULL) { |
| 334 | spin_lock(&clp->cl_lock); | 350 | spin_lock(&clp->cl_lock); |
| 335 | delegation = nfs_detach_delegation_locked(nfsi, NULL); | 351 | delegation = nfs_detach_delegation_locked(nfsi, NULL, clp); |
| 336 | spin_unlock(&clp->cl_lock); | 352 | spin_unlock(&clp->cl_lock); |
| 337 | if (delegation != NULL) | 353 | if (delegation != NULL) |
| 338 | nfs_do_return_delegation(inode, delegation, 0); | 354 | nfs_do_return_delegation(inode, delegation, 0); |
| @@ -346,9 +362,9 @@ int nfs_inode_return_delegation(struct inode *inode) | |||
| 346 | struct nfs_delegation *delegation; | 362 | struct nfs_delegation *delegation; |
| 347 | int err = 0; | 363 | int err = 0; |
| 348 | 364 | ||
| 349 | if (rcu_dereference(nfsi->delegation) != NULL) { | 365 | if (rcu_access_pointer(nfsi->delegation) != NULL) { |
| 350 | spin_lock(&clp->cl_lock); | 366 | spin_lock(&clp->cl_lock); |
| 351 | delegation = nfs_detach_delegation_locked(nfsi, NULL); | 367 | delegation = nfs_detach_delegation_locked(nfsi, NULL, clp); |
| 352 | spin_unlock(&clp->cl_lock); | 368 | spin_unlock(&clp->cl_lock); |
| 353 | if (delegation != NULL) { | 369 | if (delegation != NULL) { |
| 354 | nfs_msync_inode(inode); | 370 | nfs_msync_inode(inode); |
| @@ -526,7 +542,7 @@ restart: | |||
| 526 | if (inode == NULL) | 542 | if (inode == NULL) |
| 527 | continue; | 543 | continue; |
| 528 | spin_lock(&clp->cl_lock); | 544 | spin_lock(&clp->cl_lock); |
| 529 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL); | 545 | delegation = nfs_detach_delegation_locked(NFS_I(inode), NULL, clp); |
| 530 | spin_unlock(&clp->cl_lock); | 546 | spin_unlock(&clp->cl_lock); |
| 531 | rcu_read_unlock(); | 547 | rcu_read_unlock(); |
| 532 | if (delegation != NULL) | 548 | if (delegation != NULL) |
