11058c121SJohn Baldwin /*- 21058c121SJohn Baldwin * SPDX-License-Identifier: BSD-2-Clause 31058c121SJohn Baldwin * 41058c121SJohn Baldwin * Copyright (c) 2023-2024 Chelsio Communications, Inc. 51058c121SJohn Baldwin * Written by: John Baldwin <jhb@FreeBSD.org> 61058c121SJohn Baldwin */ 71058c121SJohn Baldwin 8*8bba2c0fSJohn Baldwin #include <sys/dnv.h> 9365b89e8SJohn Baldwin #include <sys/nv.h> 101058c121SJohn Baldwin #include <sys/socket.h> 111058c121SJohn Baldwin #include <err.h> 121058c121SJohn Baldwin #include <libnvmf.h> 131058c121SJohn Baldwin #include <stdlib.h> 141058c121SJohn Baldwin #include <string.h> 151058c121SJohn Baldwin #include <sysexits.h> 161058c121SJohn Baldwin #include <unistd.h> 171058c121SJohn Baldwin 181058c121SJohn Baldwin #include "nvmecontrol.h" 191058c121SJohn Baldwin #include "fabrics.h" 201058c121SJohn Baldwin 211058c121SJohn Baldwin /* 221058c121SJohn Baldwin * See comment about other possible settings in connect.c. 231058c121SJohn Baldwin */ 241058c121SJohn Baldwin 251058c121SJohn Baldwin static struct options { 261058c121SJohn Baldwin const char *dev; 271058c121SJohn Baldwin const char *transport; 281058c121SJohn Baldwin const char *hostnqn; 291058c121SJohn Baldwin uint32_t kato; 301058c121SJohn Baldwin uint16_t num_io_queues; 311058c121SJohn Baldwin uint16_t queue_size; 321058c121SJohn Baldwin bool data_digests; 331058c121SJohn Baldwin bool flow_control; 341058c121SJohn Baldwin bool header_digests; 351058c121SJohn Baldwin } opt = { 361058c121SJohn Baldwin .dev = NULL, 371058c121SJohn Baldwin .transport = "tcp", 381058c121SJohn Baldwin .hostnqn = NULL, 391058c121SJohn Baldwin .kato = NVMF_KATO_DEFAULT / 1000, 401058c121SJohn Baldwin .num_io_queues = 1, 411058c121SJohn Baldwin .queue_size = 0, 421058c121SJohn Baldwin .data_digests = false, 431058c121SJohn Baldwin .flow_control = false, 441058c121SJohn Baldwin .header_digests = false, 451058c121SJohn Baldwin }; 461058c121SJohn Baldwin 471058c121SJohn Baldwin static void 48*8bba2c0fSJohn Baldwin tcp_association_params(struct nvmf_association_params *params, 49*8bba2c0fSJohn Baldwin bool header_digests, bool data_digests) 501058c121SJohn Baldwin { 511058c121SJohn Baldwin params->tcp.pda = 0; 52*8bba2c0fSJohn Baldwin params->tcp.header_digests = header_digests; 53*8bba2c0fSJohn Baldwin params->tcp.data_digests = data_digests; 541058c121SJohn Baldwin /* XXX */ 551058c121SJohn Baldwin params->tcp.maxr2t = 1; 561058c121SJohn Baldwin } 571058c121SJohn Baldwin 581058c121SJohn Baldwin static int 59*8bba2c0fSJohn Baldwin reconnect_nvm_controller(int fd, const struct nvmf_association_params *aparams, 60*8bba2c0fSJohn Baldwin enum nvmf_trtype trtype, int adrfam, const char *address, const char *port, 61*8bba2c0fSJohn Baldwin uint16_t cntlid, const char *subnqn, const char *hostnqn, uint32_t kato, 62*8bba2c0fSJohn Baldwin u_int num_io_queues, u_int queue_size, 63*8bba2c0fSJohn Baldwin const struct nvme_discovery_log_entry *dle) 641058c121SJohn Baldwin { 651058c121SJohn Baldwin struct nvme_controller_data cdata; 66*8bba2c0fSJohn Baldwin struct nvme_discovery_log_entry dle_thunk; 671058c121SJohn Baldwin struct nvmf_qpair *admin, **io; 681058c121SJohn Baldwin int error; 691058c121SJohn Baldwin 70*8bba2c0fSJohn Baldwin io = calloc(num_io_queues, sizeof(*io)); 71*8bba2c0fSJohn Baldwin error = connect_nvm_queues(aparams, trtype, adrfam, address, port, 72*8bba2c0fSJohn Baldwin cntlid, subnqn, hostnqn, kato, &admin, io, num_io_queues, 73*8bba2c0fSJohn Baldwin queue_size, &cdata); 740ac468c7SJohn Baldwin if (error != 0) { 750ac468c7SJohn Baldwin free(io); 761058c121SJohn Baldwin return (error); 770ac468c7SJohn Baldwin } 781058c121SJohn Baldwin 79*8bba2c0fSJohn Baldwin if (dle == NULL) { 80*8bba2c0fSJohn Baldwin error = nvmf_init_dle_from_admin_qp(admin, &cdata, &dle_thunk); 81*8bba2c0fSJohn Baldwin if (error != 0) { 82*8bba2c0fSJohn Baldwin warnc(error, "Failed to generate handoff parameters"); 83*8bba2c0fSJohn Baldwin disconnect_nvm_queues(admin, io, num_io_queues); 84*8bba2c0fSJohn Baldwin free(io); 85*8bba2c0fSJohn Baldwin return (EX_IOERR); 86*8bba2c0fSJohn Baldwin } 87*8bba2c0fSJohn Baldwin dle = &dle_thunk; 88*8bba2c0fSJohn Baldwin } 89*8bba2c0fSJohn Baldwin 90*8bba2c0fSJohn Baldwin error = nvmf_reconnect_host(fd, dle, hostnqn, admin, num_io_queues, io, 91*8bba2c0fSJohn Baldwin &cdata); 921058c121SJohn Baldwin if (error != 0) { 931058c121SJohn Baldwin warnc(error, "Failed to handoff queues to kernel"); 940ac468c7SJohn Baldwin free(io); 951058c121SJohn Baldwin return (EX_IOERR); 961058c121SJohn Baldwin } 971058c121SJohn Baldwin free(io); 981058c121SJohn Baldwin return (0); 991058c121SJohn Baldwin } 1001058c121SJohn Baldwin 101*8bba2c0fSJohn Baldwin static int 102*8bba2c0fSJohn Baldwin reconnect_by_address(int fd, const nvlist_t *rparams, const char *addr) 103*8bba2c0fSJohn Baldwin { 104*8bba2c0fSJohn Baldwin const struct nvme_discovery_log_entry *dle; 105*8bba2c0fSJohn Baldwin struct nvmf_association_params aparams; 106*8bba2c0fSJohn Baldwin enum nvmf_trtype trtype; 107*8bba2c0fSJohn Baldwin const char *address, *hostnqn, *port; 108*8bba2c0fSJohn Baldwin char *subnqn, *tofree; 109*8bba2c0fSJohn Baldwin int error; 110*8bba2c0fSJohn Baldwin 111*8bba2c0fSJohn Baldwin memset(&aparams, 0, sizeof(aparams)); 112*8bba2c0fSJohn Baldwin aparams.sq_flow_control = opt.flow_control; 113*8bba2c0fSJohn Baldwin if (strcasecmp(opt.transport, "tcp") == 0) { 114*8bba2c0fSJohn Baldwin trtype = NVMF_TRTYPE_TCP; 115*8bba2c0fSJohn Baldwin tcp_association_params(&aparams, opt.header_digests, 116*8bba2c0fSJohn Baldwin opt.data_digests); 117*8bba2c0fSJohn Baldwin } else { 118*8bba2c0fSJohn Baldwin warnx("Unsupported or invalid transport"); 119*8bba2c0fSJohn Baldwin return (EX_USAGE); 120*8bba2c0fSJohn Baldwin } 121*8bba2c0fSJohn Baldwin 122*8bba2c0fSJohn Baldwin nvmf_parse_address(addr, &address, &port, &tofree); 123*8bba2c0fSJohn Baldwin if (port == NULL) { 124*8bba2c0fSJohn Baldwin free(tofree); 125*8bba2c0fSJohn Baldwin warnx("Explicit port required"); 126*8bba2c0fSJohn Baldwin return (EX_USAGE); 127*8bba2c0fSJohn Baldwin } 128*8bba2c0fSJohn Baldwin 129*8bba2c0fSJohn Baldwin dle = nvlist_get_binary(rparams, "dle", NULL); 130*8bba2c0fSJohn Baldwin 131*8bba2c0fSJohn Baldwin hostnqn = opt.hostnqn; 132*8bba2c0fSJohn Baldwin if (hostnqn == NULL) 133*8bba2c0fSJohn Baldwin hostnqn = nvmf_default_hostnqn(); 134*8bba2c0fSJohn Baldwin 135*8bba2c0fSJohn Baldwin /* Ensure subnqn is a terminated C string. */ 136*8bba2c0fSJohn Baldwin subnqn = strndup(dle->subnqn, sizeof(dle->subnqn)); 137*8bba2c0fSJohn Baldwin 138*8bba2c0fSJohn Baldwin error = reconnect_nvm_controller(fd, &aparams, trtype, AF_UNSPEC, 139*8bba2c0fSJohn Baldwin address, port, le16toh(dle->cntlid), subnqn, hostnqn, 140*8bba2c0fSJohn Baldwin opt.kato * 1000, opt.num_io_queues, opt.queue_size, NULL); 141*8bba2c0fSJohn Baldwin free(subnqn); 142*8bba2c0fSJohn Baldwin free(tofree); 143*8bba2c0fSJohn Baldwin return (error); 144*8bba2c0fSJohn Baldwin } 145*8bba2c0fSJohn Baldwin 146*8bba2c0fSJohn Baldwin static int 147*8bba2c0fSJohn Baldwin reconnect_by_params(int fd, const nvlist_t *rparams) 148*8bba2c0fSJohn Baldwin { 149*8bba2c0fSJohn Baldwin struct nvmf_association_params aparams; 150*8bba2c0fSJohn Baldwin const struct nvme_discovery_log_entry *dle; 151*8bba2c0fSJohn Baldwin char *address, *port, *subnqn; 152*8bba2c0fSJohn Baldwin int adrfam, error; 153*8bba2c0fSJohn Baldwin 154*8bba2c0fSJohn Baldwin dle = nvlist_get_binary(rparams, "dle", NULL); 155*8bba2c0fSJohn Baldwin 156*8bba2c0fSJohn Baldwin memset(&aparams, 0, sizeof(aparams)); 157*8bba2c0fSJohn Baldwin aparams.sq_flow_control = nvlist_get_bool(rparams, "sq_flow_control"); 158*8bba2c0fSJohn Baldwin switch (dle->trtype) { 159*8bba2c0fSJohn Baldwin case NVMF_TRTYPE_TCP: 160*8bba2c0fSJohn Baldwin switch (dle->adrfam) { 161*8bba2c0fSJohn Baldwin case NVMF_ADRFAM_IPV4: 162*8bba2c0fSJohn Baldwin adrfam = AF_INET; 163*8bba2c0fSJohn Baldwin break; 164*8bba2c0fSJohn Baldwin case NVMF_ADRFAM_IPV6: 165*8bba2c0fSJohn Baldwin adrfam = AF_INET6; 166*8bba2c0fSJohn Baldwin break; 167*8bba2c0fSJohn Baldwin default: 168*8bba2c0fSJohn Baldwin warnx("Unsupported address family"); 169*8bba2c0fSJohn Baldwin return (EX_UNAVAILABLE); 170*8bba2c0fSJohn Baldwin } 171*8bba2c0fSJohn Baldwin switch (dle->tsas.tcp.sectype) { 172*8bba2c0fSJohn Baldwin case NVME_TCP_SECURITY_NONE: 173*8bba2c0fSJohn Baldwin break; 174*8bba2c0fSJohn Baldwin default: 175*8bba2c0fSJohn Baldwin warnx("Unsupported TCP security type"); 176*8bba2c0fSJohn Baldwin return (EX_UNAVAILABLE); 177*8bba2c0fSJohn Baldwin } 178*8bba2c0fSJohn Baldwin break; 179*8bba2c0fSJohn Baldwin 180*8bba2c0fSJohn Baldwin tcp_association_params(&aparams, 181*8bba2c0fSJohn Baldwin nvlist_get_bool(rparams, "header_digests"), 182*8bba2c0fSJohn Baldwin nvlist_get_bool(rparams, "data_digests")); 183*8bba2c0fSJohn Baldwin break; 184*8bba2c0fSJohn Baldwin default: 185*8bba2c0fSJohn Baldwin warnx("Unsupported transport %s", 186*8bba2c0fSJohn Baldwin nvmf_transport_type(dle->trtype)); 187*8bba2c0fSJohn Baldwin return (EX_UNAVAILABLE); 188*8bba2c0fSJohn Baldwin } 189*8bba2c0fSJohn Baldwin 190*8bba2c0fSJohn Baldwin /* Ensure address, port, and subnqn is a terminated C string. */ 191*8bba2c0fSJohn Baldwin address = strndup(dle->traddr, sizeof(dle->traddr)); 192*8bba2c0fSJohn Baldwin port = strndup(dle->trsvcid, sizeof(dle->trsvcid)); 193*8bba2c0fSJohn Baldwin subnqn = strndup(dle->subnqn, sizeof(dle->subnqn)); 194*8bba2c0fSJohn Baldwin 195*8bba2c0fSJohn Baldwin error = reconnect_nvm_controller(fd, &aparams, dle->trtype, adrfam, 196*8bba2c0fSJohn Baldwin address, port, le16toh(dle->cntlid), dle->subnqn, 197*8bba2c0fSJohn Baldwin nvlist_get_string(rparams, "hostnqn"), 198*8bba2c0fSJohn Baldwin dnvlist_get_number(rparams, "kato", 0), 199*8bba2c0fSJohn Baldwin nvlist_get_number(rparams, "num_io_queues"), 200*8bba2c0fSJohn Baldwin nvlist_get_number(rparams, "io_qsize"), dle); 201*8bba2c0fSJohn Baldwin free(subnqn); 202*8bba2c0fSJohn Baldwin free(port); 203*8bba2c0fSJohn Baldwin free(address); 204*8bba2c0fSJohn Baldwin return (error); 205*8bba2c0fSJohn Baldwin } 206*8bba2c0fSJohn Baldwin 207*8bba2c0fSJohn Baldwin static int 208*8bba2c0fSJohn Baldwin fetch_and_validate_rparams(int fd, nvlist_t **rparamsp) 209*8bba2c0fSJohn Baldwin { 210*8bba2c0fSJohn Baldwin const struct nvme_discovery_log_entry *dle; 211*8bba2c0fSJohn Baldwin nvlist_t *rparams; 212*8bba2c0fSJohn Baldwin size_t len; 213*8bba2c0fSJohn Baldwin int error; 214*8bba2c0fSJohn Baldwin 215*8bba2c0fSJohn Baldwin error = nvmf_reconnect_params(fd, &rparams); 216*8bba2c0fSJohn Baldwin if (error != 0) { 217*8bba2c0fSJohn Baldwin warnc(error, "Failed to fetch reconnect parameters"); 218*8bba2c0fSJohn Baldwin return (EX_IOERR); 219*8bba2c0fSJohn Baldwin } 220*8bba2c0fSJohn Baldwin 221*8bba2c0fSJohn Baldwin if (!nvlist_exists_binary(rparams, "dle") || 222*8bba2c0fSJohn Baldwin !nvlist_exists_string(rparams, "hostnqn") || 223*8bba2c0fSJohn Baldwin !nvlist_exists_number(rparams, "num_io_queues") || 224*8bba2c0fSJohn Baldwin !nvlist_exists_number(rparams, "io_qsize") || 225*8bba2c0fSJohn Baldwin !nvlist_exists_bool(rparams, "sq_flow_control")) { 226*8bba2c0fSJohn Baldwin nvlist_destroy(rparams); 227*8bba2c0fSJohn Baldwin warnx("Missing required reconnect parameters"); 228*8bba2c0fSJohn Baldwin return (EX_IOERR); 229*8bba2c0fSJohn Baldwin } 230*8bba2c0fSJohn Baldwin 231*8bba2c0fSJohn Baldwin dle = nvlist_get_binary(rparams, "dle", &len); 232*8bba2c0fSJohn Baldwin if (len != sizeof(*dle)) { 233*8bba2c0fSJohn Baldwin nvlist_destroy(rparams); 234*8bba2c0fSJohn Baldwin warnx("Discovery Log entry reconnect parameter is wrong size"); 235*8bba2c0fSJohn Baldwin return (EX_IOERR); 236*8bba2c0fSJohn Baldwin } 237*8bba2c0fSJohn Baldwin 238*8bba2c0fSJohn Baldwin switch (dle->trtype) { 239*8bba2c0fSJohn Baldwin case NVMF_TRTYPE_TCP: 240*8bba2c0fSJohn Baldwin if (!nvlist_exists_bool(rparams, "header_digests") || 241*8bba2c0fSJohn Baldwin !nvlist_exists_bool(rparams, "data_digests")) { 242*8bba2c0fSJohn Baldwin nvlist_destroy(rparams); 243*8bba2c0fSJohn Baldwin warnx("Missing required reconnect parameters"); 244*8bba2c0fSJohn Baldwin return (EX_IOERR); 245*8bba2c0fSJohn Baldwin } 246*8bba2c0fSJohn Baldwin break; 247*8bba2c0fSJohn Baldwin default: 248*8bba2c0fSJohn Baldwin nvlist_destroy(rparams); 249*8bba2c0fSJohn Baldwin warnx("Unsupported transport %s", 250*8bba2c0fSJohn Baldwin nvmf_transport_type(dle->trtype)); 251*8bba2c0fSJohn Baldwin return (EX_UNAVAILABLE); 252*8bba2c0fSJohn Baldwin } 253*8bba2c0fSJohn Baldwin 254*8bba2c0fSJohn Baldwin *rparamsp = rparams; 255*8bba2c0fSJohn Baldwin return (0); 256*8bba2c0fSJohn Baldwin } 257*8bba2c0fSJohn Baldwin 2581058c121SJohn Baldwin static void 2591058c121SJohn Baldwin reconnect_fn(const struct cmd *f, int argc, char *argv[]) 2601058c121SJohn Baldwin { 261*8bba2c0fSJohn Baldwin nvlist_t *rparams; 2621058c121SJohn Baldwin int error, fd; 2631058c121SJohn Baldwin 2641058c121SJohn Baldwin if (arg_parse(argc, argv, f)) 2651058c121SJohn Baldwin return; 2661058c121SJohn Baldwin 2671058c121SJohn Baldwin open_dev(opt.dev, &fd, 1, 1); 268*8bba2c0fSJohn Baldwin error = fetch_and_validate_rparams(fd, &rparams); 2691058c121SJohn Baldwin if (error != 0) 2701058c121SJohn Baldwin exit(error); 2711058c121SJohn Baldwin 272*8bba2c0fSJohn Baldwin /* Check for optional address. */ 273*8bba2c0fSJohn Baldwin if (optind < argc) 274*8bba2c0fSJohn Baldwin error = reconnect_by_address(fd, rparams, argv[optind]); 275*8bba2c0fSJohn Baldwin else 276*8bba2c0fSJohn Baldwin error = reconnect_by_params(fd, rparams); 277*8bba2c0fSJohn Baldwin if (error != 0) 278*8bba2c0fSJohn Baldwin exit(error); 279*8bba2c0fSJohn Baldwin 280*8bba2c0fSJohn Baldwin nvlist_destroy(rparams); 2811058c121SJohn Baldwin close(fd); 2821058c121SJohn Baldwin } 2831058c121SJohn Baldwin 2841058c121SJohn Baldwin static const struct opts reconnect_opts[] = { 2851058c121SJohn Baldwin #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } 2861058c121SJohn Baldwin OPT("transport", 't', arg_string, opt, transport, 2871058c121SJohn Baldwin "Transport type"), 2881058c121SJohn Baldwin OPT("nr-io-queues", 'i', arg_uint16, opt, num_io_queues, 2891058c121SJohn Baldwin "Number of I/O queues"), 2901058c121SJohn Baldwin OPT("queue-size", 'Q', arg_uint16, opt, queue_size, 2911058c121SJohn Baldwin "Number of entries in each I/O queue"), 2921058c121SJohn Baldwin OPT("keep-alive-tmo", 'k', arg_uint32, opt, kato, 2931058c121SJohn Baldwin "Keep Alive timeout (in seconds)"), 2941058c121SJohn Baldwin OPT("hostnqn", 'q', arg_string, opt, hostnqn, 2951058c121SJohn Baldwin "Host NQN"), 2961058c121SJohn Baldwin OPT("flow_control", 'F', arg_none, opt, flow_control, 2971058c121SJohn Baldwin "Request SQ flow control"), 2981058c121SJohn Baldwin OPT("hdr_digests", 'g', arg_none, opt, header_digests, 2991058c121SJohn Baldwin "Enable TCP PDU header digests"), 3001058c121SJohn Baldwin OPT("data_digests", 'G', arg_none, opt, data_digests, 3011058c121SJohn Baldwin "Enable TCP PDU data digests"), 3021058c121SJohn Baldwin { NULL, 0, arg_none, NULL, NULL } 3031058c121SJohn Baldwin }; 3041058c121SJohn Baldwin #undef OPT 3051058c121SJohn Baldwin 3061058c121SJohn Baldwin static const struct args reconnect_args[] = { 3071058c121SJohn Baldwin { arg_string, &opt.dev, "controller-id" }, 3081058c121SJohn Baldwin { arg_none, NULL, NULL }, 3091058c121SJohn Baldwin }; 3101058c121SJohn Baldwin 3111058c121SJohn Baldwin static struct cmd reconnect_cmd = { 3121058c121SJohn Baldwin .name = "reconnect", 3131058c121SJohn Baldwin .fn = reconnect_fn, 3141058c121SJohn Baldwin .descr = "Reconnect to a fabrics controller", 3151058c121SJohn Baldwin .ctx_size = sizeof(opt), 3161058c121SJohn Baldwin .opts = reconnect_opts, 3171058c121SJohn Baldwin .args = reconnect_args, 3181058c121SJohn Baldwin }; 3191058c121SJohn Baldwin 3201058c121SJohn Baldwin CMD_COMMAND(reconnect_cmd); 321