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