nfs40client.c 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /* SPDX-License-Identifier: GPL-2.0 */
  2. #include <linux/nfs_fs.h>
  3. #include "nfs4_fs.h"
  4. #include "nfs4session.h"
  5. #include "callback.h"
  6. #include "delegation.h"
  7. #include "internal.h"
  8. #include "netns.h"
  9. #include "nfs40.h"
  10. #define NFSDBG_FACILITY NFSDBG_CLIENT
  11. /*
  12. * SETCLIENTID just did a callback update with the callback ident in
  13. * "drop," but server trunking discovery claims "drop" and "keep" are
  14. * actually the same server. Swap the callback IDs so that "keep"
  15. * will continue to use the callback ident the server now knows about,
  16. * and so that "keep"'s original callback ident is destroyed when
  17. * "drop" is freed.
  18. */
  19. static void nfs4_swap_callback_idents(struct nfs_client *keep,
  20. struct nfs_client *drop)
  21. {
  22. struct nfs_net *nn = net_generic(keep->cl_net, nfs_net_id);
  23. unsigned int save = keep->cl_cb_ident;
  24. if (keep->cl_cb_ident == drop->cl_cb_ident)
  25. return;
  26. dprintk("%s: keeping callback ident %u and dropping ident %u\n",
  27. __func__, keep->cl_cb_ident, drop->cl_cb_ident);
  28. spin_lock(&nn->nfs_client_lock);
  29. idr_replace(&nn->cb_ident_idr, keep, drop->cl_cb_ident);
  30. keep->cl_cb_ident = drop->cl_cb_ident;
  31. idr_replace(&nn->cb_ident_idr, drop, save);
  32. drop->cl_cb_ident = save;
  33. spin_unlock(&nn->nfs_client_lock);
  34. }
  35. static bool nfs4_same_verifier(nfs4_verifier *v1, nfs4_verifier *v2)
  36. {
  37. return memcmp(v1->data, v2->data, sizeof(v1->data)) == 0;
  38. }
  39. void nfs40_shutdown_client(struct nfs_client *clp)
  40. {
  41. if (clp->cl_slot_tbl) {
  42. nfs4_shutdown_slot_table(clp->cl_slot_tbl);
  43. kfree(clp->cl_slot_tbl);
  44. }
  45. }
  46. /**
  47. * nfs40_init_client - nfs_client initialization tasks for NFSv4.0
  48. * @clp: nfs_client to initialize
  49. *
  50. * Returns zero on success, or a negative errno if some error occurred.
  51. */
  52. int nfs40_init_client(struct nfs_client *clp)
  53. {
  54. struct nfs4_slot_table *tbl;
  55. int ret;
  56. tbl = kzalloc_obj(*tbl, GFP_NOFS);
  57. if (tbl == NULL)
  58. return -ENOMEM;
  59. ret = nfs4_setup_slot_table(tbl, NFS4_MAX_SLOT_TABLE,
  60. "NFSv4.0 transport Slot table");
  61. if (ret) {
  62. nfs4_shutdown_slot_table(tbl);
  63. kfree(tbl);
  64. return ret;
  65. }
  66. clp->cl_slot_tbl = tbl;
  67. return 0;
  68. }
  69. /*
  70. * nfs40_handle_cb_pathdown - return all delegations after NFS4ERR_CB_PATH_DOWN
  71. * @clp: client to process
  72. *
  73. * Set the NFS4CLNT_LEASE_EXPIRED state in order to force a
  74. * resend of the SETCLIENTID and hence re-establish the
  75. * callback channel. Then return all existing delegations.
  76. */
  77. void nfs40_handle_cb_pathdown(struct nfs_client *clp)
  78. {
  79. set_bit(NFS4CLNT_LEASE_EXPIRED, &clp->cl_state);
  80. nfs_expire_all_delegations(clp);
  81. dprintk("%s: handling CB_PATHDOWN recovery for server %s\n", __func__,
  82. clp->cl_hostname);
  83. }
  84. void nfs4_schedule_path_down_recovery(struct nfs_client *clp)
  85. {
  86. nfs40_handle_cb_pathdown(clp);
  87. nfs4_schedule_state_manager(clp);
  88. }
  89. /**
  90. * nfs40_walk_client_list - Find server that recognizes a client ID
  91. *
  92. * @new: nfs_client with client ID to test
  93. * @result: OUT: found nfs_client, or new
  94. * @cred: credential to use for trunking test
  95. *
  96. * Returns zero, a negative errno, or a negative NFS4ERR status.
  97. * If zero is returned, an nfs_client pointer is planted in "result."
  98. *
  99. * NB: nfs40_walk_client_list() relies on the new nfs_client being
  100. * the last nfs_client on the list.
  101. */
  102. static int nfs40_walk_client_list(struct nfs_client *new,
  103. struct nfs_client **result,
  104. const struct cred *cred)
  105. {
  106. struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id);
  107. struct nfs_client *pos, *prev = NULL;
  108. struct nfs4_setclientid_res clid = {
  109. .clientid = new->cl_clientid,
  110. .confirm = new->cl_confirm,
  111. };
  112. int status = -NFS4ERR_STALE_CLIENTID;
  113. spin_lock(&nn->nfs_client_lock);
  114. list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) {
  115. if (pos == new)
  116. goto found;
  117. status = nfs4_match_client(pos, new, &prev, nn);
  118. if (status < 0)
  119. goto out_unlock;
  120. if (status != 0)
  121. continue;
  122. /*
  123. * We just sent a new SETCLIENTID, which should have
  124. * caused the server to return a new cl_confirm. So if
  125. * cl_confirm is the same, then this is a different
  126. * server that just returned the same cl_confirm by
  127. * coincidence:
  128. */
  129. if ((new != pos) && nfs4_same_verifier(&pos->cl_confirm,
  130. &new->cl_confirm))
  131. continue;
  132. /*
  133. * But if the cl_confirm's are different, then the only
  134. * way that a SETCLIENTID_CONFIRM to pos can succeed is
  135. * if new and pos point to the same server:
  136. */
  137. found:
  138. refcount_inc(&pos->cl_count);
  139. spin_unlock(&nn->nfs_client_lock);
  140. nfs_put_client(prev);
  141. prev = pos;
  142. status = nfs4_proc_setclientid_confirm(pos, &clid, cred);
  143. switch (status) {
  144. case -NFS4ERR_STALE_CLIENTID:
  145. break;
  146. case 0:
  147. nfs4_swap_callback_idents(pos, new);
  148. pos->cl_confirm = new->cl_confirm;
  149. nfs_mark_client_ready(pos, NFS_CS_READY);
  150. prev = NULL;
  151. *result = pos;
  152. goto out;
  153. case -ERESTARTSYS:
  154. case -ETIMEDOUT:
  155. /* The callback path may have been inadvertently
  156. * changed. Schedule recovery!
  157. */
  158. nfs4_schedule_path_down_recovery(pos);
  159. goto out;
  160. default:
  161. goto out;
  162. }
  163. spin_lock(&nn->nfs_client_lock);
  164. }
  165. out_unlock:
  166. spin_unlock(&nn->nfs_client_lock);
  167. /* No match found. The server lost our clientid */
  168. out:
  169. nfs_put_client(prev);
  170. return status;
  171. }
  172. /**
  173. * nfs40_discover_server_trunking - Detect server IP address trunking (mv0)
  174. *
  175. * @clp: nfs_client under test
  176. * @result: OUT: found nfs_client, or clp
  177. * @cred: credential to use for trunking test
  178. *
  179. * Returns zero, a negative errno, or a negative NFS4ERR status.
  180. * If zero is returned, an nfs_client pointer is planted in
  181. * "result".
  182. *
  183. * Note: The returned client may not yet be marked ready.
  184. */
  185. int nfs40_discover_server_trunking(struct nfs_client *clp,
  186. struct nfs_client **result,
  187. const struct cred *cred)
  188. {
  189. struct nfs4_setclientid_res clid = {
  190. .clientid = clp->cl_clientid,
  191. .confirm = clp->cl_confirm,
  192. };
  193. struct nfs_net *nn = net_generic(clp->cl_net, nfs_net_id);
  194. unsigned short port;
  195. int status;
  196. port = nn->nfs_callback_tcpport;
  197. if (clp->cl_addr.ss_family == AF_INET6)
  198. port = nn->nfs_callback_tcpport6;
  199. status = nfs4_proc_setclientid(clp, NFS4_CALLBACK, port, cred, &clid);
  200. if (status != 0)
  201. goto out;
  202. clp->cl_clientid = clid.clientid;
  203. clp->cl_confirm = clid.confirm;
  204. status = nfs40_walk_client_list(clp, result, cred);
  205. if (status == 0) {
  206. /* Sustain the lease, even if it's empty. If the clientid4
  207. * goes stale it's of no use for trunking discovery. */
  208. nfs4_schedule_state_renewal(*result);
  209. /* If the client state need to recover, do it. */
  210. if (clp->cl_state)
  211. nfs4_schedule_state_manager(clp);
  212. }
  213. out:
  214. return status;
  215. }