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 81058c121SJohn Baldwin #include <sys/socket.h> 91058c121SJohn Baldwin #include <err.h> 101058c121SJohn Baldwin #include <libnvmf.h> 111058c121SJohn Baldwin #include <stdlib.h> 121058c121SJohn Baldwin #include <string.h> 131058c121SJohn Baldwin #include <sysexits.h> 141058c121SJohn Baldwin #include <unistd.h> 151058c121SJohn Baldwin 161058c121SJohn Baldwin #include "comnd.h" 171058c121SJohn Baldwin #include "fabrics.h" 181058c121SJohn Baldwin 191058c121SJohn Baldwin /* 201058c121SJohn Baldwin * Settings that are currently hardcoded but could be exposed to the 211058c121SJohn Baldwin * user via additional command line options: 221058c121SJohn Baldwin * 231058c121SJohn Baldwin * - ADMIN queue entries 241058c121SJohn Baldwin * - MaxR2T 251058c121SJohn Baldwin */ 261058c121SJohn Baldwin 271058c121SJohn Baldwin static struct options { 281058c121SJohn Baldwin const char *transport; 291058c121SJohn Baldwin const char *address; 301058c121SJohn Baldwin const char *cntlid; 311058c121SJohn Baldwin const char *subnqn; 321058c121SJohn Baldwin const char *hostnqn; 331058c121SJohn Baldwin uint32_t kato; 341058c121SJohn Baldwin uint16_t num_io_queues; 351058c121SJohn Baldwin uint16_t queue_size; 361058c121SJohn Baldwin bool data_digests; 371058c121SJohn Baldwin bool flow_control; 381058c121SJohn Baldwin bool header_digests; 391058c121SJohn Baldwin } opt = { 401058c121SJohn Baldwin .transport = "tcp", 411058c121SJohn Baldwin .address = NULL, 421058c121SJohn Baldwin .cntlid = "dynamic", 431058c121SJohn Baldwin .subnqn = NULL, 441058c121SJohn Baldwin .hostnqn = NULL, 451058c121SJohn Baldwin .kato = NVMF_KATO_DEFAULT / 1000, 461058c121SJohn Baldwin .num_io_queues = 1, 471058c121SJohn Baldwin .queue_size = 0, 481058c121SJohn Baldwin .data_digests = false, 491058c121SJohn Baldwin .flow_control = false, 501058c121SJohn Baldwin .header_digests = false, 511058c121SJohn Baldwin }; 521058c121SJohn Baldwin 531058c121SJohn Baldwin static void 541058c121SJohn Baldwin tcp_association_params(struct nvmf_association_params *params) 551058c121SJohn Baldwin { 561058c121SJohn Baldwin params->tcp.pda = 0; 571058c121SJohn Baldwin params->tcp.header_digests = opt.header_digests; 581058c121SJohn Baldwin params->tcp.data_digests = opt.data_digests; 591058c121SJohn Baldwin /* XXX */ 601058c121SJohn Baldwin params->tcp.maxr2t = 1; 611058c121SJohn Baldwin } 621058c121SJohn Baldwin 631058c121SJohn Baldwin static int 641058c121SJohn Baldwin connect_nvm_controller(enum nvmf_trtype trtype, int adrfam, const char *address, 65*8bba2c0fSJohn Baldwin const char *port, uint16_t cntlid, const char *subnqn, 66*8bba2c0fSJohn Baldwin const struct nvme_discovery_log_entry *dle) 671058c121SJohn Baldwin { 681058c121SJohn Baldwin struct nvme_controller_data cdata; 69*8bba2c0fSJohn Baldwin struct nvme_discovery_log_entry dle_thunk; 701058c121SJohn Baldwin struct nvmf_association_params aparams; 711058c121SJohn Baldwin struct nvmf_qpair *admin, **io; 72*8bba2c0fSJohn Baldwin const char *hostnqn; 731058c121SJohn Baldwin int error; 741058c121SJohn Baldwin 751058c121SJohn Baldwin memset(&aparams, 0, sizeof(aparams)); 761058c121SJohn Baldwin aparams.sq_flow_control = opt.flow_control; 771058c121SJohn Baldwin switch (trtype) { 781058c121SJohn Baldwin case NVMF_TRTYPE_TCP: 791058c121SJohn Baldwin tcp_association_params(&aparams); 801058c121SJohn Baldwin break; 811058c121SJohn Baldwin default: 821058c121SJohn Baldwin warnx("Unsupported transport %s", nvmf_transport_type(trtype)); 831058c121SJohn Baldwin return (EX_UNAVAILABLE); 841058c121SJohn Baldwin } 851058c121SJohn Baldwin 86*8bba2c0fSJohn Baldwin hostnqn = opt.hostnqn; 87*8bba2c0fSJohn Baldwin if (hostnqn == NULL) 88*8bba2c0fSJohn Baldwin hostnqn = nvmf_default_hostnqn(); 891058c121SJohn Baldwin io = calloc(opt.num_io_queues, sizeof(*io)); 901058c121SJohn Baldwin error = connect_nvm_queues(&aparams, trtype, adrfam, address, port, 91*8bba2c0fSJohn Baldwin cntlid, subnqn, hostnqn, opt.kato * 1000, &admin, io, 921058c121SJohn Baldwin opt.num_io_queues, opt.queue_size, &cdata); 930ac468c7SJohn Baldwin if (error != 0) { 940ac468c7SJohn Baldwin free(io); 951058c121SJohn Baldwin return (error); 960ac468c7SJohn Baldwin } 971058c121SJohn Baldwin 98*8bba2c0fSJohn Baldwin if (dle == NULL) { 99*8bba2c0fSJohn Baldwin error = nvmf_init_dle_from_admin_qp(admin, &cdata, &dle_thunk); 100*8bba2c0fSJohn Baldwin if (error != 0) { 101*8bba2c0fSJohn Baldwin warnc(error, "Failed to generate handoff parameters"); 102*8bba2c0fSJohn Baldwin disconnect_nvm_queues(admin, io, opt.num_io_queues); 103*8bba2c0fSJohn Baldwin free(io); 104*8bba2c0fSJohn Baldwin return (EX_IOERR); 105*8bba2c0fSJohn Baldwin } 106*8bba2c0fSJohn Baldwin dle = &dle_thunk; 107*8bba2c0fSJohn Baldwin } 108*8bba2c0fSJohn Baldwin 109*8bba2c0fSJohn Baldwin error = nvmf_handoff_host(dle, hostnqn, admin, opt.num_io_queues, io, 110*8bba2c0fSJohn Baldwin &cdata); 1111058c121SJohn Baldwin if (error != 0) { 1121058c121SJohn Baldwin warnc(error, "Failed to handoff queues to kernel"); 1130ac468c7SJohn Baldwin free(io); 1141058c121SJohn Baldwin return (EX_IOERR); 1151058c121SJohn Baldwin } 1161058c121SJohn Baldwin free(io); 1171058c121SJohn Baldwin return (0); 1181058c121SJohn Baldwin } 1191058c121SJohn Baldwin 1201058c121SJohn Baldwin static void 1211058c121SJohn Baldwin connect_discovery_entry(struct nvme_discovery_log_entry *entry) 1221058c121SJohn Baldwin { 1231058c121SJohn Baldwin int adrfam; 1241058c121SJohn Baldwin 1251058c121SJohn Baldwin switch (entry->trtype) { 1261058c121SJohn Baldwin case NVMF_TRTYPE_TCP: 1271058c121SJohn Baldwin switch (entry->adrfam) { 1281058c121SJohn Baldwin case NVMF_ADRFAM_IPV4: 1291058c121SJohn Baldwin adrfam = AF_INET; 1301058c121SJohn Baldwin break; 1311058c121SJohn Baldwin case NVMF_ADRFAM_IPV6: 1321058c121SJohn Baldwin adrfam = AF_INET6; 1331058c121SJohn Baldwin break; 1341058c121SJohn Baldwin default: 1351058c121SJohn Baldwin warnx("Skipping unsupported address family for %s", 1361058c121SJohn Baldwin entry->subnqn); 1371058c121SJohn Baldwin return; 1381058c121SJohn Baldwin } 1391058c121SJohn Baldwin switch (entry->tsas.tcp.sectype) { 1401058c121SJohn Baldwin case NVME_TCP_SECURITY_NONE: 1411058c121SJohn Baldwin break; 1421058c121SJohn Baldwin default: 1431058c121SJohn Baldwin warnx("Skipping unsupported TCP security type for %s", 1441058c121SJohn Baldwin entry->subnqn); 1451058c121SJohn Baldwin return; 1461058c121SJohn Baldwin } 1471058c121SJohn Baldwin break; 1481058c121SJohn Baldwin default: 1491058c121SJohn Baldwin warnx("Skipping unsupported transport %s for %s", 1501058c121SJohn Baldwin nvmf_transport_type(entry->trtype), entry->subnqn); 1511058c121SJohn Baldwin return; 1521058c121SJohn Baldwin } 1531058c121SJohn Baldwin 1541058c121SJohn Baldwin /* 1551058c121SJohn Baldwin * XXX: Track portids and avoid duplicate connections for a 1561058c121SJohn Baldwin * given (subnqn,portid)? 1571058c121SJohn Baldwin */ 1581058c121SJohn Baldwin 1591058c121SJohn Baldwin /* XXX: Should this make use of entry->aqsz in some way? */ 1601058c121SJohn Baldwin connect_nvm_controller(entry->trtype, adrfam, entry->traddr, 161*8bba2c0fSJohn Baldwin entry->trsvcid, entry->cntlid, entry->subnqn, entry); 1621058c121SJohn Baldwin } 1631058c121SJohn Baldwin 1641058c121SJohn Baldwin static void 1651058c121SJohn Baldwin connect_discovery_log_page(struct nvmf_qpair *qp) 1661058c121SJohn Baldwin { 1671058c121SJohn Baldwin struct nvme_discovery_log *log; 1681058c121SJohn Baldwin int error; 1691058c121SJohn Baldwin 1701058c121SJohn Baldwin error = nvmf_host_fetch_discovery_log_page(qp, &log); 1711058c121SJohn Baldwin if (error != 0) 1721058c121SJohn Baldwin errc(EX_IOERR, error, "Failed to fetch discovery log page"); 1731058c121SJohn Baldwin 1741058c121SJohn Baldwin for (u_int i = 0; i < log->numrec; i++) 1751058c121SJohn Baldwin connect_discovery_entry(&log->entries[i]); 1761058c121SJohn Baldwin free(log); 1771058c121SJohn Baldwin } 1781058c121SJohn Baldwin 1791058c121SJohn Baldwin static void 1801058c121SJohn Baldwin discover_controllers(enum nvmf_trtype trtype, const char *address, 1811058c121SJohn Baldwin const char *port) 1821058c121SJohn Baldwin { 1831058c121SJohn Baldwin struct nvmf_qpair *qp; 1841058c121SJohn Baldwin 1851058c121SJohn Baldwin qp = connect_discovery_adminq(trtype, address, port, opt.hostnqn); 1861058c121SJohn Baldwin 1871058c121SJohn Baldwin connect_discovery_log_page(qp); 1881058c121SJohn Baldwin 1891058c121SJohn Baldwin nvmf_free_qpair(qp); 1901058c121SJohn Baldwin } 1911058c121SJohn Baldwin 1921058c121SJohn Baldwin static void 1931058c121SJohn Baldwin connect_fn(const struct cmd *f, int argc, char *argv[]) 1941058c121SJohn Baldwin { 1951058c121SJohn Baldwin enum nvmf_trtype trtype; 1961058c121SJohn Baldwin const char *address, *port; 1971058c121SJohn Baldwin char *tofree; 1981058c121SJohn Baldwin u_long cntlid; 1991058c121SJohn Baldwin int error; 2001058c121SJohn Baldwin 2011058c121SJohn Baldwin if (arg_parse(argc, argv, f)) 2021058c121SJohn Baldwin return; 2031058c121SJohn Baldwin 2041058c121SJohn Baldwin if (opt.num_io_queues <= 0) 2051058c121SJohn Baldwin errx(EX_USAGE, "Invalid number of I/O queues"); 2061058c121SJohn Baldwin 2071058c121SJohn Baldwin if (strcasecmp(opt.transport, "tcp") == 0) { 2081058c121SJohn Baldwin trtype = NVMF_TRTYPE_TCP; 2091058c121SJohn Baldwin } else 2101058c121SJohn Baldwin errx(EX_USAGE, "Unsupported or invalid transport"); 2111058c121SJohn Baldwin 2121058c121SJohn Baldwin nvmf_parse_address(opt.address, &address, &port, &tofree); 2131058c121SJohn Baldwin if (port == NULL) 2141058c121SJohn Baldwin errx(EX_USAGE, "Explicit port required"); 2151058c121SJohn Baldwin 2161058c121SJohn Baldwin cntlid = nvmf_parse_cntlid(opt.cntlid); 2171058c121SJohn Baldwin 2181058c121SJohn Baldwin error = connect_nvm_controller(trtype, AF_UNSPEC, address, port, cntlid, 219*8bba2c0fSJohn Baldwin opt.subnqn, NULL); 2201058c121SJohn Baldwin if (error != 0) 2211058c121SJohn Baldwin exit(error); 2221058c121SJohn Baldwin 2231058c121SJohn Baldwin free(tofree); 2241058c121SJohn Baldwin } 2251058c121SJohn Baldwin 2261058c121SJohn Baldwin static void 2271058c121SJohn Baldwin connect_all_fn(const struct cmd *f, int argc, char *argv[]) 2281058c121SJohn Baldwin { 2291058c121SJohn Baldwin enum nvmf_trtype trtype; 2301058c121SJohn Baldwin const char *address, *port; 2311058c121SJohn Baldwin char *tofree; 2321058c121SJohn Baldwin 2331058c121SJohn Baldwin if (arg_parse(argc, argv, f)) 2341058c121SJohn Baldwin return; 2351058c121SJohn Baldwin 2361058c121SJohn Baldwin if (opt.num_io_queues <= 0) 2371058c121SJohn Baldwin errx(EX_USAGE, "Invalid number of I/O queues"); 2381058c121SJohn Baldwin 2391058c121SJohn Baldwin if (strcasecmp(opt.transport, "tcp") == 0) { 2401058c121SJohn Baldwin trtype = NVMF_TRTYPE_TCP; 2411058c121SJohn Baldwin } else 2421058c121SJohn Baldwin errx(EX_USAGE, "Unsupported or invalid transport"); 2431058c121SJohn Baldwin 2441058c121SJohn Baldwin nvmf_parse_address(opt.address, &address, &port, &tofree); 2451058c121SJohn Baldwin discover_controllers(trtype, address, port); 2461058c121SJohn Baldwin 2471058c121SJohn Baldwin free(tofree); 2481058c121SJohn Baldwin } 2491058c121SJohn Baldwin 2501058c121SJohn Baldwin static const struct opts connect_opts[] = { 2511058c121SJohn Baldwin #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } 2521058c121SJohn Baldwin OPT("transport", 't', arg_string, opt, transport, 2531058c121SJohn Baldwin "Transport type"), 2541058c121SJohn Baldwin OPT("cntlid", 'c', arg_string, opt, cntlid, 2551058c121SJohn Baldwin "Controller ID"), 2561058c121SJohn Baldwin OPT("nr-io-queues", 'i', arg_uint16, opt, num_io_queues, 2571058c121SJohn Baldwin "Number of I/O queues"), 2581058c121SJohn Baldwin OPT("queue-size", 'Q', arg_uint16, opt, queue_size, 2591058c121SJohn Baldwin "Number of entries in each I/O queue"), 2601058c121SJohn Baldwin OPT("keep-alive-tmo", 'k', arg_uint32, opt, kato, 2611058c121SJohn Baldwin "Keep Alive timeout (in seconds)"), 2621058c121SJohn Baldwin OPT("hostnqn", 'q', arg_string, opt, hostnqn, 2631058c121SJohn Baldwin "Host NQN"), 2641058c121SJohn Baldwin OPT("flow_control", 'F', arg_none, opt, flow_control, 2651058c121SJohn Baldwin "Request SQ flow control"), 2661058c121SJohn Baldwin OPT("hdr_digests", 'g', arg_none, opt, header_digests, 2671058c121SJohn Baldwin "Enable TCP PDU header digests"), 2681058c121SJohn Baldwin OPT("data_digests", 'G', arg_none, opt, data_digests, 2691058c121SJohn Baldwin "Enable TCP PDU data digests"), 2701058c121SJohn Baldwin { NULL, 0, arg_none, NULL, NULL } 2711058c121SJohn Baldwin }; 2721058c121SJohn Baldwin #undef OPT 2731058c121SJohn Baldwin 2741058c121SJohn Baldwin static const struct args connect_args[] = { 2751058c121SJohn Baldwin { arg_string, &opt.address, "address" }, 2761058c121SJohn Baldwin { arg_string, &opt.subnqn, "SubNQN" }, 2771058c121SJohn Baldwin { arg_none, NULL, NULL }, 2781058c121SJohn Baldwin }; 2791058c121SJohn Baldwin 2801058c121SJohn Baldwin static const struct args connect_all_args[] = { 2811058c121SJohn Baldwin { arg_string, &opt.address, "address" }, 2821058c121SJohn Baldwin { arg_none, NULL, NULL }, 2831058c121SJohn Baldwin }; 2841058c121SJohn Baldwin 2851058c121SJohn Baldwin static struct cmd connect_cmd = { 2861058c121SJohn Baldwin .name = "connect", 2871058c121SJohn Baldwin .fn = connect_fn, 2881058c121SJohn Baldwin .descr = "Connect to a fabrics controller", 2891058c121SJohn Baldwin .ctx_size = sizeof(opt), 2901058c121SJohn Baldwin .opts = connect_opts, 2911058c121SJohn Baldwin .args = connect_args, 2921058c121SJohn Baldwin }; 2931058c121SJohn Baldwin 2941058c121SJohn Baldwin static struct cmd connect_all_cmd = { 2951058c121SJohn Baldwin .name = "connect-all", 2961058c121SJohn Baldwin .fn = connect_all_fn, 2971058c121SJohn Baldwin .descr = "Discover and connect to fabrics controllers", 2981058c121SJohn Baldwin .ctx_size = sizeof(opt), 2991058c121SJohn Baldwin .opts = connect_opts, 3001058c121SJohn Baldwin .args = connect_all_args, 3011058c121SJohn Baldwin }; 3021058c121SJohn Baldwin 3031058c121SJohn Baldwin CMD_COMMAND(connect_cmd); 3041058c121SJohn Baldwin CMD_COMMAND(connect_all_cmd); 305