diff options
| -rw-r--r-- | fs/nfs/delegation.c | 147 | ||||
| -rw-r--r-- | fs/nfs/delegation.h | 1 | ||||
| -rw-r--r-- | fs/nfs/nfs4proc.c | 3 |
3 files changed, 108 insertions, 43 deletions
diff --git a/fs/nfs/delegation.c b/fs/nfs/delegation.c index 81c5eec3cf38..2542cdaa1116 100644 --- a/fs/nfs/delegation.c +++ b/fs/nfs/delegation.c | |||
| @@ -55,7 +55,8 @@ int nfs4_have_delegation(struct inode *inode, fmode_t flags) | |||
| 55 | flags &= FMODE_READ|FMODE_WRITE; | 55 | flags &= FMODE_READ|FMODE_WRITE; |
| 56 | rcu_read_lock(); | 56 | rcu_read_lock(); |
| 57 | delegation = rcu_dereference(NFS_I(inode)->delegation); | 57 | delegation = rcu_dereference(NFS_I(inode)->delegation); |
| 58 | if (delegation != NULL && (delegation->type & flags) == flags) { | 58 | if (delegation != NULL && (delegation->type & flags) == flags && |
| 59 | !test_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) { | ||
| 59 | nfs_mark_delegation_referenced(delegation); | 60 | nfs_mark_delegation_referenced(delegation); |
| 60 | ret = 1; | 61 | ret = 1; |
| 61 | } | 62 | } |
| @@ -94,7 +95,9 @@ static int nfs_delegation_claim_opens(struct inode *inode, const nfs4_stateid *s | |||
| 94 | { | 95 | { |
| 95 | struct nfs_inode *nfsi = NFS_I(inode); | 96 | struct nfs_inode *nfsi = NFS_I(inode); |
| 96 | struct nfs_open_context *ctx; | 97 | struct nfs_open_context *ctx; |
| 98 | struct nfs4_state_owner *sp; | ||
| 97 | struct nfs4_state *state; | 99 | struct nfs4_state *state; |
| 100 | unsigned int seq; | ||
| 98 | int err; | 101 | int err; |
| 99 | 102 | ||
| 100 | again: | 103 | again: |
| @@ -109,9 +112,13 @@ again: | |||
| 109 | continue; | 112 | continue; |
| 110 | get_nfs_open_context(ctx); | 113 | get_nfs_open_context(ctx); |
| 111 | spin_unlock(&inode->i_lock); | 114 | spin_unlock(&inode->i_lock); |
| 115 | sp = state->owner; | ||
| 116 | seq = raw_seqcount_begin(&sp->so_reclaim_seqcount); | ||
| 112 | err = nfs4_open_delegation_recall(ctx, state, stateid); | 117 | err = nfs4_open_delegation_recall(ctx, state, stateid); |
| 113 | if (err >= 0) | 118 | if (!err) |
| 114 | err = nfs_delegation_claim_locks(ctx, state); | 119 | err = nfs_delegation_claim_locks(ctx, state); |
| 120 | if (!err && read_seqcount_retry(&sp->so_reclaim_seqcount, seq)) | ||
| 121 | err = -EAGAIN; | ||
| 115 | put_nfs_open_context(ctx); | 122 | put_nfs_open_context(ctx); |
| 116 | if (err != 0) | 123 | if (err != 0) |
| 117 | return err; | 124 | return err; |
| @@ -182,39 +189,91 @@ static struct inode *nfs_delegation_grab_inode(struct nfs_delegation *delegation | |||
| 182 | } | 189 | } |
| 183 | 190 | ||
| 184 | static struct nfs_delegation * | 191 | static struct nfs_delegation * |
| 192 | nfs_start_delegation_return_locked(struct nfs_inode *nfsi) | ||
| 193 | { | ||
| 194 | struct nfs_delegation *ret = NULL; | ||
| 195 | struct nfs_delegation *delegation = rcu_dereference(nfsi->delegation); | ||
| 196 | |||
| 197 | if (delegation == NULL) | ||
| 198 | goto out; | ||
| 199 | spin_lock(&delegation->lock); | ||
| 200 | if (!test_and_set_bit(NFS_DELEGATION_RETURNING, &delegation->flags)) | ||
| 201 | ret = delegation; | ||
| 202 | spin_unlock(&delegation->lock); | ||
| 203 | out: | ||
| 204 | return ret; | ||
| 205 | } | ||
| 206 | |||
| 207 | static struct nfs_delegation * | ||
| 208 | nfs_start_delegation_return(struct nfs_inode *nfsi) | ||
| 209 | { | ||
| 210 | struct nfs_delegation *delegation; | ||
| 211 | |||
| 212 | rcu_read_lock(); | ||
| 213 | delegation = nfs_start_delegation_return_locked(nfsi); | ||
| 214 | rcu_read_unlock(); | ||
| 215 | return delegation; | ||
| 216 | } | ||
| 217 | |||
| 218 | static void | ||
| 219 | nfs_abort_delegation_return(struct nfs_delegation *delegation, | ||
| 220 | struct nfs_client *clp) | ||
| 221 | { | ||
| 222 | |||
| 223 | spin_lock(&delegation->lock); | ||
| 224 | clear_bit(NFS_DELEGATION_RETURNING, &delegation->flags); | ||
| 225 | set_bit(NFS_DELEGATION_RETURN, &delegation->flags); | ||
| 226 | spin_unlock(&delegation->lock); | ||
| 227 | set_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state); | ||
| 228 | } | ||
| 229 | |||
| 230 | static struct nfs_delegation * | ||
| 185 | nfs_detach_delegation_locked(struct nfs_inode *nfsi, | 231 | nfs_detach_delegation_locked(struct nfs_inode *nfsi, |
| 186 | struct nfs_server *server) | 232 | struct nfs_delegation *delegation, |
| 233 | struct nfs_client *clp) | ||
| 187 | { | 234 | { |
| 188 | struct nfs_delegation *delegation = | 235 | struct nfs_delegation *deleg_cur = |
| 189 | rcu_dereference_protected(nfsi->delegation, | 236 | rcu_dereference_protected(nfsi->delegation, |
| 190 | lockdep_is_held(&server->nfs_client->cl_lock)); | 237 | lockdep_is_held(&clp->cl_lock)); |
| 191 | 238 | ||
| 192 | if (delegation == NULL) | 239 | if (deleg_cur == NULL || delegation != deleg_cur) |
| 193 | goto nomatch; | 240 | return NULL; |
| 194 | 241 | ||
| 195 | spin_lock(&delegation->lock); | 242 | spin_lock(&delegation->lock); |
| 243 | set_bit(NFS_DELEGATION_RETURNING, &delegation->flags); | ||
| 196 | list_del_rcu(&delegation->super_list); | 244 | list_del_rcu(&delegation->super_list); |
| 197 | delegation->inode = NULL; | 245 | delegation->inode = NULL; |
| 198 | nfsi->delegation_state = 0; | 246 | nfsi->delegation_state = 0; |
| 199 | rcu_assign_pointer(nfsi->delegation, NULL); | 247 | rcu_assign_pointer(nfsi->delegation, NULL); |
| 200 | spin_unlock(&delegation->lock); | 248 | spin_unlock(&delegation->lock); |
| 201 | return delegation; | 249 | return delegation; |
| 202 | nomatch: | ||
| 203 | return NULL; | ||
| 204 | } | 250 | } |
| 205 | 251 | ||
| 206 | static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, | 252 | static struct nfs_delegation *nfs_detach_delegation(struct nfs_inode *nfsi, |
| 207 | struct nfs_server *server) | 253 | struct nfs_delegation *delegation, |
| 254 | struct nfs_server *server) | ||
| 208 | { | 255 | { |
| 209 | struct nfs_client *clp = server->nfs_client; | 256 | struct nfs_client *clp = server->nfs_client; |
| 210 | struct nfs_delegation *delegation; | ||
| 211 | 257 | ||
| 212 | spin_lock(&clp->cl_lock); | 258 | spin_lock(&clp->cl_lock); |
| 213 | delegation = nfs_detach_delegation_locked(nfsi, server); | 259 | delegation = nfs_detach_delegation_locked(nfsi, delegation, clp); |
| 214 | spin_unlock(&clp->cl_lock); | 260 | spin_unlock(&clp->cl_lock); |
| 215 | return delegation; | 261 | return delegation; |
| 216 | } | 262 | } |
| 217 | 263 | ||
| 264 | static struct nfs_delegation * | ||
| 265 | nfs_inode_detach_delegation(struct inode *inode) | ||
| 266 | { | ||
| 267 | struct nfs_inode *nfsi = NFS_I(inode); | ||
| 268 | struct nfs_server *server = NFS_SERVER(inode); | ||
| 269 | struct nfs_delegation *delegation; | ||
| 270 | |||
| 271 | delegation = nfs_start_delegation_return(nfsi); | ||
| 272 | if (delegation == NULL) | ||
| 273 | return NULL; | ||
| 274 | return nfs_detach_delegation(nfsi, delegation, server); | ||
| 275 | } | ||
| 276 | |||
| 218 | /** | 277 | /** |
| 219 | * nfs_inode_set_delegation - set up a delegation on an inode | 278 | * nfs_inode_set_delegation - set up a delegation on an inode |
| 220 | * @inode: inode to which delegation applies | 279 | * @inode: inode to which delegation applies |
| @@ -268,7 +327,10 @@ int nfs_inode_set_delegation(struct inode *inode, struct rpc_cred *cred, struct | |||
| 268 | delegation = NULL; | 327 | delegation = NULL; |
| 269 | goto out; | 328 | goto out; |
| 270 | } | 329 | } |
| 271 | freeme = nfs_detach_delegation_locked(nfsi, server); | 330 | freeme = nfs_detach_delegation_locked(nfsi, |
| 331 | old_delegation, clp); | ||
| 332 | if (freeme == NULL) | ||
| 333 | goto out; | ||
| 272 | } | 334 | } |
| 273 | list_add_rcu(&delegation->super_list, &server->delegations); | 335 | list_add_rcu(&delegation->super_list, &server->delegations); |
| 274 | nfsi->delegation_state = delegation->type; | 336 | nfsi->delegation_state = delegation->type; |
| @@ -292,19 +354,29 @@ out: | |||
| 292 | /* | 354 | /* |
| 293 | * Basic procedure for returning a delegation to the server | 355 | * Basic procedure for returning a delegation to the server |
| 294 | */ | 356 | */ |
| 295 | static int __nfs_inode_return_delegation(struct inode *inode, struct nfs_delegation *delegation, int issync) | 357 | static int nfs_end_delegation_return(struct inode *inode, struct nfs_delegation *delegation, int issync) |
| 296 | { | 358 | { |
| 359 | struct nfs_client *clp = NFS_SERVER(inode)->nfs_client; | ||
| 297 | struct nfs_inode *nfsi = NFS_I(inode); | 360 | struct nfs_inode *nfsi = NFS_I(inode); |
| 298 | int err; | 361 | int err; |
| 299 | 362 | ||
| 300 | /* | 363 | if (delegation == NULL) |
| 301 | * Guard against new delegated open/lock/unlock calls and against | 364 | return 0; |
| 302 | * state recovery | 365 | do { |
| 303 | */ | 366 | err = nfs_delegation_claim_opens(inode, &delegation->stateid); |
| 304 | down_write(&nfsi->rwsem); | 367 | if (!issync || err != -EAGAIN) |
| 305 | err = nfs_delegation_claim_opens(inode, &delegation->stateid); | 368 | break; |
| 306 | up_write(&nfsi->rwsem); | 369 | /* |
| 307 | if (err) | 370 | * Guard against state recovery |
