diff options
Diffstat (limited to 'fs/nfs/nfs4state.c')
| -rw-r--r-- | fs/nfs/nfs4state.c | 183 |
1 files changed, 104 insertions, 79 deletions
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 5ef4c57618fe..afad0255e7db 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c | |||
| @@ -43,6 +43,8 @@ | |||
| 43 | #include <linux/smp_lock.h> | 43 | #include <linux/smp_lock.h> |
| 44 | #include <linux/nfs_fs.h> | 44 | #include <linux/nfs_fs.h> |
| 45 | #include <linux/nfs_idmap.h> | 45 | #include <linux/nfs_idmap.h> |
| 46 | #include <linux/kthread.h> | ||
| 47 | #include <linux/module.h> | ||
| 46 | #include <linux/workqueue.h> | 48 | #include <linux/workqueue.h> |
| 47 | #include <linux/bitops.h> | 49 | #include <linux/bitops.h> |
| 48 | 50 | ||
| @@ -57,8 +59,6 @@ const nfs4_stateid zero_stateid; | |||
| 57 | static DEFINE_SPINLOCK(state_spinlock); | 59 | static DEFINE_SPINLOCK(state_spinlock); |
| 58 | static LIST_HEAD(nfs4_clientid_list); | 60 | static LIST_HEAD(nfs4_clientid_list); |
| 59 | 61 | ||
| 60 | static void nfs4_recover_state(void *); | ||
| 61 | |||
| 62 | void | 62 | void |
| 63 | init_nfsv4_state(struct nfs_server *server) | 63 | init_nfsv4_state(struct nfs_server *server) |
| 64 | { | 64 | { |
| @@ -91,11 +91,10 @@ nfs4_alloc_client(struct in_addr *addr) | |||
| 91 | 91 | ||
| 92 | if (nfs_callback_up() < 0) | 92 | if (nfs_callback_up() < 0) |
| 93 | return NULL; | 93 | return NULL; |
| 94 | if ((clp = kmalloc(sizeof(*clp), GFP_KERNEL)) == NULL) { | 94 | if ((clp = kzalloc(sizeof(*clp), GFP_KERNEL)) == NULL) { |
| 95 | nfs_callback_down(); | 95 | nfs_callback_down(); |
| 96 | return NULL; | 96 | return NULL; |
| 97 | } | 97 | } |
| 98 | memset(clp, 0, sizeof(*clp)); | ||
| 99 | memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr)); | 98 | memcpy(&clp->cl_addr, addr, sizeof(clp->cl_addr)); |
| 100 | init_rwsem(&clp->cl_sem); | 99 | init_rwsem(&clp->cl_sem); |
| 101 | INIT_LIST_HEAD(&clp->cl_delegations); | 100 | INIT_LIST_HEAD(&clp->cl_delegations); |
| @@ -103,14 +102,12 @@ nfs4_alloc_client(struct in_addr *addr) | |||
| 103 | INIT_LIST_HEAD(&clp->cl_unused); | 102 | INIT_LIST_HEAD(&clp->cl_unused); |
| 104 | spin_lock_init(&clp->cl_lock); | 103 | spin_lock_init(&clp->cl_lock); |
| 105 | atomic_set(&clp->cl_count, 1); | 104 | atomic_set(&clp->cl_count, 1); |
| 106 | INIT_WORK(&clp->cl_recoverd, nfs4_recover_state, clp); | ||
| 107 | INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp); | 105 | INIT_WORK(&clp->cl_renewd, nfs4_renew_state, clp); |
| 108 | INIT_LIST_HEAD(&clp->cl_superblocks); | 106 | INIT_LIST_HEAD(&clp->cl_superblocks); |
| 109 | init_waitqueue_head(&clp->cl_waitq); | ||
| 110 | rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS4 client"); | 107 | rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS4 client"); |
| 111 | clp->cl_rpcclient = ERR_PTR(-EINVAL); | 108 | clp->cl_rpcclient = ERR_PTR(-EINVAL); |
| 112 | clp->cl_boot_time = CURRENT_TIME; | 109 | clp->cl_boot_time = CURRENT_TIME; |
| 113 | clp->cl_state = 1 << NFS4CLNT_OK; | 110 | clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; |
| 114 | return clp; | 111 | return clp; |
| 115 | } | 112 | } |
| 116 | 113 | ||
| @@ -127,8 +124,6 @@ nfs4_free_client(struct nfs4_client *clp) | |||
| 127 | kfree(sp); | 124 | kfree(sp); |
| 128 | } | 125 | } |
| 129 | BUG_ON(!list_empty(&clp->cl_state_owners)); | 126 | BUG_ON(!list_empty(&clp->cl_state_owners)); |
| 130 | if (clp->cl_cred) | ||
| 131 | put_rpccred(clp->cl_cred); | ||
| 132 | nfs_idmap_delete(clp); | 127 | nfs_idmap_delete(clp); |
| 133 | if (!IS_ERR(clp->cl_rpcclient)) | 128 | if (!IS_ERR(clp->cl_rpcclient)) |
| 134 | rpc_shutdown_client(clp->cl_rpcclient); | 129 | rpc_shutdown_client(clp->cl_rpcclient); |
| @@ -193,27 +188,22 @@ nfs4_put_client(struct nfs4_client *clp) | |||
| 193 | list_del(&clp->cl_servers); | 188 | list_del(&clp->cl_servers); |
| 194 | spin_unlock(&state_spinlock); | 189 | spin_unlock(&state_spinlock); |
| 195 | BUG_ON(!list_empty(&clp->cl_superblocks)); | 190 | BUG_ON(!list_empty(&clp->cl_superblocks)); |
| 196 | wake_up_all(&clp->cl_waitq); | ||
| 197 | rpc_wake_up(&clp->cl_rpcwaitq); | 191 | rpc_wake_up(&clp->cl_rpcwaitq); |
| 198 | nfs4_kill_renewd(clp); | 192 | nfs4_kill_renewd(clp); |
| 199 | nfs4_free_client(clp); | 193 | nfs4_free_client(clp); |
| 200 | } | 194 | } |
| 201 | 195 | ||
| 202 | static int __nfs4_init_client(struct nfs4_client *clp) | 196 | static int nfs4_init_client(struct nfs4_client *clp, struct rpc_cred *cred) |
| 203 | { | 197 | { |
| 204 | int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, nfs_callback_tcpport); | 198 | int status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, |
| 199 | nfs_callback_tcpport, cred); | ||
| 205 | if (status == 0) | 200 | if (status == 0) |
| 206 | status = nfs4_proc_setclientid_confirm(clp); | 201 | status = nfs4_proc_setclientid_confirm(clp, cred); |
| 207 | if (status == 0) | 202 | if (status == 0) |
| 208 | nfs4_schedule_state_renewal(clp); | 203 | nfs4_schedule_state_renewal(clp); |
| 209 | return status; | 204 | return status; |
| 210 | } | 205 | } |
| 211 | 206 | ||
| 212 | int nfs4_init_client(struct nfs4_client *clp) | ||
| 213 | { | ||
| 214 | return nfs4_map_errors(__nfs4_init_client(clp)); | ||
| 215 | } | ||
| 216 | |||
| 217 | u32 | 207 | u32 |
| 218 | nfs4_alloc_lockowner_id(struct nfs4_client *clp) | 208 | nfs4_alloc_lockowner_id(struct nfs4_client *clp) |
| 219 | { | 209 | { |
| @@ -235,6 +225,32 @@ nfs4_client_grab_unused(struct nfs4_client *clp, struct rpc_cred *cred) | |||
| 235 | return sp; | 225 | return sp; |
| 236 | } | 226 | } |
| 237 | 227 | ||
| 228 | struct rpc_cred *nfs4_get_renew_cred(struct nfs4_client *clp) | ||
| 229 | { | ||
| 230 | struct nfs4_state_owner *sp; | ||
| 231 | struct rpc_cred *cred = NULL; | ||
| 232 | |||
| 233 | list_for_each_entry(sp, &clp->cl_state_owners, so_list) { | ||
| 234 | if (list_empty(&sp->so_states)) | ||
| 235 | continue; | ||
| 236 | cred = get_rpccred(sp->so_cred); | ||
| 237 | break; | ||
| 238 | } | ||
| 239 | return cred; | ||
| 240 | } | ||
| 241 | |||
| 242 | struct rpc_cred *nfs4_get_setclientid_cred(struct nfs4_client *clp) | ||
| 243 | { | ||
| 244 | struct nfs4_state_owner *sp; | ||
| 245 | |||
| 246 | if (!list_empty(&clp->cl_state_owners)) { | ||
| 247 | sp = list_entry(clp->cl_state_owners.next, | ||
| 248 | struct nfs4_state_owner, so_list); | ||
| 249 | return get_rpccred(sp->so_cred); | ||
| 250 | } | ||
| 251 | return NULL; | ||
| 252 | } | ||
| 253 | |||
| 238 | static struct nfs4_state_owner * | 254 | static struct nfs4_state_owner * |
| 239 | nfs4_find_state_owner(struct nfs4_client *clp, struct rpc_cred *cred) | 255 | nfs4_find_state_owner(struct nfs4_client *clp, struct rpc_cred *cred) |
| 240 | { | 256 | { |
| @@ -349,14 +365,9 @@ nfs4_alloc_open_state(void) | |||
| 349 | { | 365 | { |
| 350 | struct nfs4_state *state; | 366 | struct nfs4_state *state; |
| 351 | 367 | ||
| 352 | state = kmalloc(sizeof(*state), GFP_KERNEL); | 368 | state = kzalloc(sizeof(*state), GFP_KERNEL); |
| 353 | if (!state) | 369 | if (!state) |
| 354 | return NULL; | 370 | return NULL; |
| 355 | state->state = 0; | ||
| 356 | state->nreaders = 0; | ||
| 357 | state->nwriters = 0; | ||
| 358 | state->flags = 0; | ||
| 359 | memset(state->stateid.data, 0, sizeof(state->stateid.data)); | ||
| 360 | atomic_set(&state->count, 1); | 371 | atomic_set(&state->count, 1); |
| 361 | INIT_LIST_HEAD(&state->lock_states); | 372 | INIT_LIST_HEAD(&state->lock_states); |
| 362 | spin_lock_init(&state->state_lock); | 373 | spin_lock_init(&state->state_lock); |
| @@ -475,15 +486,23 @@ void nfs4_close_state(struct nfs4_state *state, mode_t mode) | |||
| 475 | /* Protect against nfs4_find_state() */ | 486 | /* Protect against nfs4_find_state() */ |
| 476 | spin_lock(&owner->so_lock); | 487 | spin_lock(&owner->so_lock); |
| 477 | spin_lock(&inode->i_lock); | 488 | spin_lock(&inode->i_lock); |
| 478 | if (mode & FMODE_READ) | 489 | switch (mode & (FMODE_READ | FMODE_WRITE)) { |
| 479 | state->nreaders--; | 490 | case FMODE_READ: |
| 480 | if (mode & FMODE_WRITE) | 491 | state->n_rdonly--; |
| 481 | state->nwriters--; | 492 | break; |
| 493 | case FMODE_WRITE: | ||
| 494 | state->n_wronly--; | ||
| 495 | break; | ||
| 496 | case FMODE_READ|FMODE_WRITE: | ||
| 497 | state->n_rdwr--; | ||
| 498 | } | ||
| 482 | oldstate = newstate = state->state; | 499 | oldstate = newstate = state->state; |
| 483 | if (state->nreaders == 0) | 500 | if (state->n_rdwr == 0) { |
| 484 | newstate &= ~FMODE_READ; | 501 | if (state->n_rdonly == 0) |
| 485 | if (state->nwriters == 0) | 502 | newstate &= ~FMODE_READ; |
| 486 | newstate &= ~FMODE_WRITE; | 503 | if (state->n_wronly == 0) |
| 504 | newstate &= ~FMODE_WRITE; | ||
| 505 | } | ||
| 487 | if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { | 506 | if (test_bit(NFS_DELEGATED_STATE, &state->flags)) { |
| 488 | nfs4_state_set_mode_locked(state, newstate); | 507 | nfs4_state_set_mode_locked(state, newstate); |
| 489 | oldstate = newstate; | 508 | oldstate = newstate; |
| @@ -733,45 +752,43 @@ out: | |||
| 733 | } | 752 | } |
| 734 | 753 | ||
| 735 | static int reclaimer(void *); | 754 | static int reclaimer(void *); |
| 736 | struct reclaimer_args { | 755 | |
| 737 | struct nfs4_client *clp; | 756 | static inline void nfs4_clear_recover_bit(struct nfs4_client *clp) |
| 738 | struct completion complete; | 757 | { |
| 739 | }; | 758 | smp_mb__before_clear_bit(); |
| 759 | clear_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state); | ||
| 760 | smp_mb__after_clear_bit(); | ||
| 761 | wake_up_bit(&clp->cl_state, NFS4CLNT_STATE_RECOVER); | ||
| 762 | rpc_wake_up(&clp->cl_rpcwaitq); | ||
| 763 | } | ||
| 740 | 764 | ||
| 741 | /* | 765 | /* |
| 742 | * State recovery routine | 766 | * State recovery routine |
| 743 | */ | 767 | */ |
| 744 | void | 768 | static void nfs4_recover_state(struct nfs4_client *clp) |
| 745 | nfs4_recover_state(void *data) | ||
| 746 | { | 769 | { |
| 747 | struct nfs4_client *clp = (struct nfs4_client *)data; | 770 | struct task_struct *task; |
| 748 | struct reclaimer_args args = { | ||
| 749 | .clp = clp, | ||
| 750 | }; | ||
| 751 | might_sleep(); | ||
| 752 | |||
| 753 | init_completion(&args.complete); | ||
| 754 | 771 | ||
| 755 | if (kernel_thread(reclaimer, &args, CLONE_KERNEL) < 0) | 772 | __module_get(THIS_MODULE); |
| 756 | goto out_failed_clear; | 773 | atomic_inc(&clp->cl_count); |
| 757 | wait_for_completion(&args.complete); | 774 | task = kthread_run(reclaimer, clp, "%u.%u.%u.%u-reclaim", |
| 758 | return; | 775 | NIPQUAD(clp->cl_addr)); |
| 759 | out_failed_clear: | 776 | if (!IS_ERR(task)) |
| 760 | set_bit(NFS4CLNT_OK, &clp->cl_state); | 777 | return; |
| 761 | wake_up_all(&clp->cl_waitq); | 778 | nfs4_clear_recover_bit(clp); |
| 762 | rpc_wake_up(&clp->cl_rpcwaitq); | 779 | nfs4_put_client(clp); |
| 780 | module_put(THIS_MODULE); | ||
| 763 | } | 781 | } |
| 764 | 782 | ||
| 765 | /* | 783 | /* |
| 766 | * Schedule a state recovery attempt | 784 | * Schedule a state recovery attempt |
| 767 | */ | 785 | */ |
| 768 | void | 786 | void nfs4_schedule_state_recovery(struct nfs4_client *clp) |
| 769 | nfs4_schedule_state_recovery(struct nfs4_client *clp) | ||
| 770 | { | 787 | { |
| 771 | if (!clp) | 788 | if (!clp) |
| 772 | return; | 789 | return; |
| 773 | if (test_and_clear_bit(NFS4CLNT_OK, &clp->cl_state)) | 790 | if (test_and_set_bit(NFS4CLNT_STATE_RECOVER, &clp->cl_state) == 0) |
| 774 | schedule_work(&clp->cl_recoverd); | 791 | nfs4_recover_state(clp); |
| 775 | } | 792 | } |
| 776 | 793 | ||
| 777 | static int nfs4_reclaim_locks(struct nfs4_state_recovery_ops *ops, struct nfs4_state *state) | 794 | static int nfs4_reclaim_locks(struct nfs4_state_recovery_ops *ops, struct nfs4_state *state) |
| @@ -887,18 +904,14 @@ static void nfs4_state_mark_reclaim(struct nfs4_client *clp) | |||
| 887 | 904 | ||
| 888 | static int reclaimer(void *ptr) | 905 | static int reclaimer(void *ptr) |
| 889 | { | 906 | { |
| 890 | struct reclaimer_args *args = (struct reclaimer_args *)ptr; | 907 | struct nfs4_client *clp = ptr; |
| 891 | struct nfs4_client *clp = args->clp; | ||
| 892 | struct nfs4_state_owner *sp; | 908 | struct nfs4_state_owner *sp; |
| 893 | struct nfs4_state_recovery_ops *ops; | 909 | struct nfs4_state_recovery_ops *ops; |
| 910 | struct rpc_cred *cred; | ||
| 894 | int status = 0; | 911 | int status = 0; |
| 895 | 912 | ||
| 896 | daemonize("%u.%u.%u.%u-reclaim", NIPQUAD(clp->cl_addr)); | ||
| 897 | allow_signal(SIGKILL); | 913 | allow_signal(SIGKILL); |
| 898 | 914 | ||
| 899 | atomic_inc(&clp->cl_count); | ||
| 900 | complete(&args->complete); | ||
| 901 | |||
| 902 | /* Ensure exclusive access to NFSv4 state */ | 915 | /* Ensure exclusive access to NFSv4 state */ |
| 903 | lock_kernel(); | 916 | lock_kernel(); |
| 904 | down_write(&clp->cl_sem); | 917 | down_write(&clp->cl_sem); |
| @@ -906,20 +919,33 @@ static int reclaimer(void *ptr) | |||
| 906 | if (list_empty(&clp->cl_superblocks)) | 919 | if (list_empty(&clp->cl_superblocks)) |
| 907 | goto out; | 920 | goto out; |
| 908 | restart_loop: | 921 | restart_loop: |
| 909 | status = nfs4_proc_renew(clp); | 922 | ops = &nfs4_network_partition_recovery_ops; |
| 910 | switch (status) { | 923 | /* Are there any open files on this volume? */ |
| 911 | case 0: | 924 | cred = nfs4_get_renew_cred(clp); |
| 912 | case -NFS4ERR_CB_PATH_DOWN: | 925 | if (cred != NULL) { |
| 913 | goto out; | 926 | /* Yes there are: try to renew the old lease */ |
| 914 | case -NFS4ERR_STALE_CLIENTID: | 927 | status = nfs4_proc_renew(clp, cred); |
| 915 | case -NFS4ERR_LEASE_MOVED: | 928 | switch (status) { |
| 916 | ops = &nfs4_reboot_recovery_ops; | 929 | case 0: |
| 917 | break; | 930 | case -NFS4ERR_CB_PATH_DOWN: |
| 918 | default: | 931 | put_rpccred(cred); |
| 919 | ops = &nfs4_network_partition_recovery_ops; | 932 | goto out; |
| 920 | }; | 933 | case -NFS4ERR_STALE_CLIENTID: |
| 934 | case -NFS4ERR_LEASE_MOVED: | ||
| 935 | ops = &nfs4_reboot_recovery_ops; | ||
| 936 | } | ||
| 937 | } else { | ||
| 938 | /* "reboot" to ensure we clear all state on the server */ | ||
| 939 | clp->cl_boot_time = CURRENT_TIME; | ||
| 940 | cred = nfs4_get_setclientid_cred(clp); | ||
| 941 | } | ||
| 942 | /* We're going to have to re-establish a clientid */ | ||
| 921 | nfs4_state_mark_reclaim(clp); | 943 | nfs4_state_mark_reclaim(clp); |
| 922 | status = __nfs4_init_client(clp); | 944 | status = -ENOENT; |
| 945 | if (cred != NULL) { | ||
| 946 | status = nfs4_init_client(clp, cred); | ||
| 947 | put_rpccred(cred); | ||
| 948 | } | ||
| 923 | if (status) | 949 | if (status) |
| 924 | goto out_error; | 950 | goto out_error; |
| 925 | /* Mark all delegations for reclaim */ | 951 | /* Mark all delegations for reclaim */ |
| @@ -940,14 +966,13 @@ restart_loop: | |||
| 940 | } | 966 | } |
| 941 | nfs_delegation_reap_unclaimed(clp); | 967 | nfs_delegation_reap_unclaimed(clp); |
| 942 | out: | 968 | out: |
| 943 | set_bit(NFS4CLNT_OK, &clp->cl_state); | ||
| 944 | up_write(&clp->cl_sem); | 969 | up_write(&clp->cl_sem); |
| 945 | unlock_kernel(); | 970 | unlock_kernel(); |
| 946 | wake_up_all(&clp->cl_waitq); | ||
| 947 | rpc_wake_up(&clp->cl_rpcwaitq); | ||
| 948 | if (status == -NFS4ERR_CB_PATH_DOWN) | 971 | if (status == -NFS4ERR_CB_PATH_DOWN) |
| 949 | nfs_handle_cb_pathdown(clp); | 972 | nfs_handle_cb_pathdown(clp); |
| 973 | nfs4_clear_recover_bit(clp); | ||
| 950 | nfs4_put_client(clp); | 974 | nfs4_put_client(clp); |
| 975 | module_put_and_exit(0); | ||
| 951 | return 0; | 976 | return 0; |
| 952 | out_error: | 977 | out_error: |
| 953 | printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u with error %d\n", | 978 | printk(KERN_WARNING "Error: state recovery failed on NFSv4 server %u.%u.%u.%u with error %d\n", |
