diff options
Diffstat (limited to 'fs/nfs/nfs4state.c')
-rw-r--r-- | fs/nfs/nfs4state.c | 228 |
1 files changed, 211 insertions, 17 deletions
diff --git a/fs/nfs/nfs4state.c b/fs/nfs/nfs4state.c index 55148def5540..c351e6b39838 100644 --- a/fs/nfs/nfs4state.c +++ b/fs/nfs/nfs4state.c | |||
@@ -51,18 +51,21 @@ | |||
51 | #include <linux/bitops.h> | 51 | #include <linux/bitops.h> |
52 | #include <linux/jiffies.h> | 52 | #include <linux/jiffies.h> |
53 | 53 | ||
54 | #include <linux/sunrpc/clnt.h> | ||
55 | |||
54 | #include "nfs4_fs.h" | 56 | #include "nfs4_fs.h" |
55 | #include "callback.h" | 57 | #include "callback.h" |
56 | #include "delegation.h" | 58 | #include "delegation.h" |
57 | #include "internal.h" | 59 | #include "internal.h" |
58 | #include "pnfs.h" | 60 | #include "pnfs.h" |
61 | #include "netns.h" | ||
59 | 62 | ||
60 | #define NFSDBG_FACILITY NFSDBG_STATE | 63 | #define NFSDBG_FACILITY NFSDBG_STATE |
61 | 64 | ||
62 | #define OPENOWNER_POOL_SIZE 8 | 65 | #define OPENOWNER_POOL_SIZE 8 |
63 | 66 | ||
64 | const nfs4_stateid zero_stateid; | 67 | const nfs4_stateid zero_stateid; |
65 | 68 | static DEFINE_MUTEX(nfs_clid_init_mutex); | |
66 | static LIST_HEAD(nfs4_clientid_list); | 69 | static LIST_HEAD(nfs4_clientid_list); |
67 | 70 | ||
68 | int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred) | 71 | int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred) |
@@ -73,12 +76,13 @@ int nfs4_init_clientid(struct nfs_client *clp, struct rpc_cred *cred) | |||
73 | }; | 76 | }; |
74 | unsigned short port; | 77 | unsigned short port; |
75 | int status; | 78 | int status; |
79 | struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); | ||
76 | 80 | ||
77 | if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state)) | 81 | if (test_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state)) |
78 | goto do_confirm; | 82 | goto do_confirm; |
79 | port = nfs_callback_tcpport; | 83 | port = nn->nfs_callback_tcpport; |
80 | if (clp->cl_addr.ss_family == AF_INET6) | 84 | if (clp->cl_addr.ss_family == AF_INET6) |
81 | port = nfs_callback_tcpport6; | 85 | port = nn->nfs_callback_tcpport6; |
82 | 86 | ||
83 | status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid); | 87 | status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid); |
84 | if (status != 0) | 88 | if (status != 0) |
@@ -96,6 +100,56 @@ out: | |||
96 | return status; | 100 | return status; |
97 | } | 101 | } |
98 | 102 | ||
103 | /** | ||
104 | * nfs40_discover_server_trunking - Detect server IP address trunking (mv0) | ||
105 | * | ||
106 | * @clp: nfs_client under test | ||
107 | * @result: OUT: found nfs_client, or clp | ||
108 | * @cred: credential to use for trunking test | ||
109 | * | ||
110 | * Returns zero, a negative errno, or a negative NFS4ERR status. | ||
111 | * If zero is returned, an nfs_client pointer is planted in | ||
112 | * "result". | ||
113 | * | ||
114 | * Note: The returned client may not yet be marked ready. | ||
115 | */ | ||
116 | int nfs40_discover_server_trunking(struct nfs_client *clp, | ||
117 | struct nfs_client **result, | ||
118 | struct rpc_cred *cred) | ||
119 | { | ||
120 | struct nfs4_setclientid_res clid = { | ||
121 | .clientid = clp->cl_clientid, | ||
122 | .confirm = clp->cl_confirm, | ||
123 | }; | ||
124 | struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id); | ||
125 | unsigned short port; | ||
126 | int status; | ||
127 | |||
128 | port = nn->nfs_callback_tcpport; | ||
129 | if (clp->cl_addr.ss_family == AF_INET6) | ||
130 | port = nn->nfs_callback_tcpport6; | ||
131 | |||
132 | status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid); | ||
133 | if (status != 0) | ||
134 | goto out; | ||
135 | clp->cl_clientid = clid.clientid; | ||
136 | clp->cl_confirm = clid.confirm; | ||
137 | |||
138 | status = nfs40_walk_client_list(clp, result, cred); | ||
139 | switch (status) { | ||
140 | case -NFS4ERR_STALE_CLIENTID: | ||
141 | set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); | ||
142 | case 0: | ||
143 | /* Sustain the lease, even if it's empty. If the clientid4 | ||
144 | * goes stale it's of no use for trunking discovery. */ | ||
145 | nfs4_schedule_state_renewal(*result); | ||
146 | break; | ||
147 | } | ||
148 | |||
149 | out: | ||
150 | return status; | ||
151 | } | ||
152 | |||
99 | struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp) | 153 | struct rpc_cred *nfs4_get_machine_cred_locked(struct nfs_client *clp) |
100 | { | 154 | { |
101 | struct rpc_cred *cred = NULL; | 155 | struct rpc_cred *cred = NULL; |
@@ -275,6 +329,33 @@ out: | |||
275 | return status; | 329 | return status; |
276 | } | 330 | } |
277 | 331 | ||
332 | /** | ||
333 | * nfs41_discover_server_trunking - Detect server IP address trunking (mv1) | ||
334 | * | ||
335 | * @clp: nfs_client under test | ||
336 | * @result: OUT: found nfs_client, or clp | ||
337 | * @cred: credential to use for trunking test | ||
338 | * | ||
339 | * Returns NFS4_OK, a negative errno, or a negative NFS4ERR status. | ||
340 | * If NFS4_OK is returned, an nfs_client pointer is planted in | ||
341 | * "result". | ||
342 | * | ||
343 | * Note: The returned client may not yet be marked ready. | ||
344 | */ | ||
345 | int nfs41_discover_server_trunking(struct nfs_client *clp, | ||
346 | struct nfs_client **result, | ||
347 | struct rpc_cred *cred) | ||
348 | { | ||
349 | int status; | ||
350 | |||
351 | status = nfs4_proc_exchange_id(clp, cred); | ||
352 | if (status != NFS4_OK) | ||
353 | return status; | ||
354 | set_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); | ||
355 | |||
356 | return nfs41_walk_client_list(clp, result, cred); | ||
357 | } | ||
358 | |||
278 | struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp) | 359 | struct rpc_cred *nfs4_get_exchange_id_cred(struct nfs_client *clp) |
279 | { | 360 | { |
280 | struct rpc_cred *cred; | 361 | struct rpc_cred *cred; |
@@ -729,11 +810,8 @@ static void __nfs4_close(struct nfs4_state *state, | |||
729 | if (!call_close) { | 810 | if (!call_close) { |
730 | nfs4_put_open_state(state); | 811 | nfs4_put_open_state(state); |
731 | nfs4_put_state_owner(owner); | 812 | nfs4_put_state_owner(owner); |
732 | } else { | 813 | } else |
733 | bool roc = pnfs_roc(state->inode); | 814 | nfs4_do_close(state, gfp_mask, wait); |
734 | |||
735 | nfs4_do_close(state, gfp_mask, wait, roc); | ||
736 | } | ||
737 | } | 815 | } |
738 | 816 | ||
739 | void nfs4_close_state(struct nfs4_state *state, fmode_t fmode) | 817 | void nfs4_close_state(struct nfs4_state *state, fmode_t fmode) |
@@ -865,7 +943,7 @@ void nfs4_put_lock_state(struct nfs4_lock_state *lsp) | |||
865 | if (list_empty(&state->lock_states)) | 943 | if (list_empty(&state->lock_states)) |
866 | clear_bit(LK_STATE_IN_USE, &state->flags); | 944 | clear_bit(LK_STATE_IN_USE, &state->flags); |
867 | spin_unlock(&state->state_lock); | 945 | spin_unlock(&state->state_lock); |
868 | if (lsp->ls_flags & NFS_LOCK_INITIALIZED) { | 946 | if (test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags)) { |
869 | if (nfs4_release_lockowner(lsp) == 0) | 947 | if (nfs4_release_lockowner(lsp) == 0) |
870 | return; | 948 | return; |
871 | } | 949 | } |
@@ -911,17 +989,25 @@ int nfs4_set_lock_state(struct nfs4_state *state, struct file_lock *fl) | |||
911 | } | 989 | } |
912 | 990 | ||
913 | static bool nfs4_copy_lock_stateid(nfs4_stateid *dst, struct nfs4_state *state, | 991 | static bool nfs4_copy_lock_stateid(nfs4_stateid *dst, struct nfs4_state *state, |
914 | fl_owner_t fl_owner, pid_t fl_pid) | 992 | const struct nfs_lockowner *lockowner) |
915 | { | 993 | { |
916 | struct nfs4_lock_state *lsp; | 994 | struct nfs4_lock_state *lsp; |
995 | fl_owner_t fl_owner; | ||
996 | pid_t fl_pid; | ||
917 | bool ret = false; | 997 | bool ret = false; |
918 | 998 | ||
999 | |||
1000 | if (lockowner == NULL) | ||
1001 | goto out; | ||
1002 | |||
919 | if (test_bit(LK_STATE_IN_USE, &state->flags) == 0) | 1003 | if (test_bit(LK_STATE_IN_USE, &state->flags) == 0) |
920 | goto out; | 1004 | goto out; |
921 | 1005 | ||
1006 | fl_owner = lockowner->l_owner; | ||
1007 | fl_pid = lockowner->l_pid; | ||
922 | spin_lock(&state->state_lock); | 1008 | spin_lock(&state->state_lock); |
923 | lsp = __nfs4_find_lock_state(state, fl_owner, fl_pid, NFS4_ANY_LOCK_TYPE); | 1009 | lsp = __nfs4_find_lock_state(state, fl_owner, fl_pid, NFS4_ANY_LOCK_TYPE); |
924 | if (lsp != NULL && (lsp->ls_flags & NFS_LOCK_INITIALIZED) != 0) { | 1010 | if (lsp != NULL && test_bit(NFS_LOCK_INITIALIZED, &lsp->ls_flags) != 0) { |
925 | nfs4_stateid_copy(dst, &lsp->ls_stateid); | 1011 | nfs4_stateid_copy(dst, &lsp->ls_stateid); |
926 | ret = true; | 1012 | ret = true; |
927 | } | 1013 | } |
@@ -946,11 +1032,11 @@ static void nfs4_copy_open_stateid(nfs4_stateid *dst, struct nfs4_state *state) | |||
946 | * requests. | 1032 | * requests. |
947 | */ | 1033 | */ |
948 | void nfs4_select_rw_stateid(nfs4_stateid *dst, struct nfs4_state *state, | 1034 | void nfs4_select_rw_stateid(nfs4_stateid *dst, struct nfs4_state *state, |
949 | fmode_t fmode, fl_owner_t fl_owner, pid_t fl_pid) | 1035 | fmode_t fmode, const struct nfs_lockowner *lockowner) |
950 | { | 1036 | { |
951 | if (nfs4_copy_delegation_stateid(dst, state->inode, fmode)) | 1037 | if (nfs4_copy_delegation_stateid(dst, state->inode, fmode)) |
952 | return; | 1038 | return; |
953 | if (nfs4_copy_lock_stateid(dst, state, fl_owner, fl_pid)) | 1039 | if (nfs4_copy_lock_stateid(dst, state, lockowner)) |
954 | return; | 1040 | return; |
955 | nfs4_copy_open_stateid(dst, state); | 1041 | nfs4_copy_open_stateid(dst, state); |
956 | } | 1042 | } |
@@ -1289,7 +1375,7 @@ restart: | |||
1289 | if (status >= 0) { | 1375 | if (status >= 0) { |
1290 | spin_lock(&state->state_lock); | 1376 | spin_lock(&state->state_lock); |
1291 | list_for_each_entry(lock, &state->lock_states, ls_locks) { | 1377 | list_for_each_entry(lock, &state->lock_states, ls_locks) { |
1292 | if (!(lock->ls_flags & NFS_LOCK_INITIALIZED)) | 1378 | if (!test_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags)) |
1293 | pr_warn_ratelimited("NFS: " | 1379 | pr_warn_ratelimited("NFS: " |
1294 | "%s: Lock reclaim " | 1380 | "%s: Lock reclaim " |
1295 | "failed!\n", __func__); | 1381 | "failed!\n", __func__); |
@@ -1361,7 +1447,7 @@ static void nfs4_clear_open_state(struct nfs4_state *state) | |||
1361 | spin_lock(&state->state_lock); | 1447 | spin_lock(&state->state_lock); |
1362 | list_for_each_entry(lock, &state->lock_states, ls_locks) { | 1448 | list_for_each_entry(lock, &state->lock_states, ls_locks) { |
1363 | lock->ls_seqid.flags = 0; | 1449 | lock->ls_seqid.flags = 0; |
1364 | lock->ls_flags &= ~NFS_LOCK_INITIALIZED; | 1450 | clear_bit(NFS_LOCK_INITIALIZED, &lock->ls_flags); |
1365 | } | 1451 | } |
1366 | spin_unlock(&state->state_lock); | 1452 | spin_unlock(&state->state_lock); |
1367 | } | 1453 | } |
@@ -1595,8 +1681,8 @@ out: | |||
1595 | return nfs4_recovery_handle_error(clp, status); | 1681 | return nfs4_recovery_handle_error(clp, status); |
1596 | } | 1682 | } |
1597 | 1683 | ||
1598 | /* Set NFS4CLNT_LEASE_EXPIRED for all v4.0 errors and for recoverable errors | 1684 | /* Set NFS4CLNT_LEASE_EXPIRED and reclaim reboot state for all v4.0 errors |
1599 | * on EXCHANGE_ID for v4.1 | 1685 | * and for recoverable errors on EXCHANGE_ID for v4.1 |
1600 | */ | 1686 | */ |
1601 | static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status) | 1687 | static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status) |
1602 | { | 1688 | { |
@@ -1606,8 +1692,12 @@ static int nfs4_handle_reclaim_lease_error(struct nfs_client *clp, int status) | |||
1606 | return -ESERVERFAULT; | 1692 | return -ESERVERFAULT; |
1607 | /* Lease confirmation error: retry after purging the lease */ | 1693 | /* Lease confirmation error: retry after purging the lease */ |
1608 | ssleep(1); | 1694 | ssleep(1); |
1695 | clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); | ||
1696 | break; | ||
1609 | case -NFS4ERR_STALE_CLIENTID: | 1697 | case -NFS4ERR_STALE_CLIENTID: |
1610 | clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); | 1698 | clear_bit(NFS4CLNT_LEASE_CONFIRM, &clp->cl_state); |
1699 | nfs4_state_clear_reclaim_reboot(clp); | ||
1700 | nfs4_state_start_reclaim_reboot(clp); | ||
1611 | break; | 1701 | break; |
1612 | case -NFS4ERR_CLID_INUSE: | 1702 | case -NFS4ERR_CLID_INUSE: |
1613 | pr_err("NFS: Server %s reports our clientid is in use\n", | 1703 | pr_err("NFS: Server %s reports our clientid is in use\n", |
@@ -1698,6 +1788,109 @@ static int nfs4_purge_lease(struct nfs_client *clp) | |||
1698 | return 0; | 1788 | return 0; |
1699 | } | 1789 | } |
1700 | 1790 | ||
1791 | /** | ||
1792 | * nfs4_discover_server_trunking - Detect server IP address trunking | ||
1793 | * | ||
1794 | * @clp: nfs_client under test | ||
1795 | * @result: OUT: found nfs_client, or clp | ||
1796 | * | ||
1797 | * Returns zero or a negative errno. If zero is returned, | ||
1798 | * an nfs_client pointer is planted in "result". | ||
1799 | * | ||
1800 | * Note: since we are invoked in process context, and | ||
1801 | * not from inside the state manager, we cannot use | ||
1802 | * nfs4_handle_reclaim_lease_error(). | ||
1803 | */ | ||
1804 | int nfs4_discover_server_trunking(struct nfs_client *clp, | ||
1805 | struct nfs_client **result) | ||
1806 | { | ||
1807 | const struct nfs4_state_recovery_ops *ops = | ||
1808 | clp->cl_mvops->reboot_recovery_ops; | ||
1809 | rpc_authflavor_t *flavors, flav, save; | ||
1810 | struct rpc_clnt *clnt; | ||
1811 | struct rpc_cred *cred; | ||
1812 | int i, len, status; | ||
1813 | |||
1814 | dprintk("NFS: %s: testing '%s'\n", __func__, clp->cl_hostname); | ||
1815 | |||
1816 | len = NFS_MAX_SECFLAVORS; | ||
1817 | flavors = kcalloc(len, sizeof(*flavors), GFP_KERNEL); | ||
1818 | if (flavors == NULL) { | ||
1819 | status = -ENOMEM; | ||
1820 | goto out; | ||
1821 | } | ||
1822 | len = rpcauth_list_flavors(flavors, len); | ||
1823 | if (len < 0) { | ||
1824 | status = len; | ||
1825 | goto out_free; | ||
1826 | } | ||
1827 | clnt = clp->cl_rpcclient; | ||
1828 | save = clnt->cl_auth->au_flavor; | ||
1829 | i = 0; | ||
1830 | |||
1831 | mutex_lock(&nfs_clid_init_mutex); | ||
1832 | status = -ENOENT; | ||
1833 | again: | ||
1834 | cred = ops->get_clid_cred(clp); | ||
1835 | if (cred == NULL) | ||
1836 | goto out_unlock; | ||
1837 | |||
1838 | status = ops->detect_trunking(clp, result, cred); | ||
1839 | put_rpccred(cred); | ||
1840 | switch (status) { | ||
1841 | case 0: | ||
1842 | break; | ||
1843 | |||
1844 | case -EACCES: | ||
1845 | if (clp->cl_machine_cred == NULL) | ||
1846 | break; | ||
1847 | /* Handle case where the user hasn't set up machine creds */ | ||
1848 | nfs4_clear_machine_cred(clp); | ||
1849 | case -NFS4ERR_DELAY: | ||
1850 | case -ETIMEDOUT: | ||
1851 | case -EAGAIN: | ||
1852 | ssleep(1); | ||
1853 | dprintk("NFS: %s after status %d, retrying\n", | ||
1854 | __func__, status); | ||
1855 | goto again; | ||
1856 | |||
1857 | case -NFS4ERR_CLID_INUSE: | ||
1858 | case -NFS4ERR_WRONGSEC: | ||
1859 | status = -EPERM; | ||
1860 | if (i >= len) | ||
1861 | break; | ||
1862 | |||
1863 | flav = flavors[i++]; | ||
1864 | if (flav == save) | ||
1865 | flav = flavors[i++]; | ||
1866 | clnt = rpc_clone_client_set_auth(clnt, flav); | ||
1867 | if (IS_ERR(clnt)) { | ||
1868 | status = PTR_ERR(clnt); | ||
1869 | break; | ||
1870 | } | ||
1871 | clp->cl_rpcclient = clnt; | ||
1872 | goto again; | ||
1873 | |||
1874 | case -NFS4ERR_MINOR_VERS_MISMATCH: | ||
1875 | status = -EPROTONOSUPPORT; | ||
1876 | break; | ||
1877 | |||
1878 | case -EKEYEXPIRED: | ||
1879 | nfs4_warn_keyexpired(clp->cl_hostname); | ||
1880 | case -NFS4ERR_NOT_SAME: /* FixMe: implement recovery | ||
1881 | * in nfs4_exchange_id */ | ||
1882 | status = -EKEYEXPIRED; | ||
1883 | } | ||
1884 | |||
1885 | out_unlock: | ||
1886 | mutex_unlock(&nfs_clid_init_mutex); | ||
1887 | out_free: | ||
1888 | kfree(flavors); | ||
1889 | out: | ||
1890 | dprintk("NFS: %s: status = %d\n", __func__, status); | ||
1891 | return status; | ||
1892 | } | ||
1893 | |||
1701 | #ifdef CONFIG_NFS_V4_1 | 1894 | #ifdef CONFIG_NFS_V4_1 |
1702 | void nfs4_schedule_session_recovery(struct nfs4_session *session, int err) | 1895 | void nfs4_schedule_session_recovery(struct nfs4_session *session, int err) |
1703 | { | 1896 | { |
@@ -2008,6 +2201,7 @@ out_error: | |||
2008 | pr_warn_ratelimited("NFS: state manager%s%s failed on NFSv4 server %s" | 2201 | pr_warn_ratelimited("NFS: state manager%s%s failed on NFSv4 server %s" |
2009 | " with error %d\n", section_sep, section, | 2202 | " with error %d\n", section_sep, section, |
2010 | clp->cl_hostname, -status); | 2203 | clp->cl_hostname, -status); |
2204 | ssleep(1); | ||
2011 | nfs4_end_drain_session(clp); | 2205 | nfs4_end_drain_session(clp); |
2012 | nfs4_clear_state_manager_bit(clp); | 2206 | nfs4_clear_state_manager_bit(clp); |
2013 | } | 2207 | } |