| 1 | // SPDX-License-Identifier: GPL-2.0-only | 
|---|
| 2 | /* | 
|---|
| 3 | * Copyright (C) 2006 Red Hat, Inc. All Rights Reserved. | 
|---|
| 4 | * Written by David Howells (dhowells@redhat.com) | 
|---|
| 5 | */ | 
|---|
| 6 | #include <linux/module.h> | 
|---|
| 7 | #include <linux/nfs_fs.h> | 
|---|
| 8 | #include <linux/nfs_mount.h> | 
|---|
| 9 | #include <linux/sunrpc/addr.h> | 
|---|
| 10 | #include <linux/sunrpc/auth.h> | 
|---|
| 11 | #include <linux/sunrpc/xprt.h> | 
|---|
| 12 | #include <linux/sunrpc/bc_xprt.h> | 
|---|
| 13 | #include <linux/sunrpc/rpc_pipe_fs.h> | 
|---|
| 14 | #include "internal.h" | 
|---|
| 15 | #include "callback.h" | 
|---|
| 16 | #include "delegation.h" | 
|---|
| 17 | #include "nfs4session.h" | 
|---|
| 18 | #include "nfs4idmap.h" | 
|---|
| 19 | #include "pnfs.h" | 
|---|
| 20 | #include "netns.h" | 
|---|
| 21 | #include "sysfs.h" | 
|---|
| 22 |  | 
|---|
| 23 | #define NFSDBG_FACILITY		NFSDBG_CLIENT | 
|---|
| 24 |  | 
|---|
| 25 | /* | 
|---|
| 26 | * Get a unique NFSv4.0 callback identifier which will be used | 
|---|
| 27 | * by the V4.0 callback service to lookup the nfs_client struct | 
|---|
| 28 | */ | 
|---|
| 29 | static int nfs_get_cb_ident_idr(struct nfs_client *clp, int minorversion) | 
|---|
| 30 | { | 
|---|
| 31 | int ret = 0; | 
|---|
| 32 | struct nfs_net *nn = net_generic(net: clp->cl_net, id: nfs_net_id); | 
|---|
| 33 |  | 
|---|
| 34 | if (clp->rpc_ops->version != 4 || minorversion != 0) | 
|---|
| 35 | return ret; | 
|---|
| 36 | idr_preload(GFP_KERNEL); | 
|---|
| 37 | spin_lock(lock: &nn->nfs_client_lock); | 
|---|
| 38 | ret = idr_alloc(&nn->cb_ident_idr, ptr: clp, start: 1, end: 0, GFP_NOWAIT); | 
|---|
| 39 | if (ret >= 0) | 
|---|
| 40 | clp->cl_cb_ident = ret; | 
|---|
| 41 | spin_unlock(lock: &nn->nfs_client_lock); | 
|---|
| 42 | idr_preload_end(); | 
|---|
| 43 | return ret < 0 ? ret : 0; | 
|---|
| 44 | } | 
|---|
| 45 |  | 
|---|
| 46 | #ifdef CONFIG_NFS_V4_1 | 
|---|
| 47 | /* | 
|---|
| 48 | * Per auth flavor data server rpc clients | 
|---|
| 49 | */ | 
|---|
| 50 | struct nfs4_ds_server { | 
|---|
| 51 | struct list_head	list;   /* ds_clp->cl_ds_clients */ | 
|---|
| 52 | struct rpc_clnt		*rpc_clnt; | 
|---|
| 53 | }; | 
|---|
| 54 |  | 
|---|
| 55 | /** | 
|---|
| 56 | * nfs4_find_ds_client - Common lookup case for DS I/O | 
|---|
| 57 | * @ds_clp: pointer to the DS's nfs_client | 
|---|
| 58 | * @flavor: rpc auth flavour to match | 
|---|
| 59 | */ | 
|---|
| 60 | static struct nfs4_ds_server * | 
|---|
| 61 | nfs4_find_ds_client(struct nfs_client *ds_clp, rpc_authflavor_t flavor) | 
|---|
| 62 | { | 
|---|
| 63 | struct nfs4_ds_server *dss; | 
|---|
| 64 |  | 
|---|
| 65 | rcu_read_lock(); | 
|---|
| 66 | list_for_each_entry_rcu(dss, &ds_clp->cl_ds_clients, list) { | 
|---|
| 67 | if (dss->rpc_clnt->cl_auth->au_flavor != flavor) | 
|---|
| 68 | continue; | 
|---|
| 69 | goto out; | 
|---|
| 70 | } | 
|---|
| 71 | dss = NULL; | 
|---|
| 72 | out: | 
|---|
| 73 | rcu_read_unlock(); | 
|---|
| 74 | return dss; | 
|---|
| 75 | } | 
|---|
| 76 |  | 
|---|
| 77 | static struct nfs4_ds_server * | 
|---|
| 78 | nfs4_add_ds_client(struct nfs_client *ds_clp, rpc_authflavor_t flavor, | 
|---|
| 79 | struct nfs4_ds_server *new) | 
|---|
| 80 | { | 
|---|
| 81 | struct nfs4_ds_server *dss; | 
|---|
| 82 |  | 
|---|
| 83 | spin_lock(&ds_clp->cl_lock); | 
|---|
| 84 | list_for_each_entry(dss, &ds_clp->cl_ds_clients, list) { | 
|---|
| 85 | if (dss->rpc_clnt->cl_auth->au_flavor != flavor) | 
|---|
| 86 | continue; | 
|---|
| 87 | goto out; | 
|---|
| 88 | } | 
|---|
| 89 | if (new) | 
|---|
| 90 | list_add_rcu(&new->list, &ds_clp->cl_ds_clients); | 
|---|
| 91 | dss = new; | 
|---|
| 92 | out: | 
|---|
| 93 | spin_unlock(&ds_clp->cl_lock); /* need some lock to protect list */ | 
|---|
| 94 | return dss; | 
|---|
| 95 | } | 
|---|
| 96 |  | 
|---|
| 97 | static struct nfs4_ds_server * | 
|---|
| 98 | nfs4_alloc_ds_server(struct nfs_client *ds_clp, rpc_authflavor_t flavor) | 
|---|
| 99 | { | 
|---|
| 100 | struct nfs4_ds_server *dss; | 
|---|
| 101 |  | 
|---|
| 102 | dss = kmalloc(sizeof(*dss), GFP_NOFS); | 
|---|
| 103 | if (dss == NULL) | 
|---|
| 104 | return ERR_PTR(-ENOMEM); | 
|---|
| 105 |  | 
|---|
| 106 | dss->rpc_clnt = rpc_clone_client_set_auth(ds_clp->cl_rpcclient, flavor); | 
|---|
| 107 | if (IS_ERR(dss->rpc_clnt)) { | 
|---|
| 108 | int err = PTR_ERR(dss->rpc_clnt); | 
|---|
| 109 | kfree (dss); | 
|---|
| 110 | return ERR_PTR(err); | 
|---|
| 111 | } | 
|---|
| 112 | INIT_LIST_HEAD(&dss->list); | 
|---|
| 113 |  | 
|---|
| 114 | return dss; | 
|---|
| 115 | } | 
|---|
| 116 |  | 
|---|
| 117 | static void | 
|---|
| 118 | nfs4_free_ds_server(struct nfs4_ds_server *dss) | 
|---|
| 119 | { | 
|---|
| 120 | rpc_release_client(dss->rpc_clnt); | 
|---|
| 121 | kfree(dss); | 
|---|
| 122 | } | 
|---|
| 123 |  | 
|---|
| 124 | /** | 
|---|
| 125 | * nfs4_find_or_create_ds_client - Find or create a DS rpc client | 
|---|
| 126 | * @ds_clp: pointer to the DS's nfs_client | 
|---|
| 127 | * @inode: pointer to the inode | 
|---|
| 128 | * | 
|---|
| 129 | * Find or create a DS rpc client with th MDS server rpc client auth flavor | 
|---|
| 130 | * in the nfs_client cl_ds_clients list. | 
|---|
| 131 | */ | 
|---|
| 132 | struct rpc_clnt * | 
|---|
| 133 | nfs4_find_or_create_ds_client(struct nfs_client *ds_clp, struct inode *inode) | 
|---|
| 134 | { | 
|---|
| 135 | struct nfs4_ds_server *dss, *new; | 
|---|
| 136 | rpc_authflavor_t flavor = NFS_SERVER(inode)->client->cl_auth->au_flavor; | 
|---|
| 137 |  | 
|---|
| 138 | dss = nfs4_find_ds_client(ds_clp, flavor); | 
|---|
| 139 | if (dss != NULL) | 
|---|
| 140 | goto out; | 
|---|
| 141 | new = nfs4_alloc_ds_server(ds_clp, flavor); | 
|---|
| 142 | if (IS_ERR(new)) | 
|---|
| 143 | return ERR_CAST(new); | 
|---|
| 144 | dss = nfs4_add_ds_client(ds_clp, flavor, new); | 
|---|
| 145 | if (dss != new) | 
|---|
| 146 | nfs4_free_ds_server(new); | 
|---|
| 147 | out: | 
|---|
| 148 | return dss->rpc_clnt; | 
|---|
| 149 | } | 
|---|
| 150 | EXPORT_SYMBOL_GPL(nfs4_find_or_create_ds_client); | 
|---|
| 151 |  | 
|---|
| 152 | static void | 
|---|
| 153 | nfs4_shutdown_ds_clients(struct nfs_client *clp) | 
|---|
| 154 | { | 
|---|
| 155 | struct nfs4_ds_server *dss; | 
|---|
| 156 |  | 
|---|
| 157 | while (!list_empty(&clp->cl_ds_clients)) { | 
|---|
| 158 | dss = list_entry(clp->cl_ds_clients.next, | 
|---|
| 159 | struct nfs4_ds_server, list); | 
|---|
| 160 | list_del(&dss->list); | 
|---|
| 161 | rpc_shutdown_client(dss->rpc_clnt); | 
|---|
| 162 | kfree (dss); | 
|---|
| 163 | } | 
|---|
| 164 | } | 
|---|
| 165 |  | 
|---|
| 166 | static void | 
|---|
| 167 | nfs4_cleanup_callback(struct nfs_client *clp) | 
|---|
| 168 | { | 
|---|
| 169 | struct nfs4_copy_state *cp_state; | 
|---|
| 170 |  | 
|---|
| 171 | while (!list_empty(&clp->pending_cb_stateids)) { | 
|---|
| 172 | cp_state = list_entry(clp->pending_cb_stateids.next, | 
|---|
| 173 | struct nfs4_copy_state, copies); | 
|---|
| 174 | list_del(&cp_state->copies); | 
|---|
| 175 | kfree(cp_state); | 
|---|
| 176 | } | 
|---|
| 177 | } | 
|---|
| 178 |  | 
|---|
| 179 | void nfs41_shutdown_client(struct nfs_client *clp) | 
|---|
| 180 | { | 
|---|
| 181 | if (nfs4_has_session(clp)) { | 
|---|
| 182 | nfs4_cleanup_callback(clp); | 
|---|
| 183 | nfs4_shutdown_ds_clients(clp); | 
|---|
| 184 | nfs4_destroy_session(clp->cl_session); | 
|---|
| 185 | nfs4_destroy_clientid(clp); | 
|---|
| 186 | } | 
|---|
| 187 |  | 
|---|
| 188 | } | 
|---|
| 189 | #endif	/* CONFIG_NFS_V4_1 */ | 
|---|
| 190 |  | 
|---|
| 191 | void nfs40_shutdown_client(struct nfs_client *clp) | 
|---|
| 192 | { | 
|---|
| 193 | if (clp->cl_slot_tbl) { | 
|---|
| 194 | nfs4_shutdown_slot_table(tbl: clp->cl_slot_tbl); | 
|---|
| 195 | kfree(objp: clp->cl_slot_tbl); | 
|---|
| 196 | } | 
|---|
| 197 | } | 
|---|
| 198 |  | 
|---|
| 199 | struct nfs_client *nfs4_alloc_client(const struct nfs_client_initdata *cl_init) | 
|---|
| 200 | { | 
|---|
| 201 | char buf[INET6_ADDRSTRLEN + 1]; | 
|---|
| 202 | const char *ip_addr = cl_init->ip_addr; | 
|---|
| 203 | struct nfs_client *clp = nfs_alloc_client(cl_init); | 
|---|
| 204 | int err; | 
|---|
| 205 |  | 
|---|
| 206 | if (IS_ERR(ptr: clp)) | 
|---|
| 207 | return clp; | 
|---|
| 208 |  | 
|---|
| 209 | err = nfs_get_cb_ident_idr(clp, minorversion: cl_init->minorversion); | 
|---|
| 210 | if (err) | 
|---|
| 211 | goto error; | 
|---|
| 212 |  | 
|---|
| 213 | if (cl_init->minorversion > NFS4_MAX_MINOR_VERSION) { | 
|---|
| 214 | err = -EINVAL; | 
|---|
| 215 | goto error; | 
|---|
| 216 | } | 
|---|
| 217 |  | 
|---|
| 218 | spin_lock_init(&clp->cl_lock); | 
|---|
| 219 | INIT_DELAYED_WORK(&clp->cl_renewd, nfs4_renew_state); | 
|---|
| 220 | INIT_LIST_HEAD(list: &clp->cl_ds_clients); | 
|---|
| 221 | rpc_init_wait_queue(&clp->cl_rpcwaitq, "NFS client"); | 
|---|
| 222 | clp->cl_state = 1 << NFS4CLNT_LEASE_EXPIRED; | 
|---|
| 223 | clp->cl_mvops = nfs_v4_minor_ops[cl_init->minorversion]; | 
|---|
| 224 | clp->cl_mig_gen = 1; | 
|---|
| 225 | #if IS_ENABLED(CONFIG_NFS_V4_1) | 
|---|
| 226 | init_waitqueue_head(&clp->cl_lock_waitq); | 
|---|
| 227 | #endif | 
|---|
| 228 | INIT_LIST_HEAD(list: &clp->pending_cb_stateids); | 
|---|
| 229 |  | 
|---|
| 230 | if (cl_init->minorversion != 0) | 
|---|
| 231 | __set_bit(NFS_CS_INFINITE_SLOTS, &clp->cl_flags); | 
|---|
| 232 | __set_bit(NFS_CS_DISCRTRY, &clp->cl_flags); | 
|---|
| 233 | __set_bit(NFS_CS_NO_RETRANS_TIMEOUT, &clp->cl_flags); | 
|---|
| 234 | if (test_bit(NFS_CS_PNFS, &cl_init->init_flags)) | 
|---|
| 235 | __set_bit(NFS_CS_PNFS, &clp->cl_flags); | 
|---|
| 236 | if (test_bit(NFS_CS_NETUNREACH_FATAL, &cl_init->init_flags)) | 
|---|
| 237 | __set_bit(NFS_CS_NETUNREACH_FATAL, &clp->cl_flags); | 
|---|
| 238 | /* | 
|---|
| 239 | * Set up the connection to the server before we add add to the | 
|---|
| 240 | * global list. | 
|---|
| 241 | */ | 
|---|
| 242 | err = nfs_create_rpc_client(clp, cl_init, RPC_AUTH_GSS_KRB5I); | 
|---|
| 243 | if (err == -EINVAL) | 
|---|
| 244 | err = nfs_create_rpc_client(clp, cl_init, RPC_AUTH_UNIX); | 
|---|
| 245 | if (err < 0) | 
|---|
| 246 | goto error; | 
|---|
| 247 |  | 
|---|
| 248 | /* If no clientaddr= option was specified, find a usable cb address */ | 
|---|
| 249 | if (ip_addr == NULL) { | 
|---|
| 250 | struct sockaddr_storage cb_addr; | 
|---|
| 251 | struct sockaddr *sap = (struct sockaddr *)&cb_addr; | 
|---|
| 252 |  | 
|---|
| 253 | err = rpc_localaddr(clp->cl_rpcclient, sap, sizeof(cb_addr)); | 
|---|
| 254 | if (err < 0) | 
|---|
| 255 | goto error; | 
|---|
| 256 | err = rpc_ntop(sap, buf, sizeof(buf)); | 
|---|
| 257 | if (err < 0) | 
|---|
| 258 | goto error; | 
|---|
| 259 | ip_addr = (const char *)buf; | 
|---|
| 260 | } | 
|---|
| 261 | strscpy(clp->cl_ipaddr, ip_addr, sizeof(clp->cl_ipaddr)); | 
|---|
| 262 |  | 
|---|
| 263 | err = nfs_idmap_new(clp); | 
|---|
| 264 | if (err < 0) { | 
|---|
| 265 | dprintk( "%s: failed to create idmapper. Error = %d\n", | 
|---|
| 266 | __func__, err); | 
|---|
| 267 | goto error; | 
|---|
| 268 | } | 
|---|
| 269 | __set_bit(NFS_CS_IDMAP, &clp->cl_res_state); | 
|---|
| 270 | return clp; | 
|---|
| 271 |  | 
|---|
| 272 | error: | 
|---|
| 273 | nfs_free_client(clp); | 
|---|
| 274 | return ERR_PTR(error: err); | 
|---|
| 275 | } | 
|---|
| 276 |  | 
|---|
| 277 | /* | 
|---|
| 278 | * Destroy the NFS4 callback service | 
|---|
| 279 | */ | 
|---|
| 280 | static void nfs4_destroy_callback(struct nfs_client *clp) | 
|---|
| 281 | { | 
|---|
| 282 | if (__test_and_clear_bit(NFS_CS_CALLBACK, &clp->cl_res_state)) | 
|---|
| 283 | nfs_callback_down(minorversion: clp->cl_mvops->minor_version, net: clp->cl_net); | 
|---|
| 284 | } | 
|---|
| 285 |  | 
|---|
| 286 | static void nfs4_shutdown_client(struct nfs_client *clp) | 
|---|
| 287 | { | 
|---|
| 288 | if (__test_and_clear_bit(NFS_CS_RENEWD, &clp->cl_res_state)) | 
|---|
| 289 | nfs4_kill_renewd(clp); | 
|---|
| 290 | clp->cl_mvops->shutdown_client(clp); | 
|---|
| 291 | nfs4_destroy_callback(clp); | 
|---|
| 292 | if (__test_and_clear_bit(NFS_CS_IDMAP, &clp->cl_res_state)) | 
|---|
| 293 | nfs_idmap_delete(clp); | 
|---|
| 294 |  | 
|---|
| 295 | rpc_destroy_wait_queue(&clp->cl_rpcwaitq); | 
|---|
| 296 | kfree(objp: clp->cl_serverowner); | 
|---|
| 297 | kfree(objp: clp->cl_serverscope); | 
|---|
| 298 | kfree(objp: clp->cl_implid); | 
|---|
| 299 | kfree(objp: clp->cl_owner_id); | 
|---|
| 300 | } | 
|---|
| 301 |  | 
|---|
| 302 | void nfs4_free_client(struct nfs_client *clp) | 
|---|
| 303 | { | 
|---|
| 304 | nfs4_shutdown_client(clp); | 
|---|
| 305 | nfs_free_client(clp); | 
|---|
| 306 | } | 
|---|
| 307 |  | 
|---|
| 308 | /* | 
|---|
| 309 | * Initialize the NFS4 callback service | 
|---|
| 310 | */ | 
|---|
| 311 | static int nfs4_init_callback(struct nfs_client *clp) | 
|---|
| 312 | { | 
|---|
| 313 | struct rpc_xprt *xprt; | 
|---|
| 314 | int error; | 
|---|
| 315 |  | 
|---|
| 316 | xprt = rcu_dereference_raw(clp->cl_rpcclient->cl_xprt); | 
|---|
| 317 |  | 
|---|
| 318 | if (nfs4_has_session(clp)) { | 
|---|
| 319 | error = xprt_setup_backchannel(xprt, NFS41_BC_MIN_CALLBACKS); | 
|---|
| 320 | if (error < 0) | 
|---|
| 321 | return error; | 
|---|
| 322 | } | 
|---|
| 323 |  | 
|---|
| 324 | error = nfs_callback_up(minorversion: clp->cl_mvops->minor_version, xprt); | 
|---|
| 325 | if (error < 0) { | 
|---|
| 326 | dprintk( "%s: failed to start callback. Error = %d\n", | 
|---|
| 327 | __func__, error); | 
|---|
| 328 | return error; | 
|---|
| 329 | } | 
|---|
| 330 | __set_bit(NFS_CS_CALLBACK, &clp->cl_res_state); | 
|---|
| 331 |  | 
|---|
| 332 | return 0; | 
|---|
| 333 | } | 
|---|
| 334 |  | 
|---|
| 335 | /** | 
|---|
| 336 | * nfs40_init_client - nfs_client initialization tasks for NFSv4.0 | 
|---|
| 337 | * @clp: nfs_client to initialize | 
|---|
| 338 | * | 
|---|
| 339 | * Returns zero on success, or a negative errno if some error occurred. | 
|---|
| 340 | */ | 
|---|
| 341 | int nfs40_init_client(struct nfs_client *clp) | 
|---|
| 342 | { | 
|---|
| 343 | struct nfs4_slot_table *tbl; | 
|---|
| 344 | int ret; | 
|---|
| 345 |  | 
|---|
| 346 | tbl = kzalloc(sizeof(*tbl), GFP_NOFS); | 
|---|
| 347 | if (tbl == NULL) | 
|---|
| 348 | return -ENOMEM; | 
|---|
| 349 |  | 
|---|
| 350 | ret = nfs4_setup_slot_table(tbl, NFS4_MAX_SLOT_TABLE, | 
|---|
| 351 | queue: "NFSv4.0 transport Slot table"); | 
|---|
| 352 | if (ret) { | 
|---|
| 353 | nfs4_shutdown_slot_table(tbl); | 
|---|
| 354 | kfree(objp: tbl); | 
|---|
| 355 | return ret; | 
|---|
| 356 | } | 
|---|
| 357 |  | 
|---|
| 358 | clp->cl_slot_tbl = tbl; | 
|---|
| 359 | return 0; | 
|---|
| 360 | } | 
|---|
| 361 |  | 
|---|
| 362 | #if defined(CONFIG_NFS_V4_1) | 
|---|
| 363 |  | 
|---|
| 364 | /** | 
|---|
| 365 | * nfs41_init_client - nfs_client initialization tasks for NFSv4.1+ | 
|---|
| 366 | * @clp: nfs_client to initialize | 
|---|
| 367 | * | 
|---|
| 368 | * Returns zero on success, or a negative errno if some error occurred. | 
|---|
| 369 | */ | 
|---|
| 370 | int nfs41_init_client(struct nfs_client *clp) | 
|---|
| 371 | { | 
|---|
| 372 | struct nfs4_session *session = NULL; | 
|---|
| 373 |  | 
|---|
| 374 | /* | 
|---|
| 375 | * Create the session and mark it expired. | 
|---|
| 376 | * When a SEQUENCE operation encounters the expired session | 
|---|
| 377 | * it will do session recovery to initialize it. | 
|---|
| 378 | */ | 
|---|
| 379 | session = nfs4_alloc_session(clp); | 
|---|
| 380 | if (!session) | 
|---|
| 381 | return -ENOMEM; | 
|---|
| 382 |  | 
|---|
| 383 | clp->cl_session = session; | 
|---|
| 384 |  | 
|---|
| 385 | /* | 
|---|
| 386 | * The create session reply races with the server back | 
|---|
| 387 | * channel probe. Mark the client NFS_CS_SESSION_INITING | 
|---|
| 388 | * so that the client back channel can find the | 
|---|
| 389 | * nfs_client struct | 
|---|
| 390 | */ | 
|---|
| 391 | nfs_mark_client_ready(clp, NFS_CS_SESSION_INITING); | 
|---|
| 392 | return 0; | 
|---|
| 393 | } | 
|---|
| 394 |  | 
|---|
| 395 | #endif	/* CONFIG_NFS_V4_1 */ | 
|---|
| 396 |  | 
|---|
| 397 | /* | 
|---|
| 398 | * Initialize the minor version specific parts of an NFS4 client record | 
|---|
| 399 | */ | 
|---|
| 400 | static int nfs4_init_client_minor_version(struct nfs_client *clp) | 
|---|
| 401 | { | 
|---|
| 402 | int ret; | 
|---|
| 403 |  | 
|---|
| 404 | ret = clp->cl_mvops->init_client(clp); | 
|---|
| 405 | if (ret) | 
|---|
| 406 | return ret; | 
|---|
| 407 | return nfs4_init_callback(clp); | 
|---|
| 408 | } | 
|---|
| 409 |  | 
|---|
| 410 | static void nfs4_add_trunk(struct nfs_client *clp, struct nfs_client *old) | 
|---|
| 411 | { | 
|---|
| 412 | struct sockaddr_storage clp_addr, old_addr; | 
|---|
| 413 | struct sockaddr *clp_sap = (struct sockaddr *)&clp_addr; | 
|---|
| 414 | struct sockaddr *old_sap = (struct sockaddr *)&old_addr; | 
|---|
| 415 | size_t clp_salen; | 
|---|
| 416 | struct xprt_create xprt_args = { | 
|---|
| 417 | .ident = old->cl_proto, | 
|---|
| 418 | .net = old->cl_net, | 
|---|
| 419 | .servername = old->cl_hostname, | 
|---|
| 420 | }; | 
|---|
| 421 | int max_connect = test_bit(NFS_CS_PNFS, &clp->cl_flags) ? | 
|---|
| 422 | clp->cl_max_connect : old->cl_max_connect; | 
|---|
| 423 |  | 
|---|
| 424 | if (clp->cl_proto != old->cl_proto) | 
|---|
| 425 | return; | 
|---|
| 426 | clp_salen = rpc_peeraddr(clp->cl_rpcclient, clp_sap, sizeof(clp_addr)); | 
|---|
| 427 | rpc_peeraddr(old->cl_rpcclient, old_sap, sizeof(old_addr)); | 
|---|
| 428 |  | 
|---|
| 429 | if (clp_addr.ss_family != old_addr.ss_family) | 
|---|
| 430 | return; | 
|---|
| 431 |  | 
|---|
| 432 | xprt_args.dstaddr = clp_sap; | 
|---|
| 433 | xprt_args.addrlen = clp_salen; | 
|---|
| 434 |  | 
|---|
| 435 | rpc_clnt_add_xprt(old->cl_rpcclient, &xprt_args, | 
|---|
| 436 | setup: rpc_clnt_test_and_add_xprt, data: &max_connect); | 
|---|
| 437 | } | 
|---|
| 438 |  | 
|---|
| 439 | /** | 
|---|
| 440 | * nfs4_init_client - Initialise an NFS4 client record | 
|---|
| 441 | * | 
|---|
| 442 | * @clp: nfs_client to initialise | 
|---|
| 443 | * @cl_init: pointer to nfs_client_initdata | 
|---|
| 444 | * | 
|---|
| 445 | * Returns pointer to an NFS client, or an ERR_PTR value. | 
|---|
| 446 | */ | 
|---|
| 447 | struct nfs_client *nfs4_init_client(struct nfs_client *clp, | 
|---|
| 448 | const struct nfs_client_initdata *cl_init) | 
|---|
| 449 | { | 
|---|
| 450 | struct nfs_client *old; | 
|---|
| 451 | int error; | 
|---|
| 452 |  | 
|---|
| 453 | if (clp->cl_cons_state == NFS_CS_READY) | 
|---|
| 454 | /* the client is initialised already */ | 
|---|
| 455 | return clp; | 
|---|
| 456 |  | 
|---|
| 457 | error = nfs4_init_client_minor_version(clp); | 
|---|
| 458 | if (error < 0) | 
|---|
| 459 | goto error; | 
|---|
| 460 |  | 
|---|
| 461 | error = nfs4_discover_server_trunking(clp, &old); | 
|---|
| 462 | if (error < 0) | 
|---|
| 463 | goto error; | 
|---|
| 464 |  | 
|---|
| 465 | if (clp != old) { | 
|---|
| 466 | clp->cl_preserve_clid = true; | 
|---|
| 467 | /* | 
|---|
| 468 | * Mark the client as having failed initialization so other | 
|---|
| 469 | * processes walking the nfs_client_list in nfs_match_client() | 
|---|
| 470 | * won't try to use it. | 
|---|
| 471 | */ | 
|---|
| 472 | nfs_mark_client_ready(clp, state: -EPERM); | 
|---|
| 473 | if (old->cl_mvops->session_trunk) | 
|---|
| 474 | nfs4_add_trunk(clp, old); | 
|---|
| 475 | } | 
|---|
| 476 | clear_bit(NFS_CS_TSM_POSSIBLE, addr: &clp->cl_flags); | 
|---|
| 477 | nfs_put_client(clp); | 
|---|
| 478 | return old; | 
|---|
| 479 |  | 
|---|
| 480 | error: | 
|---|
| 481 | nfs_mark_client_ready(clp, state: error); | 
|---|
| 482 | nfs_put_client(clp); | 
|---|
| 483 | return ERR_PTR(error); | 
|---|
| 484 | } | 
|---|
| 485 |  | 
|---|
| 486 | /* | 
|---|
| 487 | * SETCLIENTID just did a callback update with the callback ident in | 
|---|
| 488 | * "drop," but server trunking discovery claims "drop" and "keep" are | 
|---|
| 489 | * actually the same server.  Swap the callback IDs so that "keep" | 
|---|
| 490 | * will continue to use the callback ident the server now knows about, | 
|---|
| 491 | * and so that "keep"'s original callback ident is destroyed when | 
|---|
| 492 | * "drop" is freed. | 
|---|
| 493 | */ | 
|---|
| 494 | static void nfs4_swap_callback_idents(struct nfs_client *keep, | 
|---|
| 495 | struct nfs_client *drop) | 
|---|
| 496 | { | 
|---|
| 497 | struct nfs_net *nn = net_generic(net: keep->cl_net, id: nfs_net_id); | 
|---|
| 498 | unsigned int save = keep->cl_cb_ident; | 
|---|
| 499 |  | 
|---|
| 500 | if (keep->cl_cb_ident == drop->cl_cb_ident) | 
|---|
| 501 | return; | 
|---|
| 502 |  | 
|---|
| 503 | dprintk( "%s: keeping callback ident %u and dropping ident %u\n", | 
|---|
| 504 | __func__, keep->cl_cb_ident, drop->cl_cb_ident); | 
|---|
| 505 |  | 
|---|
| 506 | spin_lock(lock: &nn->nfs_client_lock); | 
|---|
| 507 |  | 
|---|
| 508 | idr_replace(&nn->cb_ident_idr, keep, id: drop->cl_cb_ident); | 
|---|
| 509 | keep->cl_cb_ident = drop->cl_cb_ident; | 
|---|
| 510 |  | 
|---|
| 511 | idr_replace(&nn->cb_ident_idr, drop, id: save); | 
|---|
| 512 | drop->cl_cb_ident = save; | 
|---|
| 513 |  | 
|---|
| 514 | spin_unlock(lock: &nn->nfs_client_lock); | 
|---|
| 515 | } | 
|---|
| 516 |  | 
|---|
| 517 | static bool nfs4_match_client_owner_id(const struct nfs_client *clp1, | 
|---|
| 518 | const struct nfs_client *clp2) | 
|---|
| 519 | { | 
|---|
| 520 | if (clp1->cl_owner_id == NULL || clp2->cl_owner_id == NULL) | 
|---|
| 521 | return true; | 
|---|
| 522 | return strcmp(clp1->cl_owner_id, clp2->cl_owner_id) == 0; | 
|---|
| 523 | } | 
|---|
| 524 |  | 
|---|
| 525 | static bool nfs4_same_verifier(nfs4_verifier *v1, nfs4_verifier *v2) | 
|---|
| 526 | { | 
|---|
| 527 | return memcmp(v1->data, v2->data, sizeof(v1->data)) == 0; | 
|---|
| 528 | } | 
|---|
| 529 |  | 
|---|
| 530 | static int nfs4_match_client(struct nfs_client  *pos,  struct nfs_client *new, | 
|---|
| 531 | struct nfs_client **prev, struct nfs_net *nn) | 
|---|
| 532 | { | 
|---|
| 533 | int status; | 
|---|
| 534 |  | 
|---|
| 535 | if (pos->rpc_ops != new->rpc_ops) | 
|---|
| 536 | return 1; | 
|---|
| 537 |  | 
|---|
| 538 | if (pos->cl_minorversion != new->cl_minorversion) | 
|---|
| 539 | return 1; | 
|---|
| 540 |  | 
|---|
| 541 | /* If "pos" isn't marked ready, we can't trust the | 
|---|
| 542 | * remaining fields in "pos", especially the client | 
|---|
| 543 | * ID and serverowner fields.  Wait for CREATE_SESSION | 
|---|
| 544 | * to finish. */ | 
|---|
| 545 | if (pos->cl_cons_state > NFS_CS_READY) { | 
|---|
| 546 | refcount_inc(r: &pos->cl_count); | 
|---|
| 547 | spin_unlock(lock: &nn->nfs_client_lock); | 
|---|
| 548 |  | 
|---|
| 549 | nfs_put_client(*prev); | 
|---|
| 550 | *prev = pos; | 
|---|
| 551 |  | 
|---|
| 552 | status = nfs_wait_client_init_complete(clp: pos); | 
|---|
| 553 | spin_lock(lock: &nn->nfs_client_lock); | 
|---|
| 554 |  | 
|---|
| 555 | if (status < 0) | 
|---|
| 556 | return status; | 
|---|
| 557 | } | 
|---|
| 558 |  | 
|---|
| 559 | if (pos->cl_cons_state != NFS_CS_READY) | 
|---|
| 560 | return 1; | 
|---|
| 561 |  | 
|---|
| 562 | if (pos->cl_clientid != new->cl_clientid) | 
|---|
| 563 | return 1; | 
|---|
| 564 |  | 
|---|
| 565 | /* NFSv4.1 always uses the uniform string, however someone | 
|---|
| 566 | * might switch the uniquifier string on us. | 
|---|
| 567 | */ | 
|---|
| 568 | if (!nfs4_match_client_owner_id(clp1: pos, clp2: new)) | 
|---|
| 569 | return 1; | 
|---|
| 570 |  | 
|---|
| 571 | return 0; | 
|---|
| 572 | } | 
|---|
| 573 |  | 
|---|
| 574 | /** | 
|---|
| 575 | * nfs40_walk_client_list - Find server that recognizes a client ID | 
|---|
| 576 | * | 
|---|
| 577 | * @new: nfs_client with client ID to test | 
|---|
| 578 | * @result: OUT: found nfs_client, or new | 
|---|
| 579 | * @cred: credential to use for trunking test | 
|---|
| 580 | * | 
|---|
| 581 | * Returns zero, a negative errno, or a negative NFS4ERR status. | 
|---|
| 582 | * If zero is returned, an nfs_client pointer is planted in "result." | 
|---|
| 583 | * | 
|---|
| 584 | * NB: nfs40_walk_client_list() relies on the new nfs_client being | 
|---|
| 585 | *     the last nfs_client on the list. | 
|---|
| 586 | */ | 
|---|
| 587 | int nfs40_walk_client_list(struct nfs_client *new, | 
|---|
| 588 | struct nfs_client **result, | 
|---|
| 589 | const struct cred *cred) | 
|---|
| 590 | { | 
|---|
| 591 | struct nfs_net *nn = net_generic(net: new->cl_net, id: nfs_net_id); | 
|---|
| 592 | struct nfs_client *pos, *prev = NULL; | 
|---|
| 593 | struct nfs4_setclientid_res clid = { | 
|---|
| 594 | .clientid	= new->cl_clientid, | 
|---|
| 595 | .confirm	= new->cl_confirm, | 
|---|
| 596 | }; | 
|---|
| 597 | int status = -NFS4ERR_STALE_CLIENTID; | 
|---|
| 598 |  | 
|---|
| 599 | spin_lock(lock: &nn->nfs_client_lock); | 
|---|
| 600 | list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) { | 
|---|
| 601 |  | 
|---|
| 602 | if (pos == new) | 
|---|
| 603 | goto found; | 
|---|
| 604 |  | 
|---|
| 605 | status = nfs4_match_client(pos, new, prev: &prev, nn); | 
|---|
| 606 | if (status < 0) | 
|---|
| 607 | goto out_unlock; | 
|---|
| 608 | if (status != 0) | 
|---|
| 609 | continue; | 
|---|
| 610 | /* | 
|---|
| 611 | * We just sent a new SETCLIENTID, which should have | 
|---|
| 612 | * caused the server to return a new cl_confirm.  So if | 
|---|
| 613 | * cl_confirm is the same, then this is a different | 
|---|
| 614 | * server that just returned the same cl_confirm by | 
|---|
| 615 | * coincidence: | 
|---|
| 616 | */ | 
|---|
| 617 | if ((new != pos) && nfs4_same_verifier(v1: &pos->cl_confirm, | 
|---|
| 618 | v2: &new->cl_confirm)) | 
|---|
| 619 | continue; | 
|---|
| 620 | /* | 
|---|
| 621 | * But if the cl_confirm's are different, then the only | 
|---|
| 622 | * way that a SETCLIENTID_CONFIRM to pos can succeed is | 
|---|
| 623 | * if new and pos point to the same server: | 
|---|
| 624 | */ | 
|---|
| 625 | found: | 
|---|
| 626 | refcount_inc(r: &pos->cl_count); | 
|---|
| 627 | spin_unlock(lock: &nn->nfs_client_lock); | 
|---|
| 628 |  | 
|---|
| 629 | nfs_put_client(prev); | 
|---|
| 630 | prev = pos; | 
|---|
| 631 |  | 
|---|
| 632 | status = nfs4_proc_setclientid_confirm(pos, arg: &clid, cred); | 
|---|
| 633 | switch (status) { | 
|---|
| 634 | case -NFS4ERR_STALE_CLIENTID: | 
|---|
| 635 | break; | 
|---|
| 636 | case 0: | 
|---|
| 637 | nfs4_swap_callback_idents(keep: pos, drop: new); | 
|---|
| 638 | pos->cl_confirm = new->cl_confirm; | 
|---|
| 639 | nfs_mark_client_ready(clp: pos, NFS_CS_READY); | 
|---|
| 640 |  | 
|---|
| 641 | prev = NULL; | 
|---|
| 642 | *result = pos; | 
|---|
| 643 | goto out; | 
|---|
| 644 | case -ERESTARTSYS: | 
|---|
| 645 | case -ETIMEDOUT: | 
|---|
| 646 | /* The callback path may have been inadvertently | 
|---|
| 647 | * changed. Schedule recovery! | 
|---|
| 648 | */ | 
|---|
| 649 | nfs4_schedule_path_down_recovery(clp: pos); | 
|---|
| 650 | goto out; | 
|---|
| 651 | default: | 
|---|
| 652 | goto out; | 
|---|
| 653 | } | 
|---|
| 654 |  | 
|---|
| 655 | spin_lock(lock: &nn->nfs_client_lock); | 
|---|
| 656 | } | 
|---|
| 657 | out_unlock: | 
|---|
| 658 | spin_unlock(lock: &nn->nfs_client_lock); | 
|---|
| 659 |  | 
|---|
| 660 | /* No match found. The server lost our clientid */ | 
|---|
| 661 | out: | 
|---|
| 662 | nfs_put_client(prev); | 
|---|
| 663 | return status; | 
|---|
| 664 | } | 
|---|
| 665 |  | 
|---|
| 666 | #ifdef CONFIG_NFS_V4_1 | 
|---|
| 667 | /* | 
|---|
| 668 | * Returns true if the server major ids match | 
|---|
| 669 | */ | 
|---|
| 670 | bool | 
|---|
| 671 | nfs4_check_serverowner_major_id(struct nfs41_server_owner *o1, | 
|---|
| 672 | struct nfs41_server_owner *o2) | 
|---|
| 673 | { | 
|---|
| 674 | if (o1->major_id_sz != o2->major_id_sz) | 
|---|
| 675 | return false; | 
|---|
| 676 | return memcmp(o1->major_id, o2->major_id, o1->major_id_sz) == 0; | 
|---|
| 677 | } | 
|---|
| 678 |  | 
|---|
| 679 | /* | 
|---|
| 680 | * Returns true if the server scopes match | 
|---|
| 681 | */ | 
|---|
| 682 | static bool | 
|---|
| 683 | nfs4_check_server_scope(struct nfs41_server_scope *s1, | 
|---|
| 684 | struct nfs41_server_scope *s2) | 
|---|
| 685 | { | 
|---|
| 686 | if (s1->server_scope_sz != s2->server_scope_sz) | 
|---|
| 687 | return false; | 
|---|
| 688 | return memcmp(s1->server_scope, s2->server_scope, | 
|---|
| 689 | s1->server_scope_sz) == 0; | 
|---|
| 690 | } | 
|---|
| 691 |  | 
|---|
| 692 | /** | 
|---|
| 693 | * nfs4_detect_session_trunking - Checks for session trunking. | 
|---|
| 694 | * @clp:    original mount nfs_client | 
|---|
| 695 | * @res:    result structure from an exchange_id using the original mount | 
|---|
| 696 | *          nfs_client with a new multi_addr transport | 
|---|
| 697 | * @xprt:   pointer to the transport to add. | 
|---|
| 698 | * | 
|---|
| 699 | * Called after a successful EXCHANGE_ID on a multi-addr connection. | 
|---|
| 700 | * Upon success, add the transport. | 
|---|
| 701 | * | 
|---|
| 702 | * Returns zero on success, otherwise -EINVAL | 
|---|
| 703 | * | 
|---|
| 704 | * Note: since the exchange_id for the new multi_addr transport uses the | 
|---|
| 705 | * same nfs_client from the original mount, the cl_owner_id is reused, | 
|---|
| 706 | * so eir_clientowner is the same. | 
|---|
| 707 | */ | 
|---|
| 708 | int nfs4_detect_session_trunking(struct nfs_client *clp, | 
|---|
| 709 | struct nfs41_exchange_id_res *res, | 
|---|
| 710 | struct rpc_xprt *xprt) | 
|---|
| 711 | { | 
|---|
| 712 | /* Check eir_clientid */ | 
|---|
| 713 | if (clp->cl_clientid != res->clientid) | 
|---|
| 714 | goto out_err; | 
|---|
| 715 |  | 
|---|
| 716 | /* Check eir_server_owner so_major_id */ | 
|---|
| 717 | if (!nfs4_check_serverowner_major_id(clp->cl_serverowner, | 
|---|
| 718 | res->server_owner)) | 
|---|
| 719 | goto out_err; | 
|---|
| 720 |  | 
|---|
| 721 | /* Check eir_server_owner so_minor_id */ | 
|---|
| 722 | if (clp->cl_serverowner->minor_id != res->server_owner->minor_id) | 
|---|
| 723 | goto out_err; | 
|---|
| 724 |  | 
|---|
| 725 | /* Check eir_server_scope */ | 
|---|
| 726 | if (!nfs4_check_server_scope(clp->cl_serverscope, res->server_scope)) | 
|---|
| 727 | goto out_err; | 
|---|
| 728 |  | 
|---|
| 729 | pr_info( "NFS:  %s: Session trunking succeeded for %s\n", | 
|---|
| 730 | clp->cl_hostname, | 
|---|
| 731 | xprt->address_strings[RPC_DISPLAY_ADDR]); | 
|---|
| 732 |  | 
|---|
| 733 | return 0; | 
|---|
| 734 | out_err: | 
|---|
| 735 | pr_info( "NFS:  %s: Session trunking failed for %s\n", clp->cl_hostname, | 
|---|
| 736 | xprt->address_strings[RPC_DISPLAY_ADDR]); | 
|---|
| 737 |  | 
|---|
| 738 | return -EINVAL; | 
|---|
| 739 | } | 
|---|
| 740 |  | 
|---|
| 741 | /** | 
|---|
| 742 | * nfs41_walk_client_list - Find nfs_client that matches a client/server owner | 
|---|
| 743 | * | 
|---|
| 744 | * @new: nfs_client with client ID to test | 
|---|
| 745 | * @result: OUT: found nfs_client, or new | 
|---|
| 746 | * @cred: credential to use for trunking test | 
|---|
| 747 | * | 
|---|
| 748 | * Returns zero, a negative errno, or a negative NFS4ERR status. | 
|---|
| 749 | * If zero is returned, an nfs_client pointer is planted in "result." | 
|---|
| 750 | * | 
|---|
| 751 | * NB: nfs41_walk_client_list() relies on the new nfs_client being | 
|---|
| 752 | *     the last nfs_client on the list. | 
|---|
| 753 | */ | 
|---|
| 754 | int nfs41_walk_client_list(struct nfs_client *new, | 
|---|
| 755 | struct nfs_client **result, | 
|---|
| 756 | const struct cred *cred) | 
|---|
| 757 | { | 
|---|
| 758 | struct nfs_net *nn = net_generic(new->cl_net, nfs_net_id); | 
|---|
| 759 | struct nfs_client *pos, *prev = NULL; | 
|---|
| 760 | int status = -NFS4ERR_STALE_CLIENTID; | 
|---|
| 761 |  | 
|---|
| 762 | spin_lock(&nn->nfs_client_lock); | 
|---|
| 763 | list_for_each_entry(pos, &nn->nfs_client_list, cl_share_link) { | 
|---|
| 764 |  | 
|---|
| 765 | if (pos == new) | 
|---|
| 766 | goto found; | 
|---|
| 767 |  | 
|---|
| 768 | status = nfs4_match_client(pos, new, &prev, nn); | 
|---|
| 769 | if (status < 0) | 
|---|
| 770 | goto out; | 
|---|
| 771 | if (status != 0) | 
|---|
| 772 | continue; | 
|---|
| 773 |  | 
|---|
| 774 | /* | 
|---|
| 775 | * Note that session trunking is just a special subcase of | 
|---|
| 776 | * client id trunking. In either case, we want to fall back | 
|---|
| 777 | * to using the existing nfs_client. | 
|---|
| 778 | */ | 
|---|
| 779 | if (!nfs4_check_serverowner_major_id(pos->cl_serverowner, | 
|---|
| 780 | new->cl_serverowner)) | 
|---|
| 781 | continue; | 
|---|
| 782 |  | 
|---|
| 783 | found: | 
|---|
| 784 | refcount_inc(&pos->cl_count); | 
|---|
| 785 | *result = pos; | 
|---|
| 786 | status = 0; | 
|---|
| 787 | break; | 
|---|
| 788 | } | 
|---|
| 789 |  | 
|---|
| 790 | out: | 
|---|
| 791 | spin_unlock(&nn->nfs_client_lock); | 
|---|
| 792 | nfs_put_client(prev); | 
|---|
| 793 | return status; | 
|---|
| 794 | } | 
|---|
| 795 | #endif	/* CONFIG_NFS_V4_1 */ | 
|---|
| 796 |  | 
|---|
| 797 | static void nfs4_destroy_server(struct nfs_server *server) | 
|---|
| 798 | { | 
|---|
| 799 | LIST_HEAD(freeme); | 
|---|
| 800 |  | 
|---|
| 801 | nfs_server_return_all_delegations(server); | 
|---|
| 802 | unset_pnfs_layoutdriver(s: server); | 
|---|
| 803 | nfs4_purge_state_owners(server, &freeme); | 
|---|
| 804 | nfs4_free_state_owners(head: &freeme); | 
|---|
| 805 | kfree(objp: server->delegation_hash_table); | 
|---|
| 806 | } | 
|---|
| 807 |  | 
|---|
| 808 | /* | 
|---|
| 809 | * NFSv4.0 callback thread helper | 
|---|
| 810 | * | 
|---|
| 811 | * Find a client by callback identifier | 
|---|
| 812 | */ | 
|---|
| 813 | struct nfs_client * | 
|---|
| 814 | nfs4_find_client_ident(struct net *net, int cb_ident) | 
|---|
| 815 | { | 
|---|
| 816 | struct nfs_client *clp; | 
|---|
| 817 | struct nfs_net *nn = net_generic(net, id: nfs_net_id); | 
|---|
| 818 |  | 
|---|
| 819 | spin_lock(lock: &nn->nfs_client_lock); | 
|---|
| 820 | clp = idr_find(&nn->cb_ident_idr, id: cb_ident); | 
|---|
| 821 | if (clp) | 
|---|
| 822 | refcount_inc(r: &clp->cl_count); | 
|---|
| 823 | spin_unlock(lock: &nn->nfs_client_lock); | 
|---|
| 824 | return clp; | 
|---|
| 825 | } | 
|---|
| 826 |  | 
|---|
| 827 | #if defined(CONFIG_NFS_V4_1) | 
|---|
| 828 | /* Common match routine for v4.0 and v4.1 callback services */ | 
|---|
| 829 | static bool nfs4_cb_match_client(const struct sockaddr *addr, | 
|---|
| 830 | struct nfs_client *clp, u32 minorversion) | 
|---|
| 831 | { | 
|---|
| 832 | struct sockaddr *clap = (struct sockaddr *)&clp->cl_addr; | 
|---|
| 833 |  | 
|---|
| 834 | /* Don't match clients that failed to initialise */ | 
|---|
| 835 | if (!(clp->cl_cons_state == NFS_CS_READY || | 
|---|
| 836 | clp->cl_cons_state == NFS_CS_SESSION_INITING)) | 
|---|
| 837 | return false; | 
|---|
| 838 |  | 
|---|
| 839 | smp_rmb(); | 
|---|
| 840 |  | 
|---|
| 841 | /* Match the version and minorversion */ | 
|---|
| 842 | if (clp->rpc_ops->version != 4 || | 
|---|
| 843 | clp->cl_minorversion != minorversion) | 
|---|
| 844 | return false; | 
|---|
| 845 |  | 
|---|
| 846 | /* Match only the IP address, not the port number */ | 
|---|
| 847 | return rpc_cmp_addr(addr, clap); | 
|---|
| 848 | } | 
|---|
| 849 |  | 
|---|
| 850 | /* | 
|---|
| 851 | * NFSv4.1 callback thread helper | 
|---|
| 852 | * For CB_COMPOUND calls, find a client by IP address, protocol version, | 
|---|
| 853 | * minorversion, and sessionID | 
|---|
| 854 | * | 
|---|
| 855 | * Returns NULL if no such client | 
|---|
| 856 | */ | 
|---|
| 857 | struct nfs_client * | 
|---|
| 858 | nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr, | 
|---|
| 859 | struct nfs4_sessionid *sid, u32 minorversion) | 
|---|
| 860 | { | 
|---|
| 861 | struct nfs_client *clp; | 
|---|
| 862 | struct nfs_net *nn = net_generic(net, nfs_net_id); | 
|---|
| 863 |  | 
|---|
| 864 | spin_lock(&nn->nfs_client_lock); | 
|---|
| 865 | list_for_each_entry(clp, &nn->nfs_client_list, cl_share_link) { | 
|---|
| 866 | if (!nfs4_cb_match_client(addr, clp, minorversion)) | 
|---|
| 867 | continue; | 
|---|
| 868 |  | 
|---|
| 869 | if (!nfs4_has_session(clp)) | 
|---|
| 870 | continue; | 
|---|
| 871 |  | 
|---|
| 872 | /* Match sessionid*/ | 
|---|
| 873 | if (memcmp(clp->cl_session->sess_id.data, | 
|---|
| 874 | sid->data, NFS4_MAX_SESSIONID_LEN) != 0) | 
|---|
| 875 | continue; | 
|---|
| 876 |  | 
|---|
| 877 | refcount_inc(&clp->cl_count); | 
|---|
| 878 | spin_unlock(&nn->nfs_client_lock); | 
|---|
| 879 | return clp; | 
|---|
| 880 | } | 
|---|
| 881 | spin_unlock(&nn->nfs_client_lock); | 
|---|
| 882 | return NULL; | 
|---|
| 883 | } | 
|---|
| 884 |  | 
|---|
| 885 | #else /* CONFIG_NFS_V4_1 */ | 
|---|
| 886 |  | 
|---|
| 887 | struct nfs_client * | 
|---|
| 888 | nfs4_find_client_sessionid(struct net *net, const struct sockaddr *addr, | 
|---|
| 889 | struct nfs4_sessionid *sid, u32 minorversion) | 
|---|
| 890 | { | 
|---|
| 891 | return NULL; | 
|---|
| 892 | } | 
|---|
| 893 | #endif /* CONFIG_NFS_V4_1 */ | 
|---|
| 894 |  | 
|---|
| 895 | /* | 
|---|
| 896 | * Set up an NFS4 client | 
|---|
| 897 | */ | 
|---|
| 898 | static int nfs4_set_client(struct nfs_server *server, | 
|---|
| 899 | struct nfs_client_initdata *cl_init) | 
|---|
| 900 | { | 
|---|
| 901 | struct nfs_client *clp; | 
|---|
| 902 |  | 
|---|
| 903 | cl_init->nfs_mod = &nfs_v4; | 
|---|
| 904 | cl_init->cred = server->cred; | 
|---|
| 905 |  | 
|---|
| 906 | if (cl_init->minorversion == 0) { | 
|---|
| 907 | __set_bit(NFS_CS_REUSEPORT, &cl_init->init_flags); | 
|---|
| 908 | cl_init->max_connect = 0; | 
|---|
| 909 | } | 
|---|
| 910 |  | 
|---|
| 911 | switch (cl_init->proto) { | 
|---|
| 912 | case XPRT_TRANSPORT_RDMA: | 
|---|
| 913 | case XPRT_TRANSPORT_TCP: | 
|---|
| 914 | case XPRT_TRANSPORT_TCP_TLS: | 
|---|
| 915 | break; | 
|---|
| 916 | default: | 
|---|
| 917 | cl_init->nconnect = 0; | 
|---|
| 918 | } | 
|---|
| 919 |  | 
|---|
| 920 | if (server->flags & NFS_MOUNT_NORESVPORT) | 
|---|
| 921 | __set_bit(NFS_CS_NORESVPORT, &cl_init->init_flags); | 
|---|
| 922 | if (server->options & NFS_OPTION_MIGRATION) | 
|---|
| 923 | __set_bit(NFS_CS_MIGRATION, &cl_init->init_flags); | 
|---|
| 924 | if (test_bit(NFS_MIG_TSM_POSSIBLE, &server->mig_status)) | 
|---|
| 925 | __set_bit(NFS_CS_TSM_POSSIBLE, &cl_init->init_flags); | 
|---|
| 926 | server->port = rpc_get_port(sap: (struct sockaddr *)cl_init->addr); | 
|---|
| 927 |  | 
|---|
| 928 | if (server->flags & NFS_MOUNT_NETUNREACH_FATAL) | 
|---|
| 929 | __set_bit(NFS_CS_NETUNREACH_FATAL, &cl_init->init_flags); | 
|---|
| 930 |  | 
|---|
| 931 | /* Allocate or find a client reference we can use */ | 
|---|
| 932 | clp = nfs_get_client(cl_init); | 
|---|
| 933 | if (IS_ERR(ptr: clp)) | 
|---|
| 934 | return PTR_ERR(ptr: clp); | 
|---|
| 935 |  | 
|---|
| 936 | if (server->nfs_client == clp) { | 
|---|
| 937 | nfs_put_client(clp); | 
|---|
| 938 | return -ELOOP; | 
|---|
| 939 | } | 
|---|
| 940 |  | 
|---|
| 941 | /* | 
|---|
| 942 | * Query for the lease time on clientid setup or renewal | 
|---|
| 943 | * | 
|---|
| 944 | * Note that this will be set on nfs_clients that were created | 
|---|
| 945 | * only for the DS role and did not set this bit, but now will | 
|---|
| 946 | * serve a dual role. | 
|---|
| 947 | */ | 
|---|
| 948 | set_bit(NFS_CS_CHECK_LEASE_TIME, addr: &clp->cl_res_state); | 
|---|
| 949 |  | 
|---|
| 950 | server->nfs_client = clp; | 
|---|
| 951 | nfs_sysfs_add_server(s: server); | 
|---|
| 952 | nfs_sysfs_link_rpc_client(server, clnt: clp->cl_rpcclient, sysfs_prefix: "_state"); | 
|---|
| 953 |  | 
|---|
| 954 | return 0; | 
|---|
| 955 | } | 
|---|
| 956 |  | 
|---|
| 957 | /* | 
|---|
| 958 | * Set up a pNFS Data Server client. | 
|---|
| 959 | * | 
|---|
| 960 | * Return any existing nfs_client that matches server address,port,version | 
|---|
| 961 | * and minorversion. | 
|---|
| 962 | * | 
|---|
| 963 | * For a new nfs_client, use a soft mount (default), a low retrans and a | 
|---|
| 964 | * low timeout interval so that if a connection is lost, we retry through | 
|---|
| 965 | * the MDS. | 
|---|
| 966 | */ | 
|---|
| 967 | struct nfs_client *nfs4_set_ds_client(struct nfs_server *mds_srv, | 
|---|
| 968 | const struct sockaddr_storage *ds_addr, int ds_addrlen, | 
|---|
| 969 | int ds_proto, unsigned int ds_timeo, unsigned int ds_retrans, | 
|---|
| 970 | u32 minor_version) | 
|---|
| 971 | { | 
|---|
| 972 | struct rpc_timeout ds_timeout; | 
|---|
| 973 | struct nfs_client *mds_clp = mds_srv->nfs_client; | 
|---|
| 974 | struct nfs_client_initdata cl_init = { | 
|---|
| 975 | .addr = ds_addr, | 
|---|
| 976 | .addrlen = ds_addrlen, | 
|---|
| 977 | .nodename = mds_clp->cl_rpcclient->cl_nodename, | 
|---|
| 978 | .ip_addr = mds_clp->cl_ipaddr, | 
|---|
| 979 | .nfs_mod = &nfs_v4, | 
|---|
| 980 | .proto = ds_proto, | 
|---|
| 981 | .minorversion = minor_version, | 
|---|
| 982 | .net = mds_clp->cl_net, | 
|---|
| 983 | .timeparms = &ds_timeout, | 
|---|
| 984 | .cred = mds_srv->cred, | 
|---|
| 985 | .xprtsec = mds_srv->nfs_client->cl_xprtsec, | 
|---|
| 986 | }; | 
|---|
| 987 | char buf[INET6_ADDRSTRLEN + 1]; | 
|---|
| 988 |  | 
|---|
| 989 | if (rpc_ntop((struct sockaddr *)ds_addr, buf, sizeof(buf)) <= 0) | 
|---|
| 990 | return ERR_PTR(error: -EINVAL); | 
|---|
| 991 | cl_init.hostname = buf; | 
|---|
| 992 |  | 
|---|
| 993 | switch (ds_proto) { | 
|---|
| 994 | case XPRT_TRANSPORT_RDMA: | 
|---|
| 995 | case XPRT_TRANSPORT_TCP: | 
|---|
| 996 | case XPRT_TRANSPORT_TCP_TLS: | 
|---|
| 997 | if (mds_clp->cl_nconnect > 1) { | 
|---|
| 998 | cl_init.nconnect = mds_clp->cl_nconnect; | 
|---|
| 999 | cl_init.max_connect = NFS_MAX_TRANSPORTS; | 
|---|
| 1000 | } | 
|---|
| 1001 | } | 
|---|
| 1002 |  | 
|---|
| 1003 | if (mds_srv->flags & NFS_MOUNT_NORESVPORT) | 
|---|
| 1004 | __set_bit(NFS_CS_NORESVPORT, &cl_init.init_flags); | 
|---|
| 1005 | if (test_bit(NFS_CS_NETUNREACH_FATAL, &mds_clp->cl_flags)) | 
|---|
| 1006 | __set_bit(NFS_CS_NETUNREACH_FATAL, &cl_init.init_flags); | 
|---|
| 1007 |  | 
|---|
| 1008 | __set_bit(NFS_CS_PNFS, &cl_init.init_flags); | 
|---|
| 1009 | cl_init.max_connect = NFS_MAX_TRANSPORTS; | 
|---|
| 1010 | /* | 
|---|
| 1011 | * Set an authflavor equual to the MDS value. Use the MDS nfs_client | 
|---|
| 1012 | * cl_ipaddr so as to use the same EXCHANGE_ID co_ownerid as the MDS | 
|---|
| 1013 | * (section 13.1 RFC 5661). | 
|---|
| 1014 | */ | 
|---|
| 1015 | nfs_init_timeout_values(to: &ds_timeout, proto: ds_proto, timeo: ds_timeo, retrans: ds_retrans); | 
|---|
| 1016 | return nfs_get_client(&cl_init); | 
|---|
| 1017 | } | 
|---|
| 1018 | EXPORT_SYMBOL_GPL(nfs4_set_ds_client); | 
|---|
| 1019 |  | 
|---|
| 1020 | /* | 
|---|
| 1021 | * Session has been established, and the client marked ready. | 
|---|
| 1022 | * Limit the mount rsize, wsize and dtsize using negotiated fore | 
|---|
| 1023 | * channel attributes. | 
|---|
| 1024 | */ | 
|---|
| 1025 | static void nfs4_session_limit_rwsize(struct nfs_server *server) | 
|---|
| 1026 | { | 
|---|
| 1027 | #ifdef CONFIG_NFS_V4_1 | 
|---|
| 1028 | struct nfs4_session *sess; | 
|---|
| 1029 | u32 server_resp_sz; | 
|---|
| 1030 | u32 server_rqst_sz; | 
|---|
| 1031 |  | 
|---|
| 1032 | if (!nfs4_has_session(server->nfs_client)) | 
|---|
| 1033 | return; | 
|---|
| 1034 | sess = server->nfs_client->cl_session; | 
|---|
| 1035 | server_resp_sz = sess->fc_attrs.max_resp_sz - nfs41_maxread_overhead; | 
|---|
| 1036 | server_rqst_sz = sess->fc_attrs.max_rqst_sz - nfs41_maxwrite_overhead; | 
|---|
| 1037 |  | 
|---|
| 1038 | if (server->dtsize > server_resp_sz) | 
|---|
| 1039 | server->dtsize = server_resp_sz; | 
|---|
| 1040 | if (server->rsize > server_resp_sz) | 
|---|
| 1041 | server->rsize = server_resp_sz; | 
|---|
| 1042 | if (server->wsize > server_rqst_sz) | 
|---|
| 1043 | server->wsize = server_rqst_sz; | 
|---|
| 1044 | #endif /* CONFIG_NFS_V4_1 */ | 
|---|
| 1045 | } | 
|---|
| 1046 |  | 
|---|
| 1047 | /* | 
|---|
| 1048 | * Limit xattr sizes using the channel attributes. | 
|---|
| 1049 | */ | 
|---|
| 1050 | static void nfs4_session_limit_xasize(struct nfs_server *server) | 
|---|
| 1051 | { | 
|---|
| 1052 | #ifdef CONFIG_NFS_V4_2 | 
|---|
| 1053 | struct nfs4_session *sess; | 
|---|
| 1054 | u32 server_gxa_sz; | 
|---|
| 1055 | u32 server_sxa_sz; | 
|---|
| 1056 | u32 server_lxa_sz; | 
|---|
| 1057 |  | 
|---|
| 1058 | if (!nfs4_has_session(server->nfs_client)) | 
|---|
| 1059 | return; | 
|---|
| 1060 |  | 
|---|
| 1061 | sess = server->nfs_client->cl_session; | 
|---|
| 1062 |  | 
|---|
| 1063 | server_gxa_sz = sess->fc_attrs.max_resp_sz - nfs42_maxgetxattr_overhead; | 
|---|
| 1064 | server_sxa_sz = sess->fc_attrs.max_rqst_sz - nfs42_maxsetxattr_overhead; | 
|---|
| 1065 | server_lxa_sz = sess->fc_attrs.max_resp_sz - | 
|---|
| 1066 | nfs42_maxlistxattrs_overhead; | 
|---|
| 1067 |  | 
|---|
| 1068 | if (server->gxasize > server_gxa_sz) | 
|---|
| 1069 | server->gxasize = server_gxa_sz; | 
|---|
| 1070 | if (server->sxasize > server_sxa_sz) | 
|---|
| 1071 | server->sxasize = server_sxa_sz; | 
|---|
| 1072 | if (server->lxasize > server_lxa_sz) | 
|---|
| 1073 | server->lxasize = server_lxa_sz; | 
|---|
| 1074 | #endif | 
|---|
| 1075 | } | 
|---|
| 1076 |  | 
|---|
| 1077 | static int nfs4_server_common_setup(struct nfs_server *server, | 
|---|
| 1078 | struct nfs_fh *mntfh, bool auth_probe) | 
|---|
| 1079 | { | 
|---|
| 1080 | int error; | 
|---|
| 1081 |  | 
|---|
| 1082 | error = nfs4_delegation_hash_alloc(server); | 
|---|
| 1083 | if (error) | 
|---|
| 1084 | return error; | 
|---|
| 1085 |  | 
|---|
| 1086 | /* data servers support only a subset of NFSv4.1 */ | 
|---|
| 1087 | if (is_ds_only_client(clp: server->nfs_client)) | 
|---|
| 1088 | return -EPROTONOSUPPORT; | 
|---|
| 1089 |  | 
|---|
| 1090 | /* We must ensure the session is initialised first */ | 
|---|
| 1091 | error = nfs4_init_session(clp: server->nfs_client); | 
|---|
| 1092 | if (error < 0) | 
|---|
| 1093 | return error; | 
|---|
| 1094 |  | 
|---|
| 1095 | nfs_server_set_init_caps(server); | 
|---|
| 1096 |  | 
|---|
| 1097 | /* Probe the root fh to retrieve its FSID and filehandle */ | 
|---|
| 1098 | error = nfs4_get_rootfh(server, mntfh, auth_probe); | 
|---|
| 1099 | if (error < 0) | 
|---|
| 1100 | return error; | 
|---|
| 1101 |  | 
|---|
| 1102 | dprintk( "Server FSID: %llx:%llx\n", | 
|---|
| 1103 | (unsigned long long) server->fsid.major, | 
|---|
| 1104 | (unsigned long long) server->fsid.minor); | 
|---|
| 1105 | nfs_display_fhandle(fh: mntfh, caption: "Pseudo-fs root FH"); | 
|---|
| 1106 |  | 
|---|
| 1107 | error = nfs_probe_server(server, mntfh); | 
|---|
| 1108 | if (error < 0) | 
|---|
| 1109 | return error; | 
|---|
| 1110 |  | 
|---|
| 1111 | nfs4_session_limit_rwsize(server); | 
|---|
| 1112 | nfs4_session_limit_xasize(server); | 
|---|
| 1113 |  | 
|---|
| 1114 | if (server->namelen == 0 || server->namelen > NFS4_MAXNAMLEN) | 
|---|
| 1115 | server->namelen = NFS4_MAXNAMLEN; | 
|---|
| 1116 |  | 
|---|
| 1117 | nfs_server_insert_lists(server); | 
|---|
| 1118 | server->mount_time = jiffies; | 
|---|
| 1119 | server->destroy = nfs4_destroy_server; | 
|---|
| 1120 | return 0; | 
|---|
| 1121 | } | 
|---|
| 1122 |  | 
|---|
| 1123 | /* | 
|---|
| 1124 | * Create a version 4 volume record | 
|---|
| 1125 | */ | 
|---|
| 1126 | static int nfs4_init_server(struct nfs_server *server, struct fs_context *fc) | 
|---|
| 1127 | { | 
|---|
| 1128 | struct nfs_fs_context *ctx = nfs_fc2context(fc); | 
|---|
| 1129 | struct rpc_timeout timeparms; | 
|---|
| 1130 | struct nfs_client_initdata cl_init = { | 
|---|
| 1131 | .hostname = ctx->nfs_server.hostname, | 
|---|
| 1132 | .addr = &ctx->nfs_server._address, | 
|---|
| 1133 | .addrlen = ctx->nfs_server.addrlen, | 
|---|
| 1134 | .ip_addr = ctx->client_address, | 
|---|
| 1135 | .proto = ctx->nfs_server.protocol, | 
|---|
| 1136 | .minorversion = ctx->minorversion, | 
|---|
| 1137 | .net = fc->net_ns, | 
|---|
| 1138 | .timeparms = &timeparms, | 
|---|
| 1139 | .xprtsec = ctx->xprtsec, | 
|---|
| 1140 | .nconnect = ctx->nfs_server.nconnect, | 
|---|
| 1141 | .max_connect = ctx->nfs_server.max_connect, | 
|---|
| 1142 | }; | 
|---|
| 1143 | int error; | 
|---|
| 1144 |  | 
|---|
| 1145 | nfs_init_timeout_values(to: &timeparms, proto: ctx->nfs_server.protocol, | 
|---|
| 1146 | timeo: ctx->timeo, retrans: ctx->retrans); | 
|---|
| 1147 |  | 
|---|
| 1148 | /* Initialise the client representation from the mount data */ | 
|---|
| 1149 | server->flags = ctx->flags; | 
|---|
| 1150 | server->options = ctx->options; | 
|---|
| 1151 | server->auth_info = ctx->auth_info; | 
|---|
| 1152 |  | 
|---|
| 1153 | /* Use the first specified auth flavor. If this flavor isn't | 
|---|
| 1154 | * allowed by the server, use the SECINFO path to try the | 
|---|
| 1155 | * other specified flavors */ | 
|---|
| 1156 | if (ctx->auth_info.flavor_len >= 1) | 
|---|
| 1157 | ctx->selected_flavor = ctx->auth_info.flavors[0]; | 
|---|
| 1158 | else | 
|---|
| 1159 | ctx->selected_flavor = RPC_AUTH_UNIX; | 
|---|
| 1160 |  | 
|---|
| 1161 | /* Get a client record */ | 
|---|
| 1162 | error = nfs4_set_client(server, cl_init: &cl_init); | 
|---|
| 1163 | if (error < 0) | 
|---|
| 1164 | return error; | 
|---|
| 1165 |  | 
|---|
| 1166 | if (ctx->rsize) | 
|---|
| 1167 | server->rsize = nfs_io_size(iosize: ctx->rsize, proto: server->nfs_client->cl_proto); | 
|---|
| 1168 | if (ctx->wsize) | 
|---|
| 1169 | server->wsize = nfs_io_size(iosize: ctx->wsize, proto: server->nfs_client->cl_proto); | 
|---|
| 1170 |  | 
|---|
| 1171 | server->acregmin = ctx->acregmin * HZ; | 
|---|
| 1172 | server->acregmax = ctx->acregmax * HZ; | 
|---|
| 1173 | server->acdirmin = ctx->acdirmin * HZ; | 
|---|
| 1174 | server->acdirmax = ctx->acdirmax * HZ; | 
|---|
| 1175 | server->port     = ctx->nfs_server.port; | 
|---|
| 1176 |  | 
|---|
| 1177 | return nfs_init_server_rpcclient(server, t: &timeparms, | 
|---|
| 1178 | ctx->selected_flavor); | 
|---|
| 1179 | } | 
|---|
| 1180 |  | 
|---|
| 1181 | /* | 
|---|
| 1182 | * Create a version 4 volume record | 
|---|
| 1183 | * - keyed on server and FSID | 
|---|
| 1184 | */ | 
|---|
| 1185 | struct nfs_server *nfs4_create_server(struct fs_context *fc) | 
|---|
| 1186 | { | 
|---|
| 1187 | struct nfs_fs_context *ctx = nfs_fc2context(fc); | 
|---|
| 1188 | struct nfs_server *server; | 
|---|
| 1189 | bool auth_probe; | 
|---|
| 1190 | int error; | 
|---|
| 1191 |  | 
|---|
| 1192 | server = nfs_alloc_server(); | 
|---|
| 1193 | if (!server) | 
|---|
| 1194 | return ERR_PTR(error: -ENOMEM); | 
|---|
| 1195 |  | 
|---|
| 1196 | server->cred = get_cred(cred: fc->cred); | 
|---|
| 1197 |  | 
|---|
| 1198 | auth_probe = ctx->auth_info.flavor_len < 1; | 
|---|
| 1199 |  | 
|---|
| 1200 | /* set up the general RPC client */ | 
|---|
| 1201 | error = nfs4_init_server(server, fc); | 
|---|
| 1202 | if (error < 0) | 
|---|
| 1203 | goto error; | 
|---|
| 1204 |  | 
|---|
| 1205 | error = nfs4_server_common_setup(server, mntfh: ctx->mntfh, auth_probe); | 
|---|
| 1206 | if (error < 0) | 
|---|
| 1207 | goto error; | 
|---|
| 1208 |  | 
|---|
| 1209 | return server; | 
|---|
| 1210 |  | 
|---|
| 1211 | error: | 
|---|
| 1212 | nfs_free_server(server); | 
|---|
| 1213 | return ERR_PTR(error); | 
|---|
| 1214 | } | 
|---|
| 1215 |  | 
|---|
| 1216 | /* | 
|---|
| 1217 | * Create an NFS4 referral server record | 
|---|
| 1218 | */ | 
|---|
| 1219 | struct nfs_server *nfs4_create_referral_server(struct fs_context *fc) | 
|---|
| 1220 | { | 
|---|
| 1221 | struct nfs_fs_context *ctx = nfs_fc2context(fc); | 
|---|
| 1222 | struct nfs_server *parent_server = NFS_SB(s: ctx->clone_data.sb); | 
|---|
| 1223 | struct nfs_client *parent_client = parent_server->nfs_client; | 
|---|
| 1224 | struct nfs_client_initdata cl_init = { | 
|---|
| 1225 | .hostname = ctx->nfs_server.hostname, | 
|---|
| 1226 | .addr = &ctx->nfs_server._address, | 
|---|
| 1227 | .addrlen = ctx->nfs_server.addrlen, | 
|---|
| 1228 | .ip_addr = parent_client->cl_ipaddr, | 
|---|
| 1229 | .minorversion = parent_client->cl_mvops->minor_version, | 
|---|
| 1230 | .net = parent_client->cl_net, | 
|---|
| 1231 | .timeparms = parent_server->client->cl_timeout, | 
|---|
| 1232 | .xprtsec = parent_client->cl_xprtsec, | 
|---|
| 1233 | .nconnect = parent_client->cl_nconnect, | 
|---|
| 1234 | .max_connect = parent_client->cl_max_connect, | 
|---|
| 1235 | }; | 
|---|
| 1236 | struct nfs_server *server; | 
|---|
| 1237 | bool auth_probe; | 
|---|
| 1238 | int error; | 
|---|
| 1239 |  | 
|---|
| 1240 | server = nfs_alloc_server(); | 
|---|
| 1241 | if (!server) | 
|---|
| 1242 | return ERR_PTR(error: -ENOMEM); | 
|---|
| 1243 |  | 
|---|
| 1244 | server->cred = get_cred(cred: parent_server->cred); | 
|---|
| 1245 |  | 
|---|
| 1246 | /* Initialise the client representation from the parent server */ | 
|---|
| 1247 | nfs_server_copy_userdata(server, parent_server); | 
|---|
| 1248 |  | 
|---|
| 1249 | /* Get a client representation */ | 
|---|
| 1250 | #if IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA) | 
|---|
| 1251 | rpc_set_port(&ctx->nfs_server.address, NFS_RDMA_PORT); | 
|---|
| 1252 | cl_init.proto = XPRT_TRANSPORT_RDMA; | 
|---|
| 1253 | error = nfs4_set_client(server, &cl_init); | 
|---|
| 1254 | if (!error) | 
|---|
| 1255 | goto init_server; | 
|---|
| 1256 | #endif	/* IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA) */ | 
|---|
| 1257 |  | 
|---|
| 1258 | cl_init.proto = XPRT_TRANSPORT_TCP; | 
|---|
| 1259 | if (parent_client->cl_xprtsec.policy != RPC_XPRTSEC_NONE) | 
|---|
| 1260 | cl_init.proto = XPRT_TRANSPORT_TCP_TLS; | 
|---|
| 1261 | rpc_set_port(sap: &ctx->nfs_server.address, NFS_PORT); | 
|---|
| 1262 | error = nfs4_set_client(server, cl_init: &cl_init); | 
|---|
| 1263 | if (error < 0) | 
|---|
| 1264 | goto error; | 
|---|
| 1265 |  | 
|---|
| 1266 | #if IS_ENABLED(CONFIG_SUNRPC_XPRT_RDMA) | 
|---|
| 1267 | init_server: | 
|---|
| 1268 | #endif | 
|---|
| 1269 | error = nfs_init_server_rpcclient(server, t: parent_server->client->cl_timeout, | 
|---|
| 1270 | ctx->selected_flavor); | 
|---|
| 1271 | if (error < 0) | 
|---|
| 1272 | goto error; | 
|---|
| 1273 |  | 
|---|
| 1274 | auth_probe = parent_server->auth_info.flavor_len < 1; | 
|---|
| 1275 |  | 
|---|
| 1276 | error = nfs4_server_common_setup(server, mntfh: ctx->mntfh, auth_probe); | 
|---|
| 1277 | if (error < 0) | 
|---|
| 1278 | goto error; | 
|---|
| 1279 |  | 
|---|
| 1280 | return server; | 
|---|
| 1281 |  | 
|---|
| 1282 | error: | 
|---|
| 1283 | nfs_free_server(server); | 
|---|
| 1284 | return ERR_PTR(error); | 
|---|
| 1285 | } | 
|---|
| 1286 |  | 
|---|
| 1287 | /** | 
|---|
| 1288 | * nfs4_update_server - Move an nfs_server to a different nfs_client | 
|---|
| 1289 | * | 
|---|
| 1290 | * @server: represents FSID to be moved | 
|---|
| 1291 | * @hostname: new end-point's hostname | 
|---|
| 1292 | * @sap: new end-point's socket address | 
|---|
| 1293 | * @salen: size of "sap" | 
|---|
| 1294 | * @net: net namespace | 
|---|
| 1295 | * | 
|---|
| 1296 | * The nfs_server must be quiescent before this function is invoked. | 
|---|
| 1297 | * Either its session is drained (NFSv4.1+), or its transport is | 
|---|
| 1298 | * plugged and drained (NFSv4.0). | 
|---|
| 1299 | * | 
|---|
| 1300 | * Returns zero on success, or a negative errno value. | 
|---|
| 1301 | */ | 
|---|
| 1302 | int nfs4_update_server(struct nfs_server *server, const char *hostname, | 
|---|
| 1303 | struct sockaddr_storage *sap, size_t salen, struct net *net) | 
|---|
| 1304 | { | 
|---|
| 1305 | struct nfs_client *clp = server->nfs_client; | 
|---|
| 1306 | struct rpc_clnt *clnt = server->client; | 
|---|
| 1307 | struct xprt_create xargs = { | 
|---|
| 1308 | .ident		= clp->cl_proto, | 
|---|
| 1309 | .net		= net, | 
|---|
| 1310 | .dstaddr	= (struct sockaddr *)sap, | 
|---|
| 1311 | .addrlen	= salen, | 
|---|
| 1312 | .servername	= hostname, | 
|---|
| 1313 | /* cel: bleh. We might need to pass TLS parameters here */ | 
|---|
| 1314 | }; | 
|---|
| 1315 | char buf[INET6_ADDRSTRLEN + 1]; | 
|---|
| 1316 | struct sockaddr_storage address; | 
|---|
| 1317 | struct sockaddr *localaddr = (struct sockaddr *)&address; | 
|---|
| 1318 | struct nfs_client_initdata cl_init = { | 
|---|
| 1319 | .hostname = hostname, | 
|---|
| 1320 | .addr = sap, | 
|---|
| 1321 | .addrlen = salen, | 
|---|
| 1322 | .ip_addr = buf, | 
|---|
| 1323 | .proto = clp->cl_proto, | 
|---|
| 1324 | .minorversion = clp->cl_minorversion, | 
|---|
| 1325 | .net = net, | 
|---|
| 1326 | .timeparms = clnt->cl_timeout, | 
|---|
| 1327 | .xprtsec = clp->cl_xprtsec, | 
|---|
| 1328 | .nconnect = clp->cl_nconnect, | 
|---|
| 1329 | .max_connect = clp->cl_max_connect, | 
|---|
| 1330 | }; | 
|---|
| 1331 | int error; | 
|---|
| 1332 |  | 
|---|
| 1333 | error = rpc_switch_client_transport(clnt, &xargs, clnt->cl_timeout); | 
|---|
| 1334 | if (error != 0) | 
|---|
| 1335 | return error; | 
|---|
| 1336 |  | 
|---|
| 1337 | error = rpc_localaddr(clnt, localaddr, sizeof(address)); | 
|---|
| 1338 | if (error != 0) | 
|---|
| 1339 | return error; | 
|---|
| 1340 |  | 
|---|
| 1341 | if (rpc_ntop(localaddr, buf, sizeof(buf)) == 0) | 
|---|
| 1342 | return -EAFNOSUPPORT; | 
|---|
| 1343 |  | 
|---|
| 1344 | nfs_server_remove_lists(server); | 
|---|
| 1345 | set_bit(NFS_MIG_TSM_POSSIBLE, addr: &server->mig_status); | 
|---|
| 1346 | error = nfs4_set_client(server, cl_init: &cl_init); | 
|---|
| 1347 | clear_bit(NFS_MIG_TSM_POSSIBLE, addr: &server->mig_status); | 
|---|
| 1348 | if (error != 0) { | 
|---|
| 1349 | nfs_server_insert_lists(server); | 
|---|
| 1350 | return error; | 
|---|
| 1351 | } | 
|---|
| 1352 | nfs_put_client(clp); | 
|---|
| 1353 |  | 
|---|
| 1354 | if (server->nfs_client->cl_hostname == NULL) { | 
|---|
| 1355 | server->nfs_client->cl_hostname = kstrdup(s: hostname, GFP_KERNEL); | 
|---|
| 1356 | if (server->nfs_client->cl_hostname == NULL) | 
|---|
| 1357 | return -ENOMEM; | 
|---|
| 1358 | } | 
|---|
| 1359 | nfs_server_insert_lists(server); | 
|---|
| 1360 |  | 
|---|
| 1361 | return nfs_probe_server(server, NFS_FH(inode: d_inode(dentry: server->super->s_root))); | 
|---|
| 1362 | } | 
|---|
| 1363 |  | 
|---|