1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2023-2024 Chelsio Communications, Inc. 5 * Written by: John Baldwin <jhb@FreeBSD.org> 6 */ 7 8 #include <sys/nv.h> 9 #include <sys/socket.h> 10 #include <err.h> 11 #include <libnvmf.h> 12 #include <stdlib.h> 13 #include <string.h> 14 #include <sysexits.h> 15 #include <unistd.h> 16 17 #include "nvmecontrol.h" 18 #include "fabrics.h" 19 20 /* 21 * See comment about other possible settings in connect.c. 22 */ 23 24 static struct options { 25 const char *dev; 26 const char *transport; 27 const char *address; 28 const char *hostnqn; 29 uint32_t kato; 30 uint16_t num_io_queues; 31 uint16_t queue_size; 32 bool data_digests; 33 bool flow_control; 34 bool header_digests; 35 } opt = { 36 .dev = NULL, 37 .transport = "tcp", 38 .address = NULL, 39 .hostnqn = NULL, 40 .kato = NVMF_KATO_DEFAULT / 1000, 41 .num_io_queues = 1, 42 .queue_size = 0, 43 .data_digests = false, 44 .flow_control = false, 45 .header_digests = false, 46 }; 47 48 static void 49 tcp_association_params(struct nvmf_association_params *params) 50 { 51 params->tcp.pda = 0; 52 params->tcp.header_digests = opt.header_digests; 53 params->tcp.data_digests = opt.data_digests; 54 /* XXX */ 55 params->tcp.maxr2t = 1; 56 } 57 58 static int 59 reconnect_nvm_controller(int fd, enum nvmf_trtype trtype, int adrfam, 60 const char *address, const char *port) 61 { 62 struct nvme_controller_data cdata; 63 struct nvmf_association_params aparams; 64 nvlist_t *rparams; 65 struct nvmf_qpair *admin, **io; 66 int error; 67 68 error = nvmf_reconnect_params(fd, &rparams); 69 if (error != 0) { 70 warnc(error, "Failed to fetch reconnect parameters"); 71 return (EX_IOERR); 72 } 73 74 if (!nvlist_exists_number(rparams, "cntlid") || 75 !nvlist_exists_string(rparams, "subnqn")) { 76 nvlist_destroy(rparams); 77 warnx("Missing required reconnect parameters"); 78 return (EX_IOERR); 79 } 80 81 memset(&aparams, 0, sizeof(aparams)); 82 aparams.sq_flow_control = opt.flow_control; 83 switch (trtype) { 84 case NVMF_TRTYPE_TCP: 85 tcp_association_params(&aparams); 86 break; 87 default: 88 nvlist_destroy(rparams); 89 warnx("Unsupported transport %s", nvmf_transport_type(trtype)); 90 return (EX_UNAVAILABLE); 91 } 92 93 io = calloc(opt.num_io_queues, sizeof(*io)); 94 error = connect_nvm_queues(&aparams, trtype, adrfam, address, port, 95 nvlist_get_number(rparams, "cntlid"), 96 nvlist_get_string(rparams, "subnqn"), opt.hostnqn, opt.kato, 97 &admin, io, opt.num_io_queues, opt.queue_size, &cdata); 98 if (error != 0) { 99 free(io); 100 nvlist_destroy(rparams); 101 return (error); 102 } 103 nvlist_destroy(rparams); 104 105 error = nvmf_reconnect_host(fd, admin, opt.num_io_queues, io, &cdata); 106 if (error != 0) { 107 warnc(error, "Failed to handoff queues to kernel"); 108 free(io); 109 return (EX_IOERR); 110 } 111 free(io); 112 return (0); 113 } 114 115 static void 116 reconnect_fn(const struct cmd *f, int argc, char *argv[]) 117 { 118 enum nvmf_trtype trtype; 119 const char *address, *port; 120 char *tofree; 121 int error, fd; 122 123 if (arg_parse(argc, argv, f)) 124 return; 125 126 if (strcasecmp(opt.transport, "tcp") == 0) { 127 trtype = NVMF_TRTYPE_TCP; 128 } else 129 errx(EX_USAGE, "Unsupported or invalid transport"); 130 131 nvmf_parse_address(opt.address, &address, &port, &tofree); 132 133 open_dev(opt.dev, &fd, 1, 1); 134 if (port == NULL) 135 errx(EX_USAGE, "Explicit port required"); 136 137 error = reconnect_nvm_controller(fd, trtype, AF_UNSPEC, address, port); 138 if (error != 0) 139 exit(error); 140 141 close(fd); 142 free(tofree); 143 } 144 145 static const struct opts reconnect_opts[] = { 146 #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } 147 OPT("transport", 't', arg_string, opt, transport, 148 "Transport type"), 149 OPT("nr-io-queues", 'i', arg_uint16, opt, num_io_queues, 150 "Number of I/O queues"), 151 OPT("queue-size", 'Q', arg_uint16, opt, queue_size, 152 "Number of entries in each I/O queue"), 153 OPT("keep-alive-tmo", 'k', arg_uint32, opt, kato, 154 "Keep Alive timeout (in seconds)"), 155 OPT("hostnqn", 'q', arg_string, opt, hostnqn, 156 "Host NQN"), 157 OPT("flow_control", 'F', arg_none, opt, flow_control, 158 "Request SQ flow control"), 159 OPT("hdr_digests", 'g', arg_none, opt, header_digests, 160 "Enable TCP PDU header digests"), 161 OPT("data_digests", 'G', arg_none, opt, data_digests, 162 "Enable TCP PDU data digests"), 163 { NULL, 0, arg_none, NULL, NULL } 164 }; 165 #undef OPT 166 167 static const struct args reconnect_args[] = { 168 { arg_string, &opt.dev, "controller-id" }, 169 { arg_string, &opt.address, "address" }, 170 { arg_none, NULL, NULL }, 171 }; 172 173 static struct cmd reconnect_cmd = { 174 .name = "reconnect", 175 .fn = reconnect_fn, 176 .descr = "Reconnect to a fabrics controller", 177 .ctx_size = sizeof(opt), 178 .opts = reconnect_opts, 179 .args = reconnect_args, 180 }; 181 182 CMD_COMMAND(reconnect_cmd); 183