diff options
author | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-01-03 03:55:15 -0500 |
---|---|---|
committer | Trond Myklebust <Trond.Myklebust@netapp.com> | 2006-01-06 14:58:43 -0500 |
commit | 864472e9b8fa76ffaad17dfcb84d79e16df6828c (patch) | |
tree | 1cc5c4624c36be0b9aebdabae30d1a5385e04dee /fs/nfs/nfs4proc.c | |
parent | e761692381f294ea079d2e869fcd7c0afc79e394 (diff) |
NFSv4: Make open recovery track O_RDWR, O_RDONLY and O_WRONLY correctly
When recovering from a delegation recall or a network partition, we need
to replay open(O_RDWR), open(O_RDONLY) and open(O_WRONLY) separately.
Signed-off-by: Trond Myklebust <Trond.Myklebust@netapp.com>
Diffstat (limited to 'fs/nfs/nfs4proc.c')
-rw-r--r-- | fs/nfs/nfs4proc.c | 276 |
1 files changed, 149 insertions, 127 deletions
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index e494cc2ea986..3ecb7da220f5 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -58,7 +58,7 @@ | |||
58 | #define NFS4_POLL_RETRY_MAX (15*HZ) | 58 | #define NFS4_POLL_RETRY_MAX (15*HZ) |
59 | 59 | ||
60 | struct nfs4_opendata; | 60 | struct nfs4_opendata; |
61 | static int _nfs4_proc_open_confirm(struct nfs4_opendata *data); | 61 | static int _nfs4_proc_open(struct nfs4_opendata *data); |
62 | static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); | 62 | static int nfs4_do_fsinfo(struct nfs_server *, struct nfs_fh *, struct nfs_fsinfo *); |
63 | static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *); | 63 | static int nfs4_async_handle_error(struct rpc_task *, const struct nfs_server *); |
64 | static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry); | 64 | static int _nfs4_proc_access(struct inode *inode, struct nfs_access_entry *entry); |
@@ -346,32 +346,108 @@ out: | |||
346 | return state; | 346 | return state; |
347 | } | 347 | } |
348 | 348 | ||
349 | static struct nfs_open_context *nfs4_state_find_open_context(struct nfs4_state *state) | ||
350 | { | ||
351 | struct nfs_inode *nfsi = NFS_I(state->inode); | ||
352 | struct nfs_open_context *ctx; | ||
353 | |||
354 | spin_lock(&state->inode->i_lock); | ||
355 | list_for_each_entry(ctx, &nfsi->open_files, list) { | ||
356 | if (ctx->state != state) | ||
357 | continue; | ||
358 | get_nfs_open_context(ctx); | ||
359 | spin_unlock(&state->inode->i_lock); | ||
360 | return ctx; | ||
361 | } | ||
362 | spin_unlock(&state->inode->i_lock); | ||
363 | return ERR_PTR(-ENOENT); | ||
364 | } | ||
365 | |||
366 | static int nfs4_open_recover_helper(struct nfs4_opendata *opendata, mode_t openflags, nfs4_stateid *stateid) | ||
367 | { | ||
368 | int ret; | ||
369 | |||
370 | opendata->o_arg.open_flags = openflags; | ||
371 | ret = _nfs4_proc_open(opendata); | ||
372 | if (ret != 0) | ||
373 | return ret; | ||
374 | memcpy(stateid->data, opendata->o_res.stateid.data, | ||
375 | sizeof(stateid->data)); | ||
376 | return 0; | ||
377 | } | ||
378 | |||
379 | static int nfs4_open_recover(struct nfs4_opendata *opendata, struct nfs4_state *state) | ||
380 | { | ||
381 | nfs4_stateid stateid; | ||
382 | struct nfs4_state *newstate; | ||
383 | int mode = 0; | ||
384 | int delegation = 0; | ||
385 | int ret; | ||
386 | |||
387 | /* memory barrier prior to reading state->n_* */ | ||
388 | smp_rmb(); | ||
389 | if (state->n_rdwr != 0) { | ||
390 | ret = nfs4_open_recover_helper(opendata, FMODE_READ|FMODE_WRITE, &stateid); | ||
391 | if (ret != 0) | ||
392 | return ret; | ||
393 | mode |= FMODE_READ|FMODE_WRITE; | ||
394 | if (opendata->o_res.delegation_type != 0) | ||
395 | delegation = opendata->o_res.delegation_type; | ||
396 | smp_rmb(); | ||
397 | } | ||
398 | if (state->n_wronly != 0) { | ||
399 | ret = nfs4_open_recover_helper(opendata, FMODE_WRITE, &stateid); | ||
400 | if (ret != 0) | ||
401 | return ret; | ||
402 | mode |= FMODE_WRITE; | ||
403 | if (opendata->o_res.delegation_type != 0) | ||
404 | delegation = opendata->o_res.delegation_type; | ||
405 | smp_rmb(); | ||
406 | } | ||
407 | if (state->n_rdonly != 0) { | ||
408 | ret = nfs4_open_recover_helper(opendata, FMODE_READ, &stateid); | ||
409 | if (ret != 0) | ||
410 | return ret; | ||
411 | mode |= FMODE_READ; | ||
412 | } | ||
413 | clear_bit(NFS_DELEGATED_STATE, &state->flags); | ||
414 | if (mode == 0) | ||
415 | return 0; | ||
416 | if (opendata->o_res.delegation_type == 0) | ||
417 | opendata->o_res.delegation_type = delegation; | ||
418 | opendata->o_arg.open_flags |= mode; | ||
419 | newstate = nfs4_opendata_to_nfs4_state(opendata); | ||
420 | if (newstate != NULL) { | ||
421 | if (opendata->o_res.delegation_type != 0) { | ||
422 | struct nfs_inode *nfsi = NFS_I(newstate->inode); | ||
423 | int delegation_flags = 0; | ||
424 | if (nfsi->delegation) | ||
425 | delegation_flags = nfsi->delegation->flags; | ||
426 | if (!(delegation_flags & NFS_DELEGATION_NEED_RECLAIM)) | ||
427 | nfs_inode_set_delegation(newstate->inode, | ||
428 | opendata->owner->so_cred, | ||
429 | &opendata->o_res); | ||
430 | else | ||
431 | nfs_inode_reclaim_delegation(newstate->inode, | ||
432 | opendata->owner->so_cred, | ||
433 | &opendata->o_res); | ||
434 | } | ||
435 | nfs4_close_state(newstate, opendata->o_arg.open_flags); | ||
436 | } | ||
437 | if (newstate != state) | ||
438 | return -ESTALE; | ||
439 | return 0; | ||
440 | } | ||
441 | |||
349 | /* | 442 | /* |
350 | * OPEN_RECLAIM: | 443 | * OPEN_RECLAIM: |
351 | * reclaim state on the server after a reboot. | 444 | * reclaim state on the server after a reboot. |
352 | */ | 445 | */ |
353 | static int _nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) | 446 | static int _nfs4_do_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry) |
354 | { | 447 | { |
355 | struct inode *inode = state->inode; | 448 | struct nfs_delegation *delegation = NFS_I(state->inode)->delegation; |
356 | struct nfs_server *server = NFS_SERVER(inode); | 449 | struct nfs4_opendata *opendata; |
357 | struct nfs_delegation *delegation = NFS_I(inode)->delegation; | 450 | int delegation_type = 0; |
358 | struct nfs_openargs o_arg = { | ||
359 | .fh = NFS_FH(inode), | ||
360 | .id = sp->so_id, | ||
361 | .open_flags = state->state, | ||
362 | .clientid = server->nfs4_state->cl_clientid, | ||
363 | .claim = NFS4_OPEN_CLAIM_PREVIOUS, | ||
364 | .bitmask = server->attr_bitmask, | ||
365 | }; | ||
366 | struct nfs_openres o_res = { | ||
367 | .server = server, /* Grrr */ | ||
368 | }; | ||
369 | struct rpc_message msg = { | ||
370 | .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR], | ||
371 | .rpc_argp = &o_arg, | ||
372 | .rpc_resp = &o_res, | ||
373 | .rpc_cred = sp->so_cred, | ||
374 | }; | ||
375 | int status; | 451 | int status; |
376 | 452 | ||
377 | if (delegation != NULL) { | 453 | if (delegation != NULL) { |
@@ -381,38 +457,27 @@ static int _nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *st | |||
381 | set_bit(NFS_DELEGATED_STATE, &state->flags); | 457 | set_bit(NFS_DELEGATED_STATE, &state->flags); |
382 | return 0; | 458 | return 0; |
383 | } | 459 | } |
384 | o_arg.u.delegation_type = delegation->type; | 460 | delegation_type = delegation->type; |
385 | } | 461 | } |
386 | o_arg.seqid = nfs_alloc_seqid(&sp->so_seqid); | 462 | opendata = nfs4_opendata_alloc(dentry, sp, 0, NULL); |
387 | if (o_arg.seqid == NULL) | 463 | if (opendata == NULL) |
388 | return -ENOMEM; | 464 | return -ENOMEM; |
389 | status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR); | 465 | opendata->o_arg.claim = NFS4_OPEN_CLAIM_PREVIOUS; |
390 | /* Confirm the sequence as being established */ | 466 | opendata->o_arg.fh = NFS_FH(state->inode); |
391 | nfs_confirm_seqid(&sp->so_seqid, status); | 467 | nfs_copy_fh(&opendata->o_res.fh, opendata->o_arg.fh); |
392 | nfs_increment_open_seqid(status, o_arg.seqid); | 468 | opendata->o_arg.u.delegation_type = delegation_type; |
393 | if (status == 0) { | 469 | status = nfs4_open_recover(opendata, state); |
394 | memcpy(&state->stateid, &o_res.stateid, sizeof(state->stateid)); | 470 | nfs4_opendata_free(opendata); |
395 | if (o_res.delegation_type != 0) { | ||
396 | nfs_inode_reclaim_delegation(inode, sp->so_cred, &o_res); | ||
397 | /* Did the server issue an immediate delegation recall? */ | ||
398 | if (o_res.do_recall) | ||
399 | nfs_async_inode_return_delegation(inode, &o_res.stateid); | ||
400 | } | ||
401 | } | ||
402 | nfs_free_seqid(o_arg.seqid); | ||
403 | clear_bit(NFS_DELEGATED_STATE, &state->flags); | ||
404 | /* Ensure we update the inode attributes */ | ||
405 | NFS_CACHEINV(inode); | ||
406 | return status; | 471 | return status; |
407 | } | 472 | } |
408 | 473 | ||
409 | static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) | 474 | static int nfs4_do_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry) |
410 | { | 475 | { |
411 | struct nfs_server *server = NFS_SERVER(state->inode); | 476 | struct nfs_server *server = NFS_SERVER(state->inode); |
412 | struct nfs4_exception exception = { }; | 477 | struct nfs4_exception exception = { }; |
413 | int err; | 478 | int err; |
414 | do { | 479 | do { |
415 | err = _nfs4_open_reclaim(sp, state); | 480 | err = _nfs4_do_open_reclaim(sp, state, dentry); |
416 | if (err != -NFS4ERR_DELAY) | 481 | if (err != -NFS4ERR_DELAY) |
417 | break; | 482 | break; |
418 | nfs4_handle_exception(server, err, &exception); | 483 | nfs4_handle_exception(server, err, &exception); |
@@ -420,50 +485,36 @@ static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *sta | |||
420 | return err; | 485 | return err; |
421 | } | 486 | } |
422 | 487 | ||
488 | static int nfs4_open_reclaim(struct nfs4_state_owner *sp, struct nfs4_state *state) | ||
489 | { | ||
490 | struct nfs_open_context *ctx; | ||
491 | int ret; | ||
492 | |||
493 | ctx = nfs4_state_find_open_context(state); | ||
494 | if (IS_ERR(ctx)) | ||
495 | return PTR_ERR(ctx); | ||
496 | ret = nfs4_do_open_reclaim(sp, state, ctx->dentry); | ||
497 | put_nfs_open_context(ctx); | ||
498 | return ret; | ||
499 | } | ||
500 | |||
423 | static int _nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state) | 501 | static int _nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state) |
424 | { | 502 | { |
425 | struct nfs4_state_owner *sp = state->owner; | 503 | struct nfs4_state_owner *sp = state->owner; |
426 | struct inode *inode = dentry->d_inode; | ||
427 | struct nfs_server *server = NFS_SERVER(inode); | ||
428 | struct rpc_message msg = { | ||
429 | .rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR], | ||
430 | .rpc_cred = sp->so_cred, | ||
431 | }; | ||
432 | struct nfs4_opendata *opendata; | 504 | struct nfs4_opendata *opendata; |
433 | int status = 0; | 505 | int ret; |
434 | 506 | ||
435 | if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) | 507 | if (!test_bit(NFS_DELEGATED_STATE, &state->flags)) |
436 | goto out; | 508 | return 0; |
437 | if (state->state == 0) | 509 | opendata = nfs4_opendata_alloc(dentry, sp, 0, NULL); |
438 | goto out; | ||
439 | opendata = nfs4_opendata_alloc(dentry, sp, state->state, NULL); | ||
440 | status = -ENOMEM; | ||
441 | if (opendata == NULL) | 510 | if (opendata == NULL) |
442 | goto out; | 511 | return -ENOMEM; |
443 | opendata->o_arg.claim = NFS4_OPEN_CLAIM_DELEGATE_CUR; | 512 | opendata->o_arg.claim = NFS4_OPEN_CLAIM_DELEGATE_CUR; |
444 | msg.rpc_argp = &opendata->o_arg; | ||
445 | msg.rpc_resp = &opendata->o_res; | ||
446 | memcpy(opendata->o_arg.u.delegation.data, state->stateid.data, | 513 | memcpy(opendata->o_arg.u.delegation.data, state->stateid.data, |
447 | sizeof(opendata->o_arg.u.delegation.data)); | 514 | sizeof(opendata->o_arg.u.delegation.data)); |
448 | status = rpc_call_sync(server->client, &msg, RPC_TASK_NOINTR); | 515 | ret = nfs4_open_recover(opendata, state); |
449 | nfs_increment_open_seqid(status, opendata->o_arg.seqid); | ||
450 | if (status != 0) | ||
451 | goto out_free; | ||
452 | if(opendata->o_res.rflags & NFS4_OPEN_RESULT_CONFIRM) { | ||
453 | status = _nfs4_proc_open_confirm(opendata); | ||
454 | if (status != 0) | ||
455 | goto out_free; | ||
456 | } | ||
457 | nfs_confirm_seqid(&sp->so_seqid, 0); | ||
458 | if (status >= 0) { | ||
459 | memcpy(state->stateid.data, opendata->o_res.stateid.data, | ||
460 | sizeof(state->stateid.data)); | ||
461 | clear_bit(NFS_DELEGATED_STATE, &state->flags); | ||
462 | } | ||
463 | out_free: | ||
464 | nfs4_opendata_free(opendata); | 516 | nfs4_opendata_free(opendata); |
465 | out: | 517 | return ret; |
466 | return status; | ||
467 | } | 518 | } |
468 | 519 | ||
469 | int nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state) | 520 | int nfs4_open_delegation_recall(struct dentry *dentry, struct nfs4_state *state) |
@@ -580,6 +631,8 @@ static void nfs4_open_prepare(struct rpc_task *task, void *calldata) | |||
580 | /* Update sequence id. */ | 631 | /* Update sequence id. */ |
581 | data->o_arg.id = sp->so_id; | 632 | data->o_arg.id = sp->so_id; |
582 | data->o_arg.clientid = sp->so_client->cl_clientid; | 633 | data->o_arg.clientid = sp->so_client->cl_clientid; |
634 | if (data->o_arg.claim == NFS4_OPEN_CLAIM_PREVIOUS) | ||
635 | msg.rpc_proc = &nfs4_procedures[NFSPROC4_CLNT_OPEN_NOATTR]; | ||
583 | rpc_call_setup(task, &msg, 0); | 636 | rpc_call_setup(task, &msg, 0); |
584 | } | 637 | } |
585 | 638 | ||
@@ -714,55 +767,31 @@ out: | |||
714 | */ | 767 | */ |
715 | static int _nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry) | 768 | static int _nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry) |
716 | { | 769 | { |
717 | struct dentry *parent = dget_parent(dentry); | ||
718 | struct inode *inode = state->inode; | 770 | struct inode *inode = state->inode; |
719 | struct nfs_delegation *delegation = NFS_I(inode)->delegation; | 771 | struct nfs_delegation *delegation = NFS_I(inode)->delegation; |
720 | struct nfs4_opendata *opendata; | 772 | struct nfs4_opendata *opendata; |
721 | struct nfs4_state *newstate; | ||
722 | int openflags = state->state & (FMODE_READ|FMODE_WRITE); | 773 | int openflags = state->state & (FMODE_READ|FMODE_WRITE); |
723 | int status = 0; | 774 | int ret; |
724 | 775 | ||
725 | if (delegation != NULL && !(delegation->flags & NFS_DELEGATION_NEED_RECLAIM)) { | 776 | if (delegation != NULL && !(delegation->flags & NFS_DELEGATION_NEED_RECLAIM)) { |
726 | status = _nfs4_do_access(inode, sp->so_cred, openflags); | 777 | ret = _nfs4_do_access(inode, sp->so_cred, openflags); |
727 | if (status < 0) | 778 | if (ret < 0) |
728 | goto out; | 779 | return ret; |
729 | memcpy(&state->stateid, &delegation->stateid, sizeof(state->stateid)); | 780 | memcpy(&state->stateid, &delegation->stateid, sizeof(state->stateid)); |
730 | set_bit(NFS_DELEGATED_STATE, &state->flags); | 781 | set_bit(NFS_DELEGATED_STATE, &state->flags); |
731 | goto out; | 782 | return 0; |
732 | } | 783 | } |
733 | status = -ENOMEM; | ||
734 | opendata = nfs4_opendata_alloc(dentry, sp, openflags, NULL); | 784 | opendata = nfs4_opendata_alloc(dentry, sp, openflags, NULL); |
735 | if (opendata == NULL) | 785 | if (opendata == NULL) |
736 | goto out; | 786 | return -ENOMEM; |
737 | status = _nfs4_proc_open(opendata); | 787 | ret = nfs4_open_recover(opendata, state); |
738 | if (status != 0) | 788 | if (ret == -ESTALE) { |
739 | goto out_nodeleg; | 789 | /* Invalidate the state owner so we don't ever use it again */ |
740 | newstate = nfs4_opendata_to_nfs4_state(opendata); | 790 | nfs4_drop_state_owner(sp); |
741 | if (newstate != state) | 791 | d_drop(dentry); |
742 | goto out_stale; | ||
743 | if (opendata->o_res.delegation_type != 0) { | ||
744 | if (!(delegation->flags & NFS_DELEGATION_NEED_RECLAIM)) | ||
745 | nfs_inode_set_delegation(inode, sp->so_cred, | ||
746 | &opendata->o_res); | ||
747 | else | ||
748 | nfs_inode_reclaim_delegation(inode, sp->so_cred, | ||
749 | &opendata->o_res); | ||
750 | } | 792 | } |
751 | out_close_state: | ||
752 | nfs4_close_state(newstate, openflags); | ||
753 | out_nodeleg: | ||
754 | nfs4_opendata_free(opendata); | 793 | nfs4_opendata_free(opendata); |
755 | clear_bit(NFS_DELEGATED_STATE, &state->flags); | 794 | return ret; |
756 | out: | ||
757 | dput(parent); | ||
758 | return status; | ||
759 | out_stale: | ||
760 | status = -ESTALE; | ||
761 | /* Invalidate the state owner so we don't ever use it again */ | ||
762 | nfs4_drop_state_owner(sp); | ||
763 | d_drop(dentry); | ||
764 | /* Should we be trying to close that stateid? */ | ||
765 | goto out_close_state; | ||
766 | } | 795 | } |
767 | 796 | ||
768 | static inline int nfs4_do_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry) | 797 | static inline int nfs4_do_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state, struct dentry *dentry) |
@@ -781,22 +810,15 @@ static inline int nfs4_do_open_expired(struct nfs4_state_owner *sp, struct nfs4_ | |||
781 | 810 | ||
782 | static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state) | 811 | static int nfs4_open_expired(struct nfs4_state_owner *sp, struct nfs4_state *state) |
783 | { | 812 | { |
784 | struct nfs_inode *nfsi = NFS_I(state->inode); | ||
785 | struct nfs_open_context *ctx; | 813 | struct nfs_open_context *ctx; |
786 | int status; | 814 | int ret; |
787 | 815 | ||
788 | spin_lock(&state->inode->i_lock); | 816 | ctx = nfs4_state_find_open_context(state); |
789 | list_for_each_entry(ctx, &nfsi->open_files, list) { | 817 | if (IS_ERR(ctx)) |
790 | if (ctx->state != state) | 818 | return PTR_ERR(ctx); |
791 | continue; | 819 | ret = nfs4_do_open_expired(sp, state, ctx->dentry); |
792 | get_nfs_open_context(ctx); | 820 | put_nfs_open_context(ctx); |
793 | spin_unlock(&state->inode->i_lock); | 821 | return ret; |
794 | status = nfs4_do_open_expired(sp, state, ctx->dentry); | ||
795 | put_nfs_open_context(ctx); | ||
796 | return status; | ||
797 | } | ||
798 | spin_unlock(&state->inode->i_lock); | ||
799 | return -ENOENT; | ||
800 | } | 822 | } |
801 | 823 | ||
802 | /* | 824 | /* |