diff options
Diffstat (limited to 'fs/nfs/nfs4state.c')
-rw-r--r-- | fs/nfs/nfs4state.c | 415 |
1 files changed, 294 insertions, 121 deletions
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 401ef8b28f97..2022fe47966f 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c | |||
@@ -71,14 +71,12 @@ static int nfs4_init_client(struct nfs_client *clp, struct rpc_cred *cred) | |||
71 | return status; | 71 | return status; |
72 | } | 72 | } |
73 | 73 | ||
74 | static struct rpc_cred *nfs4_get_machine_cred(struct nfs_client *clp) | 74 | static struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp) |
75 | { | 75 | { |
76 | struct rpc_cred *cred = NULL; | 76 | struct rpc_cred *cred = NULL; |
77 | 77 | ||
78 | spin_lock(&clp->cl_lock); | ||
79 | if (clp->cl_machine_cred != NULL) | 78 | if (clp->cl_machine_cred != NULL) |
80 | cred = get_rpccred(clp->cl_machine_cred); | 79 | cred = get_rpccred(clp->cl_machine_cred); |
81 | spin_unlock(&clp->cl_lock); | ||
82 | return cred; | 80 | return cred; |
83 | } | 81 | } |
84 | 82 | ||
@@ -94,7 +92,7 @@ static void nfs4_clear_machine_cred(struct nfs_client *clp) | |||
94 | put_rpccred(cred); | 92 | put_rpccred(cred); |
95 | } | 93 | } |
96 | 94 | ||
97 | struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp) | 95 | struct rpc_cred *nfs4_get_renew_cred_locked(struct nfs_client *clp) |
98 | { | 96 | { |
99 | struct nfs4_state_owner *sp; | 97 | struct nfs4_state_owner *sp; |
100 | struct rb_node *pos; | 98 | struct rb_node *pos; |
@@ -110,13 +108,24 @@ struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp) | |||
110 | return cred; | 108 | return cred; |
111 | } | 109 | } |
112 | 110 | ||
111 | static struct rpc_cred *nfs4_get_renew_cred(struct nfs_client *clp) | ||
112 | { | ||
113 | struct rpc_cred *cred; | ||
114 | |||
115 | spin_lock(&clp->cl_lock); | ||
116 | cred = nfs4_get_renew_cred_locked(clp); | ||
117 | spin_unlock(&clp->cl_lock); | ||
118 | return cred; | ||
119 | } | ||
120 | |||
113 | static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp) | 121 | static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp) |
114 | { | 122 | { |
115 | struct nfs4_state_owner *sp; | 123 | struct nfs4_state_owner *sp; |
116 | struct rb_node *pos; | 124 | struct rb_node *pos; |
117 | struct rpc_cred *cred; | 125 | struct rpc_cred *cred; |
118 | 126 | ||
119 | cred = nfs4_get_machine_cred(clp); | 127 | spin_lock(&clp->cl_lock); |
128 | cred = nfs4_get_machine_cred_locked(clp); | ||
120 | if (cred != NULL) | 129 | if (cred != NULL) |
121 | goto out; | 130 | goto out; |
122 | pos = rb_first(&clp->cl_state_owners); | 131 | pos = rb_first(&clp->cl_state_owners); |
@@ -125,6 +134,7 @@ static struct rpc_cred *nfs4_get_setclientid_cred(struct nfs_client *clp) | |||
125 | cred = get_rpccred(sp->so_cred); | 134 | cred = get_rpccred(sp->so_cred); |
126 | } | 135 | } |
127 | out: | 136 | out: |
137 | spin_unlock(&clp->cl_lock); | ||
128 | return cred; | 138 | return cred; |
129 | } | 139 | } |
130 | 140 | ||
@@ -295,10 +305,6 @@ nfs4_drop_state_owner(struct nfs4_state_owner *sp) | |||
295 | } | 305 | } |
296 | } | 306 | } |
297 | 307 | ||
298 | /* | ||
299 | * Note: must be called with clp->cl_sem held in order to prevent races | ||
300 | * with reboot recovery! | ||
301 | */ | ||
302 | struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred) | 308 | struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct rpc_cred *cred) |
303 | { | 309 | { |
304 | struct nfs_client *clp = server->nfs_client; | 310 | struct nfs_client *clp = server->nfs_client; |
@@ -327,10 +333,6 @@ struct nfs4_state_owner *nfs4_get_state_owner(struct nfs_server *server, struct | |||
327 | return sp; | 333 | return sp; |
328 | } | 334 | } |
329 | 335 | ||
330 | /* | ||
331 | * Must be called with clp->cl_sem held in order to avoid races | ||
332 | * with state recovery... | ||
333 | */ | ||
334 | void nfs4_put_state_owner(struct nfs4_state_owner *sp) | 336 | void nfs4_put_state_owner(struct nfs4_state_owner *sp) |
335 | { | 337 | { |
336 | struct nfs_client *clp = sp->so_client; | 338 | struct nfs_client *clp = sp->so_client; |
@@ -361,18 +363,18 @@ nfs4_alloc_open_state(void) | |||
361 | } | 363 | } |
362 | 364 | ||
363 | void | 365 | void |
364 | nfs4_state_set_mode_locked(struct nfs4_state *state, mode_t mode) | 366 | nfs4_state_set_mode_locked(struct nfs4_state *state, fmode_t fmode) |
365 | { | 367 | { |
366 | if (state->state == mode) | 368 | if (state->state == fmode) |
367 | return; | 369 | return; |
368 | /* NB! List reordering - see the reclaim code for why. */ | 370 | /* NB! List reordering - see the reclaim code for why. */ |
369 | if ((mode & FMODE_WRITE) != (state->state & FMODE_WRITE)) { | 371 | if ((fmode & FMODE_WRITE) != (state->state & FMODE_WRITE)) { |
370 | if (mode & FMODE_WRITE) | 372 | if (fmode & FMODE_WRITE) |
371 | list_move(&state->open_states, &state->owner->so_states); | 373 | list_move(&state->open_states, &state->owner->so_states); |
372 | else | 374 | else |
373 | list_move_tail(&state->open_states, &state->owner->so_states); | 375 | list_move_tail(&state->open_states, &state->owner->so_states); |
374 | } | 376 | } |
375 | state->state = mode; | 377 | state->state = fmode; |
376 | } | 378 | } |
377 | 379 | ||
378 | static struct nfs4_state * | 380 | static struct nfs4_state * |
@@ -432,10 +434,6 @@ out: | |||
432 | return state; | 434 | return state; |
433 | } | 435 | } |
434 | 436 | ||
435 | /* | ||
436 | * Beware! Caller must be holding exactly one | ||
437 | * reference to clp->cl_sem! | ||
438 | */ | ||
439 | void nfs4_put_open_state(struct nfs4_state *state) | 437 | void nfs4_put_open_state(struct nfs4_state *state) |
440 | { | 438 | { |
441 | struct inode *inode = state->inode; | 439 | struct inode *inode = state->inode; |
@@ -456,16 +454,16 @@ void nfs4_put_open_state(struct nfs4_state *state) | |||
456 | /* | 454 | /* |
457 | * Close the current file. | 455 | * Close the current file. |
458 | */ | 456 | */ |
459 | static void __nfs4_close(struct path *path, struct nfs4_state *state, mode_t mode, int wait) | 457 | static void __nfs4_close(struct path *path, struct nfs4_state *state, fmode_t fmode, int wait) |
460 | { | 458 | { |
461 | struct nfs4_state_owner *owner = state->owner; | 459 | struct nfs4_state_owner *owner = state->owner; |
462 | int call_close = 0; | 460 | int call_close = 0; |
463 | int newstate; | 461 | fmode_t newstate; |
464 | 462 | ||
465 | atomic_inc(&owner->so_count); | 463 | atomic_inc(&owner->so_count); |
466 | /* Protect against nfs4_find_state() */ | 464 | /* Protect against nfs4_find_state() */ |
467 | spin_lock(&owner->so_lock); | 465 | spin_lock(&owner->so_lock); |
468 | switch (mode & (FMODE_READ | FMODE_WRITE)) { | 466 | switch (fmode & (FMODE_READ | FMODE_WRITE)) { |
469 | case FMODE_READ: | 467 | case FMODE_READ: |
470 | state->n_rdonly--; | 468 | state->n_rdonly--; |
471 | break; | 469 | break; |
@@ -500,14 +498,14 @@ static void __nfs4_close(struct path *path, struct nfs4_state *state, mode_t mod | |||
500 | nfs4_do_close(path, state, wait); | 498 | nfs4_do_close(path, state, wait); |
501 | } | 499 | } |
502 | 500 | ||
503 | void nfs4_close_state(struct path *path, struct nfs4_state *state, mode_t mode) | 501 | void nfs4_close_state(struct path *path, struct nfs4_state *state, fmode_t fmode) |
504 | { | 502 | { |
505 | __nfs4_close(path, state, mode, 0); | 503 | __nfs4_close(path, state, fmode, 0); |
506 | } | 504 | } |
507 | 505 | ||
508 | void nfs4_close_sync(struct path *path, struct nfs4_state *state, mode_t mode) | 506 | void nfs4_close_sync(struct path *path, struct nfs4_state *state, fmode_t fmode) |
509 | { | 507 | { |
510 | __nfs4_close(path, state, mode, 1); | 508 | __nfs4_close(path, state, fmode, 1); |
511 | } | 509 | } |
512 | 510 | ||
513 | /* | 511 | /* |
@@ -568,7 +566,6 @@ static void nfs4_free_lock_state(struct nfs4_lock_state *lsp) | |||
568 | * Return a compatible lock_state. If no initialized lock_state structure | 566 | * Return a compatible lock_state. If no initialized lock_state structure |
569 | * exists, return an uninitialized one. | 567 | * exists, return an uninitialized one. |
570 | * | 568 | * |
571 | * The caller must be holding clp->cl_sem | ||
572 | */ | 569 | */ |
573 | static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner) | 570 | static struct nfs4_lock_state *nfs4_get_lock_state(struct nfs4_state *state, fl_owner_t owner) |
574 | { | 571 | { |
@@ -770,32 +767,34 @@ unlock: | |||
770 | return status; | 767 | return status; |
771 | } | 768 | } |
772 | 769 | ||
773 | static int reclaimer(void *); | 770 | static int nfs4_run_state_manager(void *); |
774 | 771 | ||
775 | static inline void nfs4_clear_recover_bit(struct nfs_client *clp) | 772 | static void nfs4_clear_state_manager_bit(struct nfs_client *clp) |
776 | { | 773 | { |
777 | smp_mb__before_clear_bit(); | 774 | smp_mb__before_clear_bit(); |
778 | clear_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state); | 775 | clear_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state); |
779 | smp_mb__after_clear_bit(); | 776 | smp_mb__after_clear_bit(); |
780 | wake_up_bit(&clp->cl_state, NFS4CLNT_STATE_RECOVER); | 777 | wake_up_bit(&clp->cl_state, NFS4CLNT_MANAGER_RUNNING); |
781 | rpc_wake_up(&clp->cl_rpcwaitq); | 778 | rpc_wake_up(&clp->cl_rpcwaitq); |
782 | } | 779 | } |
783 | 780 | ||
784 | /* | 781 | /* |
785 | * State recovery routine | 782 | * Schedule the nfs_client asynchronous state management routine |
786 | */ | 783 | */ |
787 | static void nfs4_recover_state(struct nfs_client *clp) | 784 | void nfs4_schedule_state_manager(struct nfs_client *clp) |
788 | { | 785 | { |
789 | struct task_struct *task; | 786 | struct task_struct *task; |
790 | 787 | ||
788 | if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0) | ||
789 | return; | ||
791 | __module_get(THIS_MODULE); | 790 | __module_get(THIS_MODULE); |
792 | atomic_inc(&clp->cl_count); | 791 | atomic_inc(&clp->cl_count); |
793 | task = kthread_run(reclaimer, clp, "%s-reclaim", | 792 | task = kthread_run(nfs4_run_state_manager, clp, "%s-manager", |
794 | rpc_peeraddr2str(clp->cl_rpcclient, | 793 | rpc_peeraddr2str(clp->cl_rpcclient, |
795 | RPC_DISPLAY_ADDR)); | 794 | RPC_DISPLAY_ADDR)); |
796 | if (!IS_ERR(task)) | 795 | if (!IS_ERR(task)) |
797 | return; | 796 | return; |
798 | nfs4_clear_recover_bit(clp); | 797 | nfs4_clear_state_manager_bit(clp); |
799 | nfs_put_client(clp); | 798 | nfs_put_client(clp); |
800 | module_put(THIS_MODULE); | 799 | module_put(THIS_MODULE); |
801 | } | 800 | } |
@@ -807,16 +806,42 @@ void nfs4_schedule_state_recovery(struct nfs_client *clp) | |||
807 | { | 806 | { |
808 | if (!clp) | 807 | if (!clp) |
809 | return; | 808 | return; |
810 | if (test_and_set_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) == 0) | 809 | if (!test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) |
811 | nfs4_recover_state(clp); | 810 | set_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state); |
811 | nfs4_schedule_state_manager(clp); | ||
812 | } | 812 | } |
813 | 813 | ||
814 | static int nfs4_reclaim_locks(struct nfs4_state_recovery_ops *ops, struct nfs4_state *state) | 814 | static int nfs4_state_mark_reclaim_reboot(struct nfs_client *clp, struct nfs4_state *state) |
815 | { | ||
816 | |||
817 | set_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags); | ||
818 | /* Don't recover state that expired before the reboot */ | ||
819 | if (test_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags)) { | ||
820 | clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags); | ||
821 | return 0; | ||
822 | } | ||
823 | set_bit(NFS_OWNER_RECLAIM_REBOOT, &state->owner->so_flags); | ||
824 | set_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state); | ||
825 | return 1; | ||
826 | } | ||
827 | |||
828 | int nfs4_state_mark_reclaim_nograce(struct nfs_client *clp, struct nfs4_state *state) | ||
829 | { | ||
830 | set_bit(NFS_STATE_RECLAIM_NOGRACE, &state->flags); | ||
831 | clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags); | ||
832 | set_bit(NFS_OWNER_RECLAIM_NOGRACE, &state->owner->so_flags); | ||
833 | set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state); | ||
834 | return 1; | ||
835 | } | ||
836 | |||
837 | static int nfs4_reclaim_locks(struct nfs4_state *state, const struct nfs4_state_recovery_ops *ops) | ||
815 | { | 838 | { |
816 | struct inode *inode = state->inode; | 839 | struct inode *inode = state->inode; |
840 | struct nfs_inode *nfsi = NFS_I(inode); | ||
817 | struct file_lock *fl; | 841 | struct file_lock *fl; |
818 | int status = 0; | 842 | int status = 0; |
819 | 843 | ||
844 | down_write(&nfsi->rwsem); | ||
820 | for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { | 845 | for (fl = inode->i_flock; fl != NULL; fl = fl->fl_next) { |
821 | if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) | 846 | if (!(fl->fl_flags & (FL_POSIX|FL_FLOCK))) |
822 | continue; | 847 | continue; |
@@ -839,12 +864,14 @@ static int nfs4_reclaim_locks(struct nfs4_state_recovery_ops *ops, struct nfs4_s | |||
839 | goto out_err; | 864 | goto out_err; |
840 | } | 865 | } |
841 | } | 866 | } |
867 | up_write(&nfsi->rwsem); | ||
842 | return 0; | 868 | return 0; |
843 | out_err: | 869 | out_err: |
870 | up_write(&nfsi->rwsem); | ||
844 | return status; | 871 | return status; |
845 | } | 872 | } |
846 | 873 | ||
847 | static int nfs4_reclaim_open_state(struct nfs4_state_recovery_ops *ops, struct nfs4_state_owner *sp) | 874 | static int nfs4_reclaim_open_state(struct nfs4_state_owner *sp, const struct nfs4_state_recovery_ops *ops) |
848 | { | 875 | { |
849 | struct nfs4_state *state; | 876 | struct nfs4_state *state; |
850 | struct nfs4_lock_state *lock; | 877 | struct nfs4_lock_state *lock; |
@@ -858,28 +885,34 @@ static int nfs4_reclaim_open_state(struct nfs4_state_recovery_ops *ops, struct n | |||
858 | * recovering after a network partition or a reboot from a | 885 | * recovering after a network partition or a reboot from a |
859 | * server that doesn't support a grace period. | 886 | * server that doesn't support a grace period. |
860 | */ | 887 | */ |
888 | restart: | ||
889 | spin_lock(&sp->so_lock); | ||
861 | list_for_each_entry(state, &sp->so_states, open_states) { | 890 | list_for_each_entry(state, &sp->so_states, open_states) { |
891 | if (!test_and_clear_bit(ops->state_flag_bit, &state->flags)) | ||
892 | continue; | ||
862 | if (state->state == 0) | 893 | if (state->state == 0) |
863 | continue; | 894 | continue; |
895 | atomic_inc(&state->count); | ||
896 | spin_unlock(&sp->so_lock); | ||
864 | status = ops->recover_open(sp, state); | 897 | status = ops->recover_open(sp, state); |
865 | if (status >= 0) { | 898 | if (status >= 0) { |
866 | status = nfs4_reclaim_locks(ops, state); | 899 | status = nfs4_reclaim_locks(state, ops); |
867 | if (status < 0) | 900 | if (status >= 0) { |
868 | goto out_err; | 901 | list_for_each_entry(lock, &state->lock_states, ls_locks) { |
869 | list_for_each_entry(lock, &state->lock_states, ls_locks) { | 902 | if (!(lock->ls_flags & NFS_LOCK_INITIALIZED)) |
870 | if (!(lock->ls_flags & NFS_LOCK_INITIALIZED)) | 903 | printk("%s: Lock reclaim failed!\n", |
871 | printk("%s: Lock reclaim failed!\n", | ||
872 | __func__); | 904 | __func__); |
905 | } | ||
906 | nfs4_put_open_state(state); | ||
907 | goto restart; | ||
873 | } | 908 | } |
874 | continue; | ||
875 | } | 909 | } |
876 | switch (status) { | 910 | switch (status) { |
877 | default: | 911 | default: |
878 | printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n", | 912 | printk(KERN_ERR "%s: unhandled error %d. Zeroing state\n", |
879 | __func__, status); | 913 | __func__, status); |
880 | case -ENOENT: | 914 | case -ENOENT: |
881 | case -NFS4ERR_RECLAIM_BAD: | 915 | case -ESTALE: |
882 | case -NFS4ERR_RECLAIM_CONFLICT: | ||
883 | /* | 916 | /* |
884 | * Open state on this file cannot be recovered | 917 | * Open state on this file cannot be recovered |
885 | * All we can do is revert to using the zero stateid. | 918 | * All we can do is revert to using the zero stateid. |
@@ -889,84 +922,176 @@ static int nfs4_reclaim_open_state(struct nfs4_state_recovery_ops *ops, struct n | |||
889 | /* Mark the file as being 'closed' */ | 922 | /* Mark the file as being 'closed' */ |
890 | state->state = 0; | 923 | state->state = 0; |
891 | break; | 924 | break; |
925 | case -NFS4ERR_RECLAIM_BAD: | ||
926 | case -NFS4ERR_RECLAIM_CONFLICT: | ||
927 | nfs4_state_mark_reclaim_nograce(sp->so_client, state); | ||
928 | break; | ||
892 | case -NFS4ERR_EXPIRED: | 929 | case -NFS4ERR_EXPIRED: |
893 | case -NFS4ERR_NO_GRACE: | 930 | case -NFS4ERR_NO_GRACE: |
931 | nfs4_state_mark_reclaim_nograce(sp->so_client, state); | ||
894 | case -NFS4ERR_STALE_CLIENTID: | 932 | case -NFS4ERR_STALE_CLIENTID: |
895 | goto out_err; | 933 | goto out_err; |
896 | } | 934 | } |
935 | nfs4_put_open_state(state); | ||
936 | goto restart; | ||
897 | } | 937 | } |
938 | spin_unlock(&sp->so_lock); | ||
898 | return 0; | 939 | return 0; |
899 | out_err: | 940 | out_err: |
941 | nfs4_put_open_state(state); | ||
900 | return status; | 942 | return status; |
901 | } | 943 | } |
902 | 944 | ||
903 | static void nfs4_state_mark_reclaim(struct nfs_client *clp) | 945 | static void nfs4_clear_open_state(struct nfs4_state *state) |
946 | { | ||
947 | struct nfs4_lock_state *lock; | ||
948 | |||
949 | clear_bit(NFS_DELEGATED_STATE, &state->flags); | ||
950 | clear_bit(NFS_O_RDONLY_STATE, &state->flags); | ||
951 | clear_bit(NFS_O_WRONLY_STATE, &state->flags); | ||
952 | clear_bit(NFS_O_RDWR_STATE, &state->flags); | ||
953 | list_for_each_entry(lock, &state->lock_states, ls_locks) { | ||
954 | lock->ls_seqid.flags = 0; | ||
955 | lock->ls_flags &= ~NFS_LOCK_INITIALIZED; | ||
956 | } | ||
957 | } | ||
958 | |||
959 | static void nfs4_state_mark_reclaim_helper(struct nfs_client *clp, int (*mark_reclaim)(struct nfs_client *clp, struct nfs4_state *state)) | ||
904 | { | 960 | { |
905 | struct nfs4_state_owner *sp; | 961 | struct nfs4_state_owner *sp; |
906 | struct rb_node *pos; | 962 | struct rb_node *pos; |
907 | struct nfs4_state *state; | 963 | struct nfs4_state *state; |
908 | struct nfs4_lock_state *lock; | ||
909 | 964 | ||
910 | /* Reset all sequence ids to zero */ | 965 | /* Reset all sequence ids to zero */ |
911 | for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) { | 966 | for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) { |
912 | sp = rb_entry(pos, struct nfs4_state_owner, so_client_node); | 967 | sp = rb_entry(pos, struct nfs4_state_owner, so_client_node); |
913 | sp->so_seqid.counter = 0; | ||
914 | sp->so_seqid.flags = 0; | 968 | sp->so_seqid.flags = 0; |
915 | spin_lock(&sp->so_lock); | 969 | spin_lock(&sp->so_lock); |
916 | list_for_each_entry(state, &sp->so_states, open_states) { | 970 | list_for_each_entry(state, &sp->so_states, open_states) { |
917 | clear_bit(NFS_DELEGATED_STATE, &state->flags); | 971 | if (mark_reclaim(clp, state)) |
918 | clear_bit(NFS_O_RDONLY_STATE, &state->flags); | 972 | nfs4_clear_open_state(state); |
919 | clear_bit(NFS_O_WRONLY_STATE, &state->flags); | ||
920 | clear_bit(NFS_O_RDWR_STATE, &state->flags); | ||
921 | list_for_each_entry(lock, &state->lock_states, ls_locks) { | ||
922 | lock->ls_seqid.counter = 0; | ||
923 | lock->ls_seqid.flags = 0; | ||
924 | lock->ls_flags &= ~NFS_LOCK_INITIALIZED; | ||
925 | } | ||
926 | } | 973 | } |
927 | spin_unlock(&sp->so_lock); | 974 | spin_unlock(&sp->so_lock); |
928 | } | 975 | } |
929 | } | 976 | } |
930 | 977 | ||
931 | static int reclaimer(void *ptr) | 978 | static void nfs4_state_start_reclaim_reboot(struct nfs_client *clp) |
979 | { | ||
980 | /* Mark all delegations for reclaim */ | ||
981 | nfs_delegation_mark_reclaim(clp); | ||
982 | nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_reboot); | ||
983 | } | ||
984 | |||
985 | static void nfs4_state_end_reclaim_reboot(struct nfs_client *clp) | ||
932 | { | 986 | { |
933 | struct nfs_client *clp = ptr; | ||
934 | struct nfs4_state_owner *sp; | 987 | struct nfs4_state_owner *sp; |
935 | struct rb_node *pos; | 988 | struct rb_node *pos; |
936 | struct nfs4_state_recovery_ops *ops; | 989 | struct nfs4_state *state; |
937 | struct rpc_cred *cred; | 990 | |
991 | if (!test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) | ||
992 | return; | ||
993 | |||
994 | for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) { | ||
995 | sp = rb_entry(pos, struct nfs4_state_owner, so_client_node); | ||
996 | spin_lock(&sp->so_lock); | ||
997 | list_for_each_entry(state, &sp->so_states, open_states) { | ||
998 | if (!test_and_clear_bit(NFS_STATE_RECLAIM_REBOOT, &state->flags)) | ||
999 | continue; | ||
1000 | nfs4_state_mark_reclaim_nograce(clp, state); | ||
1001 | } | ||
1002 | spin_unlock(&sp->so_lock); | ||
1003 | } | ||
1004 | |||
1005 | nfs_delegation_reap_unclaimed(clp); | ||
1006 | } | ||
1007 | |||
1008 | static void nfs_delegation_clear_all(struct nfs_client *clp) | ||
1009 | { | ||
1010 | nfs_delegation_mark_reclaim(clp); | ||
1011 | nfs_delegation_reap_unclaimed(clp); | ||
1012 | } | ||
1013 | |||
1014 | static void nfs4_state_start_reclaim_nograce(struct nfs_client *clp) | ||
1015 | { | ||
1016 | nfs_delegation_clear_all(clp); | ||
1017 | nfs4_state_mark_reclaim_helper(clp, nfs4_state_mark_reclaim_nograce); | ||
1018 | } | ||
1019 | |||
1020 | static void nfs4_state_end_reclaim_nograce(struct nfs_client *clp) | ||
1021 | { | ||
1022 | clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state); | ||
1023 | } | ||
1024 | |||
1025 | static void nfs4_recovery_handle_error(struct nfs_client *clp, int error) | ||
1026 | { | ||
1027 | switch (error) { | ||
1028 | case -NFS4ERR_CB_PATH_DOWN: | ||
1029 | nfs_handle_cb_pathdown(clp); | ||
1030 | break; | ||
1031 | case -NFS4ERR_STALE_CLIENTID: | ||
1032 | case -NFS4ERR_LEASE_MOVED: | ||
1033 | set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); | ||
1034 | nfs4_state_start_reclaim_reboot(clp); | ||
1035 | break; | ||
1036 | case -NFS4ERR_EXPIRED: | ||
1037 | set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); | ||
1038 | nfs4_state_start_reclaim_nograce(clp); | ||
1039 | } | ||
1040 | } | ||
1041 | |||
1042 | static int nfs4_do_reclaim(struct nfs_client *clp, const struct nfs4_state_recovery_ops *ops) | ||
1043 | { | ||
1044 | struct rb_node *pos; | ||
938 | int status = 0; | 1045 | int status = 0; |
939 | 1046 | ||
940 | allow_signal(SIGKILL); | 1047 | restart: |
1048 | spin_lock(&clp->cl_lock); | ||
1049 | for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) { | ||
1050 | struct nfs4_state_owner *sp = rb_entry(pos, struct nfs4_state_owner, so_client_node); | ||
1051 | if (!test_and_clear_bit(ops->owner_flag_bit, &sp->so_flags)) | ||
1052 | continue; | ||
1053 | atomic_inc(&sp->so_count); | ||
1054 | spin_unlock(&clp->cl_lock); | ||
1055 | status = nfs4_reclaim_open_state(sp, ops); | ||
1056 | if (status < 0) { | ||
1057 | set_bit(ops->owner_flag_bit, &sp->so_flags); | ||
1058 | nfs4_put_state_owner(sp); | ||
1059 | nfs4_recovery_handle_error(clp, status); | ||
1060 | return status; | ||
1061 | } | ||
1062 | nfs4_put_state_owner(sp); | ||
1063 | goto restart; | ||
1064 | } | ||
1065 | spin_unlock(&clp->cl_lock); | ||
1066 | return status; | ||
1067 | } | ||
941 | 1068 | ||
942 | /* Ensure exclusive access to NFSv4 state */ | 1069 | static int nfs4_check_lease(struct nfs_client *clp) |
943 | down_write(&clp->cl_sem); | 1070 | { |
944 | /* Are there any NFS mounts out there? */ | 1071 | struct rpc_cred *cred; |
945 | if (list_empty(&clp->cl_superblocks)) | 1072 | int status = -NFS4ERR_EXPIRED; |
946 | goto out; | 1073 | |
947 | restart_loop: | 1074 | /* Is the client already known to have an expired lease? */ |
948 | ops = &nfs4_network_partition_recovery_ops; | 1075 | if (test_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) |
949 | /* Are there any open files on this volume? */ | 1076 | return 0; |
950 | cred = nfs4_get_renew_cred(clp); | 1077 | cred = nfs4_get_renew_cred(clp); |
951 | if (cred != NULL) { | 1078 | if (cred == NULL) { |
952 | /* Yes there are: try to renew the old lease */ | 1079 | cred = nfs4_get_setclientid_cred(clp); |
953 | status = nfs4_proc_renew(clp, cred); | 1080 | if (cred == NULL) |
954 | put_rpccred(cred); | 1081 | goto out; |
955 | switch (status) { | ||
956 | case 0: | ||
957 | case -NFS4ERR_CB_PATH_DOWN: | ||
958 | goto out; | ||
959 | case -NFS4ERR_STALE_CLIENTID: | ||
960 | case -NFS4ERR_LEASE_MOVED: | ||
961 | ops = &nfs4_reboot_recovery_ops; | ||
962 | } | ||
963 | } else { | ||
964 | /* "reboot" to ensure we clear all state on the server */ | ||
965 | clp->cl_boot_time = CURRENT_TIME; | ||
966 | } | 1082 | } |
967 | /* We're going to have to re-establish a clientid */ | 1083 | status = nfs4_proc_renew(clp, cred); |
968 | nfs4_state_mark_reclaim(clp); | 1084 | put_rpccred(cred); |
969 | status = -ENOENT; | 1085 | out: |
1086 | nfs4_recovery_handle_error(clp, status); | ||
1087 | return status; | ||
1088 | } | ||
1089 | |||
1090 | static int nfs4_reclaim_lease(struct nfs_client *clp) | ||
1091 | { | ||
1092 | struct rpc_cred *cred; | ||
1093 | int status = -ENOENT; | ||
1094 | |||
970 | cred = nfs4_get_setclientid_cred(clp); | 1095 | cred = nfs4_get_setclientid_cred(clp); |
971 | if (cred != NULL) { | 1096 | if (cred != NULL) { |
972 | status = nfs4_init_client(clp, cred); | 1097 | status = nfs4_init_client(clp, cred); |
@@ -974,42 +1099,90 @@ restart_loop: | |||
974 | /* Handle case where the user hasn't set up machine creds */ | 1099 | /* Handle case where the user hasn't set up machine creds */ |
975 | if (status == -EACCES && cred == clp->cl_machine_cred) { | 1100 | if (status == -EACCES && cred == clp->cl_machine_cred) { |
976 | nfs4_clear_machine_cred(clp); | 1101 | nfs4_clear_machine_cred(clp); |
977 | goto restart_loop; | 1102 | status = -EAGAIN; |
978 | } | 1103 | } |
979 | } | 1104 | } |
980 | if (status) | 1105 | return status; |
981 | goto out_error; | 1106 | } |
982 | /* Mark all delegations for reclaim */ | 1107 | |
983 | nfs_delegation_mark_reclaim(clp); | 1108 | static void nfs4_state_manager(struct nfs_client *clp) |
984 | /* Note: list is protected by exclusive lock on cl->cl_sem */ | 1109 | { |
985 | for (pos = rb_first(&clp->cl_state_owners); pos != NULL; pos = rb_next(pos)) { | 1110 | int status = 0; |
986 | sp = rb_entry(pos, struct nfs4_state_owner, so_client_node); | 1111 | |
987 | status = nfs4_reclaim_open_state(ops, sp); | 1112 | /* Ensure exclusive access to NFSv4 state */ |
988 | if (status < 0) { | 1113 | for(;;) { |
989 | if (status == -NFS4ERR_NO_GRACE) { | 1114 | if (test_and_clear_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state)) { |
990 | ops = &nfs4_network_partition_recovery_ops; | 1115 | /* We're going to have to re-establish a clientid */ |
991 | status = nfs4_reclaim_open_state(ops, sp); | 1116 | status = nfs4_reclaim_lease(clp); |
1117 | if (status) { | ||
1118 | set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); | ||
1119 | if (status == -EAGAIN) | ||
1120 | continue; | ||
1121 | goto out_error; | ||
992 | } | 1122 | } |
1123 | clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state); | ||
1124 | } | ||
1125 | |||
1126 | if (test_and_clear_bit(NFS4CLNT_CHECK_LEASE, &clp->cl_state)) { | ||
1127 | status = nfs4_check_lease(clp); | ||
1128 | if (status != 0) | ||
1129 | continue; | ||
1130 | } | ||
1131 | |||
1132 | /* First recover reboot state... */ | ||
1133 | if (test_and_clear_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) { | ||
1134 | status = nfs4_do_reclaim(clp, &nfs4_reboot_recovery_ops); | ||
993 | if (status == -NFS4ERR_STALE_CLIENTID) | 1135 | if (status == -NFS4ERR_STALE_CLIENTID) |
994 | goto restart_loop; | 1136 | continue; |
995 | if (status == -NFS4ERR_EXPIRED) | 1137 | nfs4_state_end_reclaim_reboot(clp); |
996 | goto restart_loop; | 1138 | continue; |
1139 | } | ||
1140 | |||
1141 | /* Now recover expired state... */ | ||
1142 | if (test_and_clear_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state)) { | ||
1143 | status = nfs4_do_reclaim(clp, &nfs4_nograce_recovery_ops); | ||
1144 | if (status < 0) { | ||
1145 | set_bit(NFS4CLNT_RECLAIM_NOGRACE, &clp->cl_state); | ||
1146 | if (status == -NFS4ERR_STALE_CLIENTID) | ||
1147 | continue; | ||
1148 | if (status == -NFS4ERR_EXPIRED) | ||
1149 | continue; | ||
1150 | goto out_error; | ||
1151 | } else | ||
1152 | nfs4_state_end_reclaim_nograce(clp); | ||
1153 | continue; | ||
997 | } | 1154 | } |
1155 | |||
1156 | if (test_and_clear_bit(NFS4CLNT_DELEGRETURN, &clp->cl_state)) { | ||
1157 | nfs_client_return_marked_delegations(clp); | ||
1158 | continue; | ||
1159 | } | ||
1160 | |||
1161 | nfs4_clear_state_manager_bit(clp); | ||
1162 | /* Did we race with an attempt to give us more work? */ | ||
1163 | if (clp->cl_state == 0) | ||
1164 | break; | ||
1165 | if (test_and_set_bit(NFS4CLNT_MANAGER_RUNNING, &clp->cl_state) != 0) | ||
1166 | break; | ||
998 | } | 1167 | } |
999 | nfs_delegation_reap_unclaimed(clp); | 1168 | return; |
1000 | out: | 1169 | out_error: |
1001 | up_write(&clp->cl_sem); | 1170 | printk(KERN_WARNING "Error: state manager failed on NFSv4 server %s" |
1002 | if (status == -NFS4ERR_CB_PATH_DOWN) | 1171 | " with error %d\n", clp->cl_hostname, -status); |
1003 | nfs_handle_cb_pathdown(clp); | 1172 | if (test_bit(NFS4CLNT_RECLAIM_REBOOT, &clp->cl_state)) |
1004 | nfs4_clear_recover_bit(clp); | 1173 | nfs4_state_end_reclaim_reboot(clp); |
1174 | nfs4_clear_state_manager_bit(clp); | ||
1175 | } | ||
1176 | |||
1177 | static int nfs4_run_state_manager(void *ptr) | ||
1178 | { | ||
1179 | struct nfs_client *clp = ptr; | ||
1180 | |||
1181 | allow_signal(SIGKILL); | ||
1182 | nfs4_state_manager(clp); | ||
1005 | nfs_put_client(clp); | 1183 | nfs_put_client(clp); |
1006 | module_put_and_exit(0); | 1184 | module_put_and_exit(0); |
1007 | return 0; | 1185 | return 0; |
1008 | out_error: | ||
1009 | printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %s" | ||
1010 | " with error %d\n", clp->cl_hostname, -status); | ||
1011 | set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state); | ||
1012 | goto out; | ||
1013 | } | 1186 | } |
1014 | 1187 | ||
1015 | /* | 1188 | /* |