diff --git a/lib/libnvmf/libnvmf.h b/lib/libnvmf/libnvmf.h --- a/lib/libnvmf/libnvmf.h +++ b/lib/libnvmf/libnvmf.h @@ -330,7 +330,8 @@ * Handoff active host association to the kernel. This frees the * qpairs (even on error). */ -int nvmf_handoff_host(struct nvmf_qpair *admin_qp, u_int num_queues, +int nvmf_handoff_host(const struct nvme_discovery_log_entry *dle, + const char *hostnqn, struct nvmf_qpair *admin_qp, u_int num_queues, struct nvmf_qpair **io_queues, const struct nvme_controller_data *cdata); /* @@ -356,8 +357,8 @@ * Handoff active host association to an existing host in the kernel. * This frees the qpairs (even on error). */ -int nvmf_reconnect_host(int fd, struct nvmf_qpair *admin_qp, - u_int num_queues, struct nvmf_qpair **io_queues, - const struct nvme_controller_data *cdata); +int nvmf_reconnect_host(int fd, const struct nvme_discovery_log_entry *dle, + const char *hostnqn, struct nvmf_qpair *admin_qp, u_int num_queues, + struct nvmf_qpair **io_queues, const struct nvme_controller_data *cdata); #endif /* !__LIBNVMF_H__ */ diff --git a/lib/libnvmf/nvmf_host.c b/lib/libnvmf/nvmf_host.c --- a/lib/libnvmf/nvmf_host.c +++ b/lib/libnvmf/nvmf_host.c @@ -768,14 +768,18 @@ static int prepare_queues_for_handoff(struct nvmf_handoff_host *hh, + const struct nvme_discovery_log_entry *dle, const char *hostnqn, struct nvmf_qpair *admin_qp, u_int num_queues, struct nvmf_qpair **io_queues, const struct nvme_controller_data *cdata) { + const struct nvmf_association *na = admin_qp->nq_association; struct nvmf_handoff_qpair_params *io; u_int i; int error; - memset(hh, 0, sizeof(*hh)); + /* Ensure trtype matches. */ + if (dle->trtype != na->na_trtype) + return (EINVAL); /* All queue pairs must be idle. */ if (!is_queue_pair_idle(admin_qp)) @@ -785,15 +789,31 @@ return (EBUSY); } + memset(hh, 0, sizeof(*hh)); + + /* Fill out reconnect parameters. */ + hh->rparams.dle = *dle; + strncpy(hh->rparams.hostnqn, hostnqn, sizeof(hh->rparams.hostnqn)); + hh->rparams.num_io_queues = num_queues; + hh->rparams.kato = admin_qp->nq_kato; + hh->rparams.admin_qsize = admin_qp->nq_qsize; + hh->rparams.io_qsize = io_queues[0]->nq_qsize; + hh->rparams.sq_flow_control = na->na_params.sq_flow_control; + switch (na->na_trtype) { + case NVMF_TRTYPE_TCP: + hh->rparams.header_digests = na->na_params.tcp.header_digests; + hh->rparams.data_digests = na->na_params.tcp.data_digests; + break; + default: + __unreachable(); + } + /* First, the admin queue. */ - hh->trtype = admin_qp->nq_association->na_trtype; - hh->kato = admin_qp->nq_kato; error = nvmf_kernel_handoff_params(admin_qp, &hh->admin); if (error) return (error); /* Next, the I/O queues. */ - hh->num_io_queues = num_queues; io = calloc(num_queues, sizeof(*io)); for (i = 0; i < num_queues; i++) { error = nvmf_kernel_handoff_params(io_queues[i], &io[i]); @@ -809,7 +829,8 @@ } int -nvmf_handoff_host(struct nvmf_qpair *admin_qp, u_int num_queues, +nvmf_handoff_host(const struct nvme_discovery_log_entry *dle, + const char *hostnqn, struct nvmf_qpair *admin_qp, u_int num_queues, struct nvmf_qpair **io_queues, const struct nvme_controller_data *cdata) { struct nvmf_handoff_host hh; @@ -822,8 +843,8 @@ goto out; } - error = prepare_queues_for_handoff(&hh, admin_qp, num_queues, io_queues, - cdata); + error = prepare_queues_for_handoff(&hh, dle, hostnqn, admin_qp, + num_queues, io_queues, cdata); if (error != 0) goto out; @@ -891,15 +912,16 @@ } int -nvmf_reconnect_host(int fd, struct nvmf_qpair *admin_qp, u_int num_queues, +nvmf_reconnect_host(int fd, const struct nvme_discovery_log_entry *dle, + const char *hostnqn, struct nvmf_qpair *admin_qp, u_int num_queues, struct nvmf_qpair **io_queues, const struct nvme_controller_data *cdata) { struct nvmf_handoff_host hh; u_int i; int error; - error = prepare_queues_for_handoff(&hh, admin_qp, num_queues, io_queues, - cdata); + error = prepare_queues_for_handoff(&hh, dle, hostnqn, admin_qp, + num_queues, io_queues, cdata); if (error != 0) goto out; diff --git a/sbin/nvmecontrol/connect.c b/sbin/nvmecontrol/connect.c --- a/sbin/nvmecontrol/connect.c +++ b/sbin/nvmecontrol/connect.c @@ -61,35 +61,39 @@ } static int -connect_nvm_controller(enum nvmf_trtype trtype, int adrfam, const char *address, - const char *port, uint16_t cntlid, const char *subnqn) +connect_nvm_controller(const struct nvme_discovery_log_entry *dle) { struct nvme_controller_data cdata; struct nvmf_association_params aparams; struct nvmf_qpair *admin, **io; + const char *hostnqn; int error; memset(&aparams, 0, sizeof(aparams)); aparams.sq_flow_control = opt.flow_control; - switch (trtype) { + switch (dle->trtype) { case NVMF_TRTYPE_TCP: tcp_association_params(&aparams); break; default: - warnx("Unsupported transport %s", nvmf_transport_type(trtype)); + warnx("Unsupported transport %s", + nvmf_transport_type(dle->trtype)); return (EX_UNAVAILABLE); } + hostnqn = opt.hostnqn; + if (hostnqn == NULL) + hostnqn = nvmf_default_hostnqn(); io = calloc(opt.num_io_queues, sizeof(*io)); - error = connect_nvm_queues(&aparams, trtype, adrfam, address, port, - cntlid, subnqn, opt.hostnqn, opt.kato * 1000, &admin, io, - opt.num_io_queues, opt.queue_size, &cdata); + error = connect_nvm_queues(&aparams, dle, hostnqn, opt.kato * 1000, + &admin, io, opt.num_io_queues, opt.queue_size, &cdata); if (error != 0) { free(io); return (error); } - error = nvmf_handoff_host(admin, opt.num_io_queues, io, &cdata); + error = nvmf_handoff_host(dle, hostnqn, admin, opt.num_io_queues, io, + &cdata); if (error != 0) { warnc(error, "Failed to handoff queues to kernel"); free(io); @@ -99,19 +103,29 @@ return (0); } +static int +connect_by_address(enum nvmf_trtype trtype, const char *address, + const char *port, uint16_t cntlid, const char *subnqn) +{ + struct nvme_discovery_log_entry dle; + int error; + + error = nvmf_init_dle_from_address(trtype, address, port, cntlid, + subnqn, &dle); + if (error != 0) + return (error); + + return (connect_nvm_controller(&dle)); +} + static void connect_discovery_entry(struct nvme_discovery_log_entry *entry) { - int adrfam; - switch (entry->trtype) { case NVMF_TRTYPE_TCP: switch (entry->adrfam) { case NVMF_ADRFAM_IPV4: - adrfam = AF_INET; - break; case NVMF_ADRFAM_IPV6: - adrfam = AF_INET6; break; default: warnx("Skipping unsupported address family for %s", @@ -139,8 +153,7 @@ */ /* XXX: Should this make use of entry->aqsz in some way? */ - connect_nvm_controller(entry->trtype, adrfam, entry->traddr, - entry->trsvcid, entry->cntlid, entry->subnqn); + connect_nvm_controller(entry); } static void @@ -197,8 +210,7 @@ cntlid = nvmf_parse_cntlid(opt.cntlid); - error = connect_nvm_controller(trtype, AF_UNSPEC, address, port, cntlid, - opt.subnqn); + error = connect_by_address(trtype, address, port, cntlid, opt.subnqn); if (error != 0) exit(error); diff --git a/sbin/nvmecontrol/fabrics.h b/sbin/nvmecontrol/fabrics.h --- a/sbin/nvmecontrol/fabrics.h +++ b/sbin/nvmecontrol/fabrics.h @@ -18,9 +18,11 @@ uint16_t nvmf_parse_cntlid(const char *cntlid); -/* Returns true if able to open a connection. */ -bool tcp_qpair_params(struct nvmf_qpair_params *params, int adrfam, - const char *address, const char *port); +const char *nvmf_default_hostnqn(void); + +int nvmf_init_dle_from_address(enum nvmf_trtype trtype, const char *address, + const char *port, uint16_t cntlid, const char *subnqn, + struct nvme_discovery_log_entry *dle); /* Connect to a discovery controller and return the Admin qpair. */ struct nvmf_qpair *connect_discovery_adminq(enum nvmf_trtype trtype, @@ -33,8 +35,7 @@ * failure. */ int connect_nvm_queues(const struct nvmf_association_params *aparams, - enum nvmf_trtype trtype, int adrfam, const char *address, - const char *port, uint16_t cntlid, const char *subnqn, const char *hostnqn, + const struct nvme_discovery_log_entry *dle, const char *hostnqn, uint32_t kato, struct nvmf_qpair **admin, struct nvmf_qpair **io, u_int num_io_queues, u_int queue_size, struct nvme_controller_data *cdata); diff --git a/sbin/nvmecontrol/fabrics.c b/sbin/nvmecontrol/fabrics.c --- a/sbin/nvmecontrol/fabrics.c +++ b/sbin/nvmecontrol/fabrics.c @@ -7,6 +7,7 @@ #include #include +#include #include #include #include @@ -48,6 +49,14 @@ return (true); } +const char * +nvmf_default_hostnqn(void) +{ + if (!init_hostid()) + exit(EX_IOERR); + return (nqn); +} + void nvmf_parse_address(const char *in_address, const char **address, const char **port, char **tofree) @@ -137,7 +146,80 @@ } } -bool +int +nvmf_init_dle_from_address(enum nvmf_trtype trtype, const char *address, + const char *port, uint16_t cntlid, const char *subnqn, + struct nvme_discovery_log_entry *dle) +{ + struct addrinfo hints, *ai, *list; + const struct sockaddr_in *sin; + const struct sockaddr_in6 *sin6; + const void *in_addr; + int error, s; + uint16_t tcp_port; + + switch (trtype) { + case NVMF_TRTYPE_TCP: + break; + default: + warnx("Unsupported transport %s", nvmf_transport_type(trtype)); + return (EX_UNAVAILABLE); + } + + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_protocol = IPPROTO_TCP; + error = getaddrinfo(address, port, &hints, &list); + if (error != 0) { + warnx("%s", gai_strerror(error)); + return (EX_NOHOST); + } + + for (ai = list; ai != NULL; ai = ai->ai_next) { + s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); + if (s == -1) + continue; + + if (connect(s, ai->ai_addr, ai->ai_addrlen) != 0) { + close(s); + continue; + } + close(s); + + goto found; + } + warn("Failed to connect to controller at %s:%s", address, port); + freeaddrinfo(list); + return (EX_NOHOST); + +found: + memset(dle, 0, sizeof(*dle)); + dle->trtype = NVMF_TRTYPE_TCP; + switch (ai->ai_family) { + case AF_INET: + dle->adrfam = NVMF_ADRFAM_IPV4; + sin = (const void *)ai->ai_addr; + in_addr = &sin->sin_addr; + tcp_port = ntohs(sin->sin_port); + break; + case AF_INET6: + dle->adrfam = NVMF_ADRFAM_IPV6; + sin6 = (const void *)ai->ai_addr; + in_addr = &sin6->sin6_addr; + tcp_port = ntohs(sin6->sin6_port); + break; + default: + __unreachable(); + } + dle->cntlid = htole16(cntlid); + snprintf(dle->trsvcid, sizeof(dle->trsvcid), "%u", tcp_port); + strlcpy(dle->subnqn, subnqn, sizeof(dle->subnqn)); + inet_ntop(ai->ai_family, in_addr, dle->traddr, sizeof(dle->traddr)); + freeaddrinfo(list); + return (0); +} + +static bool tcp_qpair_params(struct nvmf_qpair_params *params, int adrfam, const char *address, const char *port) { @@ -401,51 +483,68 @@ /* Returns a value from */ int connect_nvm_queues(const struct nvmf_association_params *aparams, - enum nvmf_trtype trtype, int adrfam, const char *address, - const char *port, uint16_t cntlid, const char *subnqn, const char *hostnqn, + const struct nvme_discovery_log_entry *dle, const char *hostnqn, uint32_t kato, struct nvmf_qpair **admin, struct nvmf_qpair **io, u_int num_io_queues, u_int queue_size, struct nvme_controller_data *cdata) { struct nvmf_qpair_params qparams; struct nvmf_association *na; u_int queues; - int error; - uint16_t mqes; + int adrfam, error; + uint16_t cntlid, mqes; - switch (trtype) { + cntlid = le16toh(dle->cntlid); + switch (dle->trtype) { case NVMF_TRTYPE_TCP: + switch (dle->adrfam) { + case NVMF_ADRFAM_IPV4: + adrfam = AF_INET; + break; + case NVMF_ADRFAM_IPV6: + adrfam = AF_INET6; + break; + default: + warnx("Unsupported address family %u", dle->adrfam); + return (EX_UNAVAILABLE); + } + switch (dle->tsas.tcp.sectype) { + case NVME_TCP_SECURITY_NONE: + break; + default: + warnx("Unsupported TCP security type %u", + dle->tsas.tcp.sectype); + return (EX_UNAVAILABLE); + } break; default: - warnx("Unsupported transport %s", nvmf_transport_type(trtype)); + warnx("Unsupported transport %s", + nvmf_transport_type(dle->trtype)); return (EX_UNAVAILABLE); } if (!init_hostid()) return (EX_IOERR); - if (hostnqn != NULL) { - if (!nvmf_nqn_valid(hostnqn)) { - warnx("Invalid HostNQN %s", hostnqn); - return (EX_USAGE); - } - } else - hostnqn = nqn; + if (hostnqn == NULL || !nvmf_nqn_valid(hostnqn)) { + warnx("Invalid HostNQN %s", hostnqn); + return (EX_USAGE); + } /* Association. */ - na = nvmf_allocate_association(trtype, false, aparams); + na = nvmf_allocate_association(dle->trtype, false, aparams); if (na == NULL) { - warn("Failed to create association for %s", subnqn); + warn("Failed to create association for %s", dle->subnqn); return (EX_IOERR); } /* Admin queue. */ memset(&qparams, 0, sizeof(qparams)); qparams.admin = true; - if (!tcp_qpair_params(&qparams, adrfam, address, port)) { + if (!tcp_qpair_params(&qparams, adrfam, dle->traddr, dle->trsvcid)) { nvmf_free_association(na); return (EX_NOHOST); } - error = connect_nvm_adminq(na, &qparams, admin, cntlid, subnqn, hostnqn, - kato, &mqes); + error = connect_nvm_adminq(na, &qparams, admin, cntlid, dle->subnqn, + hostnqn, kato, &mqes); if (error != 0) { nvmf_free_association(na); return (error); @@ -467,7 +566,8 @@ if (error != 0) { shutdown_controller(*admin); nvmf_free_association(na); - warnc(error, "Failed to fetch controller data for %s", subnqn); + warnc(error, "Failed to fetch controller data for %s", + dle->subnqn); return (EX_IOERR); } @@ -493,12 +593,13 @@ for (u_int i = 0; i < num_io_queues; i++) { memset(&qparams, 0, sizeof(qparams)); qparams.admin = false; - if (!tcp_qpair_params(&qparams, adrfam, address, port)) { + if (!tcp_qpair_params(&qparams, adrfam, dle->traddr, + dle->trsvcid)) { error = EX_NOHOST; goto out; } io[i] = nvmf_connect(na, &qparams, i + 1, queue_size, hostid, - nvmf_cntlid(*admin), subnqn, hostnqn, 0); + nvmf_cntlid(*admin), dle->subnqn, hostnqn, 0); if (io[i] == NULL) { warnx("Failed to create I/O queue: %s", nvmf_association_error(na)); diff --git a/sbin/nvmecontrol/nvmecontrol.8 b/sbin/nvmecontrol/nvmecontrol.8 --- a/sbin/nvmecontrol/nvmecontrol.8 +++ b/sbin/nvmecontrol/nvmecontrol.8 @@ -235,6 +235,9 @@ .Aq Ar device-id | Ar namespace-id | Ar SubNQN .Nm .Ic reconnect +.Aq Ar device-id +.Nm +.Ic reconnect .Op Fl FGg .Op Fl i Ar queues .Op Fl k Ar seconds @@ -813,11 +816,20 @@ including any active association and open queues. .Ss reconnect Reestablish an association for the remote I/O controller associated with -.Ar device-id -at -.Ar address . -The address must include a port. -The flags have the same meaning for the new association as described above +.Ar device-id . +If an +.Ar address +is not provided, +the resolved address and settings from the previous association are used +to establish a new association. +If an +.Ar address +is provided, +the supplied address and command line flags are used to establish a new +association. +In this case, +the address must include a port and +the flags have the same meaning for the new association as described above for the .Cm connect command. diff --git a/sbin/nvmecontrol/reconnect.c b/sbin/nvmecontrol/reconnect.c --- a/sbin/nvmecontrol/reconnect.c +++ b/sbin/nvmecontrol/reconnect.c @@ -45,52 +45,47 @@ }; static void -tcp_association_params(struct nvmf_association_params *params) +tcp_association_params(struct nvmf_association_params *params, + const struct nvmf_reconnect_params *rparams) { params->tcp.pda = 0; - params->tcp.header_digests = opt.header_digests; - params->tcp.data_digests = opt.data_digests; + params->tcp.header_digests = rparams->header_digests; + params->tcp.data_digests = rparams->data_digests; /* XXX */ params->tcp.maxr2t = 1; } static int -reconnect_nvm_controller(int fd, enum nvmf_trtype trtype, int adrfam, - const char *address, const char *port) +reconnect_nvm_controller(int fd, const struct nvmf_reconnect_params *rparams) { struct nvme_controller_data cdata; struct nvmf_association_params aparams; - struct nvmf_reconnect_params rparams; struct nvmf_qpair *admin, **io; int error; - error = nvmf_reconnect_params(fd, &rparams); - if (error != 0) { - warnc(error, "Failed to fetch reconnect parameters"); - return (EX_IOERR); - } - memset(&aparams, 0, sizeof(aparams)); - aparams.sq_flow_control = opt.flow_control; - switch (trtype) { + aparams.sq_flow_control = rparams->sq_flow_control; + switch (rparams->dle.trtype) { case NVMF_TRTYPE_TCP: - tcp_association_params(&aparams); + tcp_association_params(&aparams, rparams); break; default: - warnx("Unsupported transport %s", nvmf_transport_type(trtype)); + warnx("Unsupported transport %s", + nvmf_transport_type(rparams->dle.trtype)); return (EX_UNAVAILABLE); } - io = calloc(opt.num_io_queues, sizeof(*io)); - error = connect_nvm_queues(&aparams, trtype, adrfam, address, port, - rparams.cntlid, rparams.subnqn, opt.hostnqn, opt.kato, &admin, io, - opt.num_io_queues, opt.queue_size, &cdata); + io = calloc(rparams->num_io_queues, sizeof(*io)); + error = connect_nvm_queues(&aparams, &rparams->dle, rparams->hostnqn, + rparams->kato, &admin, io, rparams->num_io_queues, + rparams->io_qsize, &cdata); if (error != 0) { free(io); return (error); } - error = nvmf_reconnect_host(fd, admin, opt.num_io_queues, io, &cdata); + error = nvmf_reconnect_host(fd, &rparams->dle, rparams->hostnqn, + admin, rparams->num_io_queues, io, &cdata); if (error != 0) { warnc(error, "Failed to handoff queues to kernel"); free(io); @@ -100,34 +95,79 @@ return (0); } -static void -reconnect_fn(const struct cmd *f, int argc, char *argv[]) +static int +reconnect_params_from_address(struct nvmf_reconnect_params *rparams) { + struct nvme_discovery_log_entry dle; enum nvmf_trtype trtype; - const char *address, *port; + const char *address, *hostnqn, *port; char *tofree; - int error, fd; - - if (arg_parse(argc, argv, f)) - return; + int error; if (strcasecmp(opt.transport, "tcp") == 0) { trtype = NVMF_TRTYPE_TCP; - } else - errx(EX_USAGE, "Unsupported or invalid transport"); + } else { + warnx("Unsupported or invalid transport"); + return (EX_USAGE); + } nvmf_parse_address(opt.address, &address, &port, &tofree); + if (port == NULL) { + free(tofree); + warnx("Explicit port required"); + return (EX_USAGE); + } + + error = nvmf_init_dle_from_address(trtype, address, port, + le16toh(rparams->dle.cntlid), rparams->dle.subnqn, &dle); + free(tofree); + if (error != 0) + return (error); + + hostnqn = opt.hostnqn; + if (hostnqn == NULL) + hostnqn = nvmf_default_hostnqn(); + + rparams->dle = dle; + strncpy(rparams->hostnqn, hostnqn, sizeof(rparams->hostnqn)); + rparams->num_io_queues = opt.num_io_queues; + rparams->kato = opt.kato * 1000; + rparams->io_qsize = opt.queue_size; + rparams->sq_flow_control = opt.flow_control; + rparams->header_digests = opt.header_digests; + rparams->data_digests = opt.data_digests; + return (0); +} + +static void +reconnect_fn(const struct cmd *f, int argc, char *argv[]) +{ + struct nvmf_reconnect_params rparams; + int error, fd; + + if (arg_parse(argc, argv, f)) + return; open_dev(opt.dev, &fd, 1, 1); - if (port == NULL) - errx(EX_USAGE, "Explicit port required"); + error = nvmf_reconnect_params(fd, &rparams); + if (error != 0) { + warnc(error, "Failed to fetch reconnect parameters"); + exit(EX_IOERR); + } - error = reconnect_nvm_controller(fd, trtype, AF_UNSPEC, address, port); + /* Check for optional address. */ + if (optind < argc) { + opt.address = argv[optind]; + error = reconnect_params_from_address(&rparams); + if (error != 0) + exit(error); + } + + error = reconnect_nvm_controller(fd, &rparams); if (error != 0) exit(error); close(fd); - free(tofree); } static const struct opts reconnect_opts[] = { @@ -154,7 +194,6 @@ static const struct args reconnect_args[] = { { arg_string, &opt.dev, "controller-id" }, - { arg_string, &opt.address, "address" }, { arg_none, NULL, NULL }, }; diff --git a/sys/dev/nvmf/host/nvmf.c b/sys/dev/nvmf/host/nvmf.c --- a/sys/dev/nvmf/host/nvmf.c +++ b/sys/dev/nvmf/host/nvmf.c @@ -204,8 +204,11 @@ memset(ivars, 0, sizeof(*ivars)); - if (!hh->admin.admin || hh->num_io_queues < 1) - return (EINVAL); + if (!hh->admin.admin || hh->rparams.num_io_queues < 1 || + hh->rparams.num_io_queues > 65535) { + error = EINVAL; + goto out; + } ivars->cdata = malloc(sizeof(*ivars->cdata), M_NVMF, M_WAITOK); error = copyin(hh->cdata, ivars->cdata, sizeof(*ivars->cdata)); @@ -213,19 +216,25 @@ goto out; nvme_controller_data_swapbytes(ivars->cdata); - len = hh->num_io_queues * sizeof(*ivars->io_params); + if (memcmp(hh->rparams.dle.subnqn, ivars->cdata->subnqn, + sizeof(ivars->cdata->subnqn)) != 0) { + error = EINVAL; + goto out; + } + + len = hh->rparams.num_io_queues * sizeof(*ivars->io_params); ivars->io_params = malloc(len, M_NVMF, M_WAITOK); error = copyin(hh->io, ivars->io_params, len); if (error != 0) goto out; - for (i = 0; i < hh->num_io_queues; i++) { + for (i = 0; i < hh->rparams.num_io_queues; i++) { if (ivars->io_params[i].admin) { error = EINVAL; goto out; } /* Require all I/O queues to be the same size. */ - if (ivars->io_params[i].qsize != ivars->io_params[0].qsize) { + if (ivars->io_params[i].qsize != hh->rparams.io_qsize) { error = EINVAL; goto out; } @@ -262,10 +271,11 @@ static int nvmf_establish_connection(struct nvmf_softc *sc, struct nvmf_ivars *ivars) { + const struct nvmf_reconnect_params *rparams = &ivars->hh->rparams; char name[16]; /* Setup the admin queue. */ - sc->admin = nvmf_init_qp(sc, ivars->hh->trtype, &ivars->hh->admin, + sc->admin = nvmf_init_qp(sc, rparams->dle.trtype, &ivars->hh->admin, "admin queue", 0); if (sc->admin == NULL) { device_printf(sc->dev, "Failed to setup admin queue\n"); @@ -273,25 +283,25 @@ } /* Setup I/O queues. */ - sc->io = malloc(ivars->hh->num_io_queues * sizeof(*sc->io), M_NVMF, + sc->num_io_queues = rparams->num_io_queues; + sc->io = malloc(sc->num_io_queues * sizeof(*sc->io), M_NVMF, M_WAITOK | M_ZERO); - sc->num_io_queues = ivars->hh->num_io_queues; for (u_int i = 0; i < sc->num_io_queues; i++) { snprintf(name, sizeof(name), "I/O queue %u", i); - sc->io[i] = nvmf_init_qp(sc, ivars->hh->trtype, + sc->io[i] = nvmf_init_qp(sc, rparams->dle.trtype, &ivars->io_params[i], name, i); if (sc->io[i] == NULL) { device_printf(sc->dev, "Failed to setup I/O queue %u\n", - i + 1); + i); return (ENXIO); } } /* Start KeepAlive timers. */ - if (ivars->hh->kato != 0) { + if (rparams->kato != 0) { sc->ka_traffic = NVMEV(NVME_CTRLR_DATA_CTRATT_TBKAS, sc->cdata->ctratt) != 0; - sc->ka_rx_sbt = mstosbt(ivars->hh->kato); + sc->ka_rx_sbt = mstosbt(rparams->kato); sc->ka_tx_sbt = sc->ka_rx_sbt / 2; callout_reset_sbt(&sc->ka_rx_timer, sc->ka_rx_sbt, 0, nvmf_check_keep_alive, sc, C_HARDCLOCK); @@ -299,6 +309,9 @@ nvmf_send_keep_alive, sc, C_HARDCLOCK); } + /* Save reconnect parameters. */ + sc->rparams = *rparams; + return (0); } @@ -462,7 +475,6 @@ return (ENXIO); sc->dev = dev; - sc->trtype = ivars->hh->trtype; callout_init(&sc->ka_rx_timer, 1); callout_init(&sc->ka_tx_timer, 1); sx_init(&sc->connection_lock, "nvmf connection"); @@ -648,7 +660,7 @@ int error; /* XXX: Should we permit changing the transport type? */ - if (sc->trtype != hh->trtype) { + if (sc->rparams.dle.trtype != hh->rparams.dle.trtype) { device_printf(sc->dev, "transport type mismatch on reconnect\n"); return (EINVAL); @@ -1045,11 +1057,7 @@ return (0); case NVMF_RECONNECT_PARAMS: rp = (struct nvmf_reconnect_params *)arg; - if ((sc->cdata->fcatt & 1) == 0) - rp->cntlid = NVMF_CNTLID_DYNAMIC; - else - rp->cntlid = sc->cdata->ctrlr_id; - memcpy(rp->subnqn, sc->cdata->subnqn, sizeof(rp->subnqn)); + *rp = sc->rparams; return (0); case NVMF_RECONNECT_HOST: hh = (struct nvmf_handoff_host *)arg; diff --git a/sys/dev/nvmf/host/nvmf_sim.c b/sys/dev/nvmf/host/nvmf_sim.c --- a/sys/dev/nvmf/host/nvmf_sim.c +++ b/sys/dev/nvmf/host/nvmf_sim.c @@ -16,6 +16,7 @@ #include #include +#include #include /* @@ -190,7 +191,7 @@ cpi->transport_version = sc->vs; cpi->xport_specific.nvmf.nsid = xpt_path_lun_id(ccb->ccb_h.path); - cpi->xport_specific.nvmf.trtype = sc->trtype; + cpi->xport_specific.nvmf.trtype = sc->rparams.dle.trtype; strlcpy(cpi->xport_specific.nvmf.dev_name, device_get_nameunit(sc->dev), sizeof(cpi->xport_specific.nvmf.dev_name)); @@ -219,7 +220,7 @@ nvmf = &cts->xport_specific.nvmf; nvmf->valid = CTS_NVMF_VALID_TRTYPE; - nvmf->trtype = sc->trtype; + nvmf->trtype = sc->rparams.dle.trtype; cts->ccb_h.status = CAM_REQ_CMP; break; } diff --git a/sys/dev/nvmf/host/nvmf_var.h b/sys/dev/nvmf/host/nvmf_var.h --- a/sys/dev/nvmf/host/nvmf_var.h +++ b/sys/dev/nvmf/host/nvmf_var.h @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include struct nvmf_aer; @@ -39,7 +39,6 @@ struct nvmf_host_qpair *admin; struct nvmf_host_qpair **io; u_int num_io_queues; - enum nvmf_trtype trtype; struct cam_sim *sim; struct cam_path *path; @@ -89,6 +88,8 @@ struct sysctl_oid_list *ioq_oid_list; + struct nvmf_reconnect_params rparams; + eventhandler_tag shutdown_pre_sync_eh; eventhandler_tag shutdown_post_sync_eh; }; diff --git a/sys/dev/nvmf/nvmf.h b/sys/dev/nvmf/nvmf.h --- a/sys/dev/nvmf/nvmf.h +++ b/sys/dev/nvmf/nvmf.h @@ -12,6 +12,7 @@ #ifndef _KERNEL #include #endif +#include /* * Default settings in Fabrics controllers. These match values used by the @@ -46,20 +47,25 @@ }; }; -struct nvmf_handoff_host { - u_int trtype; +struct nvmf_reconnect_params { + struct nvme_discovery_log_entry dle; + char hostnqn[256]; u_int num_io_queues; - u_int kato; + uint32_t kato; + uint16_t admin_qsize; + uint16_t io_qsize; + bool sq_flow_control; + bool header_digests; + bool data_digests; +}; + +struct nvmf_handoff_host { + struct nvmf_reconnect_params rparams; struct nvmf_handoff_qpair_params admin; struct nvmf_handoff_qpair_params *io; const struct nvme_controller_data *cdata; }; -struct nvmf_reconnect_params { - uint16_t cntlid; - char subnqn[256]; -}; - struct nvmf_handoff_controller_qpair { u_int trtype; struct nvmf_handoff_qpair_params params;