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/dnv.h> 9 #include <sys/nv.h> 10 #include <sys/socket.h> 11 #include <err.h> 12 #include <libnvmf.h> 13 #include <stdlib.h> 14 #include <string.h> 15 #include <sysexits.h> 16 #include <unistd.h> 17 18 #include "nvmecontrol.h" 19 #include "fabrics.h" 20 21 /* 22 * See comment about other possible settings in connect.c. 23 */ 24 25 static struct options { 26 const char *dev; 27 const char *transport; 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 .hostnqn = NULL, 39 .kato = NVMF_KATO_DEFAULT / 1000, 40 .num_io_queues = 1, 41 .queue_size = 0, 42 .data_digests = false, 43 .flow_control = false, 44 .header_digests = false, 45 }; 46 47 static void 48 tcp_association_params(struct nvmf_association_params *params, 49 bool header_digests, bool data_digests) 50 { 51 params->tcp.pda = 0; 52 params->tcp.header_digests = header_digests; 53 params->tcp.data_digests = data_digests; 54 /* XXX */ 55 params->tcp.maxr2t = 1; 56 } 57 58 static int 59 reconnect_nvm_controller(int fd, const struct nvmf_association_params *aparams, 60 enum nvmf_trtype trtype, int adrfam, const char *address, const char *port, 61 uint16_t cntlid, const char *subnqn, const char *hostnqn, uint32_t kato, 62 u_int num_io_queues, u_int queue_size, 63 const struct nvme_discovery_log_entry *dle) 64 { 65 struct nvme_controller_data cdata; 66 struct nvme_discovery_log_entry dle_thunk; 67 struct nvmf_qpair *admin, **io; 68 int error; 69 70 io = calloc(num_io_queues, sizeof(*io)); 71 error = connect_nvm_queues(aparams, trtype, adrfam, address, port, 72 cntlid, subnqn, hostnqn, kato, &admin, io, num_io_queues, 73 queue_size, &cdata); 74 if (error != 0) { 75 free(io); 76 return (error); 77 } 78 79 if (dle == NULL) { 80 error = nvmf_init_dle_from_admin_qp(admin, &cdata, &dle_thunk); 81 if (error != 0) { 82 warnc(error, "Failed to generate handoff parameters"); 83 disconnect_nvm_queues(admin, io, num_io_queues); 84 free(io); 85 return (EX_IOERR); 86 } 87 dle = &dle_thunk; 88 } 89 90 error = nvmf_reconnect_host(fd, dle, hostnqn, admin, num_io_queues, io, 91 &cdata); 92 if (error != 0) { 93 warnc(error, "Failed to handoff queues to kernel"); 94 free(io); 95 return (EX_IOERR); 96 } 97 free(io); 98 return (0); 99 } 100 101 static int 102 reconnect_by_address(int fd, const nvlist_t *rparams, const char *addr) 103 { 104 const struct nvme_discovery_log_entry *dle; 105 struct nvmf_association_params aparams; 106 enum nvmf_trtype trtype; 107 const char *address, *hostnqn, *port; 108 char *subnqn, *tofree; 109 int error; 110 111 memset(&aparams, 0, sizeof(aparams)); 112 aparams.sq_flow_control = opt.flow_control; 113 if (strcasecmp(opt.transport, "tcp") == 0) { 114 trtype = NVMF_TRTYPE_TCP; 115 tcp_association_params(&aparams, opt.header_digests, 116 opt.data_digests); 117 } else { 118 warnx("Unsupported or invalid transport"); 119 return (EX_USAGE); 120 } 121 122 nvmf_parse_address(addr, &address, &port, &tofree); 123 if (port == NULL) { 124 free(tofree); 125 warnx("Explicit port required"); 126 return (EX_USAGE); 127 } 128 129 dle = nvlist_get_binary(rparams, "dle", NULL); 130 131 hostnqn = opt.hostnqn; 132 if (hostnqn == NULL) 133 hostnqn = nvmf_default_hostnqn(); 134 135 /* Ensure subnqn is a terminated C string. */ 136 subnqn = strndup(dle->subnqn, sizeof(dle->subnqn)); 137 138 error = reconnect_nvm_controller(fd, &aparams, trtype, AF_UNSPEC, 139 address, port, le16toh(dle->cntlid), subnqn, hostnqn, 140 opt.kato * 1000, opt.num_io_queues, opt.queue_size, NULL); 141 free(subnqn); 142 free(tofree); 143 return (error); 144 } 145 146 static int 147 reconnect_by_params(int fd, const nvlist_t *rparams) 148 { 149 struct nvmf_association_params aparams; 150 const struct nvme_discovery_log_entry *dle; 151 char *address, *port, *subnqn; 152 int adrfam, error; 153 154 dle = nvlist_get_binary(rparams, "dle", NULL); 155 156 memset(&aparams, 0, sizeof(aparams)); 157 aparams.sq_flow_control = nvlist_get_bool(rparams, "sq_flow_control"); 158 switch (dle->trtype) { 159 case NVMF_TRTYPE_TCP: 160 switch (dle->adrfam) { 161 case NVMF_ADRFAM_IPV4: 162 adrfam = AF_INET; 163 break; 164 case NVMF_ADRFAM_IPV6: 165 adrfam = AF_INET6; 166 break; 167 default: 168 warnx("Unsupported address family"); 169 return (EX_UNAVAILABLE); 170 } 171 switch (dle->tsas.tcp.sectype) { 172 case NVME_TCP_SECURITY_NONE: 173 break; 174 default: 175 warnx("Unsupported TCP security type"); 176 return (EX_UNAVAILABLE); 177 } 178 break; 179 180 tcp_association_params(&aparams, 181 nvlist_get_bool(rparams, "header_digests"), 182 nvlist_get_bool(rparams, "data_digests")); 183 break; 184 default: 185 warnx("Unsupported transport %s", 186 nvmf_transport_type(dle->trtype)); 187 return (EX_UNAVAILABLE); 188 } 189 190 /* Ensure address, port, and subnqn is a terminated C string. */ 191 address = strndup(dle->traddr, sizeof(dle->traddr)); 192 port = strndup(dle->trsvcid, sizeof(dle->trsvcid)); 193 subnqn = strndup(dle->subnqn, sizeof(dle->subnqn)); 194 195 error = reconnect_nvm_controller(fd, &aparams, dle->trtype, adrfam, 196 address, port, le16toh(dle->cntlid), dle->subnqn, 197 nvlist_get_string(rparams, "hostnqn"), 198 dnvlist_get_number(rparams, "kato", 0), 199 nvlist_get_number(rparams, "num_io_queues"), 200 nvlist_get_number(rparams, "io_qsize"), dle); 201 free(subnqn); 202 free(port); 203 free(address); 204 return (error); 205 } 206 207 static int 208 fetch_and_validate_rparams(int fd, nvlist_t **rparamsp) 209 { 210 const struct nvme_discovery_log_entry *dle; 211 nvlist_t *rparams; 212 size_t len; 213 int error; 214 215 error = nvmf_reconnect_params(fd, &rparams); 216 if (error != 0) { 217 warnc(error, "Failed to fetch reconnect parameters"); 218 return (EX_IOERR); 219 } 220 221 if (!nvlist_exists_binary(rparams, "dle") || 222 !nvlist_exists_string(rparams, "hostnqn") || 223 !nvlist_exists_number(rparams, "num_io_queues") || 224 !nvlist_exists_number(rparams, "io_qsize") || 225 !nvlist_exists_bool(rparams, "sq_flow_control")) { 226 nvlist_destroy(rparams); 227 warnx("Missing required reconnect parameters"); 228 return (EX_IOERR); 229 } 230 231 dle = nvlist_get_binary(rparams, "dle", &len); 232 if (len != sizeof(*dle)) { 233 nvlist_destroy(rparams); 234 warnx("Discovery Log entry reconnect parameter is wrong size"); 235 return (EX_IOERR); 236 } 237 238 switch (dle->trtype) { 239 case NVMF_TRTYPE_TCP: 240 if (!nvlist_exists_bool(rparams, "header_digests") || 241 !nvlist_exists_bool(rparams, "data_digests")) { 242 nvlist_destroy(rparams); 243 warnx("Missing required reconnect parameters"); 244 return (EX_IOERR); 245 } 246 break; 247 default: 248 nvlist_destroy(rparams); 249 warnx("Unsupported transport %s", 250 nvmf_transport_type(dle->trtype)); 251 return (EX_UNAVAILABLE); 252 } 253 254 *rparamsp = rparams; 255 return (0); 256 } 257 258 static void 259 reconnect_fn(const struct cmd *f, int argc, char *argv[]) 260 { 261 nvlist_t *rparams; 262 int error, fd; 263 264 if (arg_parse(argc, argv, f)) 265 return; 266 267 open_dev(opt.dev, &fd, 1, 1); 268 error = fetch_and_validate_rparams(fd, &rparams); 269 if (error != 0) 270 exit(error); 271 272 /* Check for optional address. */ 273 if (optind < argc) 274 error = reconnect_by_address(fd, rparams, argv[optind]); 275 else 276 error = reconnect_by_params(fd, rparams); 277 if (error != 0) 278 exit(error); 279 280 nvlist_destroy(rparams); 281 close(fd); 282 } 283 284 static const struct opts reconnect_opts[] = { 285 #define OPT(l, s, t, opt, addr, desc) { l, s, t, &opt.addr, desc } 286 OPT("transport", 't', arg_string, opt, transport, 287 "Transport type"), 288 OPT("nr-io-queues", 'i', arg_uint16, opt, num_io_queues, 289 "Number of I/O queues"), 290 OPT("queue-size", 'Q', arg_uint16, opt, queue_size, 291 "Number of entries in each I/O queue"), 292 OPT("keep-alive-tmo", 'k', arg_uint32, opt, kato, 293 "Keep Alive timeout (in seconds)"), 294 OPT("hostnqn", 'q', arg_string, opt, hostnqn, 295 "Host NQN"), 296 OPT("flow_control", 'F', arg_none, opt, flow_control, 297 "Request SQ flow control"), 298 OPT("hdr_digests", 'g', arg_none, opt, header_digests, 299 "Enable TCP PDU header digests"), 300 OPT("data_digests", 'G', arg_none, opt, data_digests, 301 "Enable TCP PDU data digests"), 302 { NULL, 0, arg_none, NULL, NULL } 303 }; 304 #undef OPT 305 306 static const struct args reconnect_args[] = { 307 { arg_string, &opt.dev, "controller-id" }, 308 { arg_none, NULL, NULL }, 309 }; 310 311 static struct cmd reconnect_cmd = { 312 .name = "reconnect", 313 .fn = reconnect_fn, 314 .descr = "Reconnect to a fabrics controller", 315 .ctx_size = sizeof(opt), 316 .opts = reconnect_opts, 317 .args = reconnect_args, 318 }; 319 320 CMD_COMMAND(reconnect_cmd); 321