Page MenuHomeFreeBSD

D48214.1777098771.diff
No OneTemporary

Size
27 KB
Referenced Files
None
Subscribers
None

D48214.1777098771.diff

diff --git a/lib/libnvmf/internal.h b/lib/libnvmf/internal.h
--- a/lib/libnvmf/internal.h
+++ b/lib/libnvmf/internal.h
@@ -26,6 +26,8 @@
/* Add params for kernel handoff. */
void (*kernel_handoff_params)(struct nvmf_qpair *qp, nvlist_t *nvl);
+ int (*populate_dle)(struct nvmf_qpair *qp,
+ struct nvme_discovery_log_entry *dle);
/* Capsule operations. */
struct nvmf_capsule *(*allocate_capsule)(struct nvmf_qpair *qp);
@@ -111,6 +113,8 @@
void na_error(struct nvmf_association *na, const char *fmt, ...);
int nvmf_kernel_handoff_params(struct nvmf_qpair *qp, nvlist_t **nvlp);
+int nvmf_populate_dle(struct nvmf_qpair *qp,
+ struct nvme_discovery_log_entry *dle);
int nvmf_pack_ioc_nvlist(struct nvmf_ioc_nv *nv, nvlist_t *nvl);
#endif /* !__LIBNVMF_INTERNAL_H__ */
diff --git a/lib/libnvmf/libnvmf.h b/lib/libnvmf/libnvmf.h
--- a/lib/libnvmf/libnvmf.h
+++ b/lib/libnvmf/libnvmf.h
@@ -320,6 +320,14 @@
int nvmf_host_fetch_discovery_log_page(struct nvmf_qpair *qp,
struct nvme_discovery_log **logp);
+/*
+ * Construct a discovery log page entry that describes the connection
+ * used by a host association's admin queue pair.
+ */
+int nvmf_init_dle_from_admin_qp(struct nvmf_qpair *qp,
+ const struct nvme_controller_data *cdata,
+ struct nvme_discovery_log_entry *dle);
+
/*
* Request a desired number of I/O queues via SET_FEATURES. The
* number of actual I/O queues available is returned in *actual on
@@ -332,7 +340,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);
/*
@@ -359,8 +368,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
@@ -709,6 +709,27 @@
return (0);
}
+int
+nvmf_init_dle_from_admin_qp(struct nvmf_qpair *qp,
+ const struct nvme_controller_data *cdata,
+ struct nvme_discovery_log_entry *dle)
+{
+ int error;
+ uint16_t cntlid;
+
+ memset(dle, 0, sizeof(*dle));
+ error = nvmf_populate_dle(qp, dle);
+ if (error != 0)
+ return (error);
+ if ((cdata->fcatt & 1) == 0)
+ cntlid = NVMF_CNTLID_DYNAMIC;
+ else
+ cntlid = cdata->ctrlr_id;
+ dle->cntlid = htole16(cntlid);
+ memcpy(dle->subnqn, cdata->subnqn, sizeof(dle->subnqn));
+ return (0);
+}
+
int
nvmf_host_request_queues(struct nvmf_qpair *qp, u_int requested, u_int *actual)
{
@@ -767,17 +788,23 @@
}
static int
-prepare_queues_for_handoff(struct nvmf_ioc_nv *nv, struct nvmf_qpair *admin_qp,
- u_int num_queues, struct nvmf_qpair **io_queues,
- const struct nvme_controller_data *cdata)
+prepare_queues_for_handoff(struct nvmf_ioc_nv *nv,
+ 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)
{
- nvlist_t *nvl, *nvl_qp;
+ const struct nvmf_association *na = admin_qp->nq_association;
+ nvlist_t *nvl, *nvl_qp, *nvl_rparams;
u_int i;
int error;
if (num_queues == 0)
return (EINVAL);
+ /* Ensure trtype matches. */
+ if (dle->trtype != na->na_trtype)
+ return (EINVAL);
+
/* All queue pairs must be idle. */
if (!is_queue_pair_idle(admin_qp))
return (EBUSY);
@@ -786,9 +813,35 @@
return (EBUSY);
}
+ /* Fill out reconnect parameters. */
+ nvl_rparams = nvlist_create(0);
+ nvlist_add_binary(nvl_rparams, "dle", dle, sizeof(*dle));
+ nvlist_add_string(nvl_rparams, "hostnqn", hostnqn);
+ nvlist_add_number(nvl_rparams, "num_io_queues", num_queues);
+ nvlist_add_number(nvl_rparams, "kato", admin_qp->nq_kato);
+ nvlist_add_number(nvl_rparams, "io_qsize", io_queues[0]->nq_qsize);
+ nvlist_add_bool(nvl_rparams, "sq_flow_control",
+ na->na_params.sq_flow_control);
+ switch (na->na_trtype) {
+ case NVMF_TRTYPE_TCP:
+ nvlist_add_bool(nvl_rparams, "header_digests",
+ na->na_params.tcp.header_digests);
+ nvlist_add_bool(nvl_rparams, "data_digests",
+ na->na_params.tcp.data_digests);
+ break;
+ default:
+ __unreachable();
+ }
+ error = nvlist_error(nvl_rparams);
+ if (error != 0) {
+ nvlist_destroy(nvl_rparams);
+ return (error);
+ }
+
nvl = nvlist_create(0);
- nvlist_add_number(nvl, "trtype", admin_qp->nq_association->na_trtype);
+ nvlist_add_number(nvl, "trtype", na->na_trtype);
nvlist_add_number(nvl, "kato", admin_qp->nq_kato);
+ nvlist_move_nvlist(nvl, "rparams", nvl_rparams);
/* First, the admin queue. */
error = nvmf_kernel_handoff_params(admin_qp, &nvl_qp);
@@ -816,7 +869,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_ioc_nv nv;
@@ -829,8 +883,8 @@
goto out;
}
- error = prepare_queues_for_handoff(&nv, admin_qp, num_queues, io_queues,
- cdata);
+ error = prepare_queues_for_handoff(&nv, dle, hostnqn, admin_qp,
+ num_queues, io_queues, cdata);
if (error != 0)
goto out;
@@ -924,15 +978,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_ioc_nv nv;
u_int i;
int error;
- error = prepare_queues_for_handoff(&nv, admin_qp, num_queues, io_queues,
- cdata);
+ error = prepare_queues_for_handoff(&nv, dle, hostnqn, admin_qp,
+ num_queues, io_queues, cdata);
if (error != 0)
goto out;
diff --git a/lib/libnvmf/nvmf_tcp.c b/lib/libnvmf/nvmf_tcp.c
--- a/lib/libnvmf/nvmf_tcp.c
+++ b/lib/libnvmf/nvmf_tcp.c
@@ -8,9 +8,11 @@
#include <sys/endian.h>
#include <sys/gsb_crc32.h>
#include <sys/queue.h>
+#include <sys/socket.h>
#include <sys/uio.h>
#include <assert.h>
#include <errno.h>
+#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -1144,6 +1146,25 @@
nvlist_add_number(nvl, "max_icd", qp->max_icd);
}
+static int
+tcp_populate_dle(struct nvmf_qpair *nq, struct nvme_discovery_log_entry *dle)
+{
+ struct nvmf_tcp_qpair *qp = TQP(nq);
+ struct sockaddr_storage ss;
+ socklen_t ss_len;
+
+ ss_len = sizeof(ss);
+ if (getpeername(qp->s, (struct sockaddr *)&ss, &ss_len) == -1)
+ return (errno);
+
+ if (getnameinfo((struct sockaddr *)&ss, ss_len, dle->traddr,
+ sizeof(dle->traddr), dle->trsvcid, sizeof(dle->trsvcid),
+ NI_NUMERICHOST | NI_NUMERICSERV) != 0)
+ return (EINVAL);
+
+ return (0);
+}
+
static struct nvmf_capsule *
tcp_allocate_capsule(struct nvmf_qpair *qp __unused)
{
@@ -1468,6 +1489,7 @@
.allocate_qpair = tcp_allocate_qpair,
.free_qpair = tcp_free_qpair,
.kernel_handoff_params = tcp_kernel_handoff_params,
+ .populate_dle = tcp_populate_dle,
.allocate_capsule = tcp_allocate_capsule,
.free_capsule = tcp_free_capsule,
.transmit_capsule = tcp_transmit_capsule,
diff --git a/lib/libnvmf/nvmf_transport.c b/lib/libnvmf/nvmf_transport.c
--- a/lib/libnvmf/nvmf_transport.c
+++ b/lib/libnvmf/nvmf_transport.c
@@ -259,6 +259,15 @@
return (0);
}
+int
+nvmf_populate_dle(struct nvmf_qpair *qp, struct nvme_discovery_log_entry *dle)
+{
+ struct nvmf_association *na = qp->nq_association;
+
+ dle->trtype = na->na_trtype;
+ return (na->na_ops->populate_dle(qp, dle));
+}
+
const char *
nvmf_transport_type(uint8_t trtype)
{
diff --git a/sbin/nvmecontrol/connect.c b/sbin/nvmecontrol/connect.c
--- a/sbin/nvmecontrol/connect.c
+++ b/sbin/nvmecontrol/connect.c
@@ -62,11 +62,14 @@
static int
connect_nvm_controller(enum nvmf_trtype trtype, int adrfam, const char *address,
- const char *port, uint16_t cntlid, const char *subnqn)
+ const char *port, uint16_t cntlid, const char *subnqn,
+ const struct nvme_discovery_log_entry *dle)
{
struct nvme_controller_data cdata;
+ struct nvme_discovery_log_entry dle_thunk;
struct nvmf_association_params aparams;
struct nvmf_qpair *admin, **io;
+ const char *hostnqn;
int error;
memset(&aparams, 0, sizeof(aparams));
@@ -80,16 +83,31 @@
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,
+ cntlid, subnqn, 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);
+ if (dle == NULL) {
+ error = nvmf_init_dle_from_admin_qp(admin, &cdata, &dle_thunk);
+ if (error != 0) {
+ warnc(error, "Failed to generate handoff parameters");
+ disconnect_nvm_queues(admin, io, opt.num_io_queues);
+ free(io);
+ return (EX_IOERR);
+ }
+ dle = &dle_thunk;
+ }
+
+ 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);
@@ -140,7 +158,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);
+ entry->trsvcid, entry->cntlid, entry->subnqn, entry);
}
static void
@@ -198,7 +216,7 @@
cntlid = nvmf_parse_cntlid(opt.cntlid);
error = connect_nvm_controller(trtype, AF_UNSPEC, address, port, cntlid,
- opt.subnqn);
+ opt.subnqn, NULL);
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,
@@ -38,4 +40,11 @@
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);
+/*
+ * Disconnect from an NVM controller disconnecting all queues and
+ * shutting down the controller.
+ */
+void disconnect_nvm_queues(struct nvmf_qpair *admin, struct nvmf_qpair **io,
+ u_int num_io_queues);
+
#endif /* !__FABRICS_H__ */
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 <sys/socket.h>
#include <netinet/in.h>
+#include <arpa/inet.h>
#include <err.h>
#include <libnvmf.h>
#include <netdb.h>
@@ -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,7 @@
}
}
-bool
+static bool
tcp_qpair_params(struct nvmf_qpair_params *params, int adrfam,
const char *address, const char *port)
{
@@ -422,13 +431,10 @@
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);
@@ -510,12 +516,19 @@
return (0);
out:
+ disconnect_nvm_queues(*admin, io, num_io_queues);
+ nvmf_free_association(na);
+ return (error);
+}
+
+void
+disconnect_nvm_queues(struct nvmf_qpair *admin, struct nvmf_qpair **io,
+ u_int num_io_queues)
+{
for (u_int i = 0; i < num_io_queues; i++) {
if (io[i] == NULL)
break;
nvmf_free_qpair(io[i]);
}
- shutdown_controller(*admin);
- nvmf_free_association(na);
- return (error);
+ shutdown_controller(admin);
}
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
@@ -5,6 +5,7 @@
* Written by: John Baldwin <jhb@FreeBSD.org>
*/
+#include <sys/dnv.h>
#include <sys/nv.h>
#include <sys/socket.h>
#include <err.h>
@@ -24,7 +25,6 @@
static struct options {
const char *dev;
const char *transport;
- const char *address;
const char *hostnqn;
uint32_t kato;
uint16_t num_io_queues;
@@ -35,7 +35,6 @@
} opt = {
.dev = NULL,
.transport = "tcp",
- .address = NULL,
.hostnqn = NULL,
.kato = NVMF_KATO_DEFAULT / 1000,
.num_io_queues = 1,
@@ -46,100 +45,240 @@
};
static void
-tcp_association_params(struct nvmf_association_params *params)
+tcp_association_params(struct nvmf_association_params *params,
+ bool header_digests, bool data_digests)
{
params->tcp.pda = 0;
- params->tcp.header_digests = opt.header_digests;
- params->tcp.data_digests = opt.data_digests;
+ params->tcp.header_digests = header_digests;
+ params->tcp.data_digests = 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_association_params *aparams,
+ enum nvmf_trtype trtype, int adrfam, const char *address, const char *port,
+ uint16_t cntlid, const char *subnqn, const char *hostnqn, uint32_t kato,
+ u_int num_io_queues, u_int queue_size,
+ const struct nvme_discovery_log_entry *dle)
{
struct nvme_controller_data cdata;
- struct nvmf_association_params aparams;
- nvlist_t *rparams;
+ struct nvme_discovery_log_entry dle_thunk;
struct nvmf_qpair *admin, **io;
int error;
- error = nvmf_reconnect_params(fd, &rparams);
+ io = calloc(num_io_queues, sizeof(*io));
+ error = connect_nvm_queues(aparams, trtype, adrfam, address, port,
+ cntlid, subnqn, hostnqn, kato, &admin, io, num_io_queues,
+ queue_size, &cdata);
if (error != 0) {
- warnc(error, "Failed to fetch reconnect parameters");
- return (EX_IOERR);
+ free(io);
+ return (error);
}
- if (!nvlist_exists_number(rparams, "cntlid") ||
- !nvlist_exists_string(rparams, "subnqn")) {
- nvlist_destroy(rparams);
- warnx("Missing required reconnect parameters");
+ if (dle == NULL) {
+ error = nvmf_init_dle_from_admin_qp(admin, &cdata, &dle_thunk);
+ if (error != 0) {
+ warnc(error, "Failed to generate handoff parameters");
+ disconnect_nvm_queues(admin, io, num_io_queues);
+ free(io);
+ return (EX_IOERR);
+ }
+ dle = &dle_thunk;
+ }
+
+ error = nvmf_reconnect_host(fd, dle, hostnqn, admin, num_io_queues, io,
+ &cdata);
+ if (error != 0) {
+ warnc(error, "Failed to handoff queues to kernel");
+ free(io);
return (EX_IOERR);
}
+ free(io);
+ return (0);
+}
+
+static int
+reconnect_by_address(int fd, const nvlist_t *rparams, const char *addr)
+{
+ const struct nvme_discovery_log_entry *dle;
+ struct nvmf_association_params aparams;
+ enum nvmf_trtype trtype;
+ const char *address, *hostnqn, *port;
+ char *subnqn, *tofree;
+ int error;
memset(&aparams, 0, sizeof(aparams));
aparams.sq_flow_control = opt.flow_control;
- switch (trtype) {
+ if (strcasecmp(opt.transport, "tcp") == 0) {
+ trtype = NVMF_TRTYPE_TCP;
+ tcp_association_params(&aparams, opt.header_digests,
+ opt.data_digests);
+ } else {
+ warnx("Unsupported or invalid transport");
+ return (EX_USAGE);
+ }
+
+ nvmf_parse_address(addr, &address, &port, &tofree);
+ if (port == NULL) {
+ free(tofree);
+ warnx("Explicit port required");
+ return (EX_USAGE);
+ }
+
+ dle = nvlist_get_binary(rparams, "dle", NULL);
+
+ hostnqn = opt.hostnqn;
+ if (hostnqn == NULL)
+ hostnqn = nvmf_default_hostnqn();
+
+ /* Ensure subnqn is a terminated C string. */
+ subnqn = strndup(dle->subnqn, sizeof(dle->subnqn));
+
+ error = reconnect_nvm_controller(fd, &aparams, trtype, AF_UNSPEC,
+ address, port, le16toh(dle->cntlid), subnqn, hostnqn,
+ opt.kato * 1000, opt.num_io_queues, opt.queue_size, NULL);
+ free(subnqn);
+ free(tofree);
+ return (error);
+}
+
+static int
+reconnect_by_params(int fd, const nvlist_t *rparams)
+{
+ struct nvmf_association_params aparams;
+ const struct nvme_discovery_log_entry *dle;
+ char *address, *port, *subnqn;
+ int adrfam, error;
+
+ dle = nvlist_get_binary(rparams, "dle", NULL);
+
+ memset(&aparams, 0, sizeof(aparams));
+ aparams.sq_flow_control = nvlist_get_bool(rparams, "sq_flow_control");
+ switch (dle->trtype) {
case NVMF_TRTYPE_TCP:
- tcp_association_params(&aparams);
+ switch (dle->adrfam) {
+ case NVMF_ADRFAM_IPV4:
+ adrfam = AF_INET;
+ break;
+ case NVMF_ADRFAM_IPV6:
+ adrfam = AF_INET6;
+ break;
+ default:
+ warnx("Unsupported address family");
+ return (EX_UNAVAILABLE);
+ }
+ switch (dle->tsas.tcp.sectype) {
+ case NVME_TCP_SECURITY_NONE:
+ break;
+ default:
+ warnx("Unsupported TCP security type");
+ return (EX_UNAVAILABLE);
+ }
+ break;
+
+ tcp_association_params(&aparams,
+ nvlist_get_bool(rparams, "header_digests"),
+ nvlist_get_bool(rparams, "data_digests"));
break;
default:
- nvlist_destroy(rparams);
- warnx("Unsupported transport %s", nvmf_transport_type(trtype));
+ warnx("Unsupported transport %s",
+ nvmf_transport_type(dle->trtype));
return (EX_UNAVAILABLE);
}
- io = calloc(opt.num_io_queues, sizeof(*io));
- error = connect_nvm_queues(&aparams, trtype, adrfam, address, port,
- nvlist_get_number(rparams, "cntlid"),
- nvlist_get_string(rparams, "subnqn"), opt.hostnqn, opt.kato,
- &admin, io, opt.num_io_queues, opt.queue_size, &cdata);
+ /* Ensure address, port, and subnqn is a terminated C string. */
+ address = strndup(dle->traddr, sizeof(dle->traddr));
+ port = strndup(dle->trsvcid, sizeof(dle->trsvcid));
+ subnqn = strndup(dle->subnqn, sizeof(dle->subnqn));
+
+ error = reconnect_nvm_controller(fd, &aparams, dle->trtype, adrfam,
+ address, port, le16toh(dle->cntlid), dle->subnqn,
+ nvlist_get_string(rparams, "hostnqn"),
+ dnvlist_get_number(rparams, "kato", 0),
+ nvlist_get_number(rparams, "num_io_queues"),
+ nvlist_get_number(rparams, "io_qsize"), dle);
+ free(subnqn);
+ free(port);
+ free(address);
+ return (error);
+}
+
+static int
+fetch_and_validate_rparams(int fd, nvlist_t **rparamsp)
+{
+ const struct nvme_discovery_log_entry *dle;
+ nvlist_t *rparams;
+ size_t len;
+ int error;
+
+ error = nvmf_reconnect_params(fd, &rparams);
if (error != 0) {
- free(io);
+ warnc(error, "Failed to fetch reconnect parameters");
+ return (EX_IOERR);
+ }
+
+ if (!nvlist_exists_binary(rparams, "dle") ||
+ !nvlist_exists_string(rparams, "hostnqn") ||
+ !nvlist_exists_number(rparams, "num_io_queues") ||
+ !nvlist_exists_number(rparams, "io_qsize") ||
+ !nvlist_exists_bool(rparams, "sq_flow_control")) {
nvlist_destroy(rparams);
- return (error);
+ warnx("Missing required reconnect parameters");
+ return (EX_IOERR);
}
- nvlist_destroy(rparams);
- error = nvmf_reconnect_host(fd, admin, opt.num_io_queues, io, &cdata);
- if (error != 0) {
- warnc(error, "Failed to handoff queues to kernel");
- free(io);
+ dle = nvlist_get_binary(rparams, "dle", &len);
+ if (len != sizeof(*dle)) {
+ nvlist_destroy(rparams);
+ warnx("Discovery Log entry reconnect parameter is wrong size");
return (EX_IOERR);
}
- free(io);
+
+ switch (dle->trtype) {
+ case NVMF_TRTYPE_TCP:
+ if (!nvlist_exists_bool(rparams, "header_digests") ||
+ !nvlist_exists_bool(rparams, "data_digests")) {
+ nvlist_destroy(rparams);
+ warnx("Missing required reconnect parameters");
+ return (EX_IOERR);
+ }
+ break;
+ default:
+ nvlist_destroy(rparams);
+ warnx("Unsupported transport %s",
+ nvmf_transport_type(dle->trtype));
+ return (EX_UNAVAILABLE);
+ }
+
+ *rparamsp = rparams;
return (0);
}
static void
reconnect_fn(const struct cmd *f, int argc, char *argv[])
{
- enum nvmf_trtype trtype;
- const char *address, *port;
- char *tofree;
+ nvlist_t *rparams;
int error, fd;
if (arg_parse(argc, argv, f))
return;
- if (strcasecmp(opt.transport, "tcp") == 0) {
- trtype = NVMF_TRTYPE_TCP;
- } else
- errx(EX_USAGE, "Unsupported or invalid transport");
-
- nvmf_parse_address(opt.address, &address, &port, &tofree);
-
open_dev(opt.dev, &fd, 1, 1);
- if (port == NULL)
- errx(EX_USAGE, "Explicit port required");
+ error = fetch_and_validate_rparams(fd, &rparams);
+ if (error != 0)
+ exit(error);
- error = reconnect_nvm_controller(fd, trtype, AF_UNSPEC, address, port);
+ /* Check for optional address. */
+ if (optind < argc)
+ error = reconnect_by_address(fd, rparams, argv[optind]);
+ else
+ error = reconnect_by_params(fd, rparams);
if (error != 0)
exit(error);
+ nvlist_destroy(rparams);
close(fd);
- free(tofree);
}
static const struct opts reconnect_opts[] = {
@@ -166,7 +305,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
@@ -200,8 +200,10 @@
int
nvmf_copyin_handoff(const struct nvmf_ioc_nv *nv, nvlist_t **nvlp)
{
+ const struct nvme_discovery_log_entry *dle;
+ const struct nvme_controller_data *cdata;
const nvlist_t *const *io;
- const nvlist_t *admin;
+ const nvlist_t *admin, *rparams;
nvlist_t *nvl;
size_t i, num_io_queues;
uint32_t qsize;
@@ -214,7 +216,15 @@
if (!nvlist_exists_number(nvl, "trtype") ||
!nvlist_exists_nvlist(nvl, "admin") ||
!nvlist_exists_nvlist_array(nvl, "io") ||
- !nvlist_exists_binary(nvl, "cdata"))
+ !nvlist_exists_binary(nvl, "cdata") ||
+ !nvlist_exists_nvlist(nvl, "rparams"))
+ goto invalid;
+
+ rparams = nvlist_get_nvlist(nvl, "rparams");
+ if (!nvlist_exists_binary(rparams, "dle") ||
+ !nvlist_exists_string(rparams, "hostnqn") ||
+ !nvlist_exists_number(rparams, "num_io_queues") ||
+ !nvlist_exists_number(rparams, "io_qsize"))
goto invalid;
admin = nvlist_get_nvlist(nvl, "admin");
@@ -224,7 +234,8 @@
goto invalid;
io = nvlist_get_nvlist_array(nvl, "io", &num_io_queues);
- if (num_io_queues < 1)
+ if (num_io_queues < 1 ||
+ num_io_queues != nvlist_get_number(rparams, "num_io_queues"))
goto invalid;
for (i = 0; i < num_io_queues; i++) {
if (!nvmf_validate_qpair_nvlist(io[i], false))
@@ -232,14 +243,20 @@
}
/* Require all I/O queues to be the same size. */
- qsize = nvlist_get_number(io[0], "qsize");
- for (i = 1; i < num_io_queues; i++) {
+ qsize = nvlist_get_number(rparams, "io_qsize");
+ for (i = 0; i < num_io_queues; i++) {
if (nvlist_get_number(io[i], "qsize") != qsize)
goto invalid;
}
- nvlist_get_binary(nvl, "cdata", &i);
- if (i != sizeof(struct nvme_controller_data))
+ cdata = nvlist_get_binary(nvl, "cdata", &i);
+ if (i != sizeof(*cdata))
+ goto invalid;
+ dle = nvlist_get_binary(rparams, "dle", &i);
+ if (i != sizeof(*dle))
+ goto invalid;
+
+ if (memcmp(dle->subnqn, cdata->subnqn, sizeof(cdata->subnqn)) != 0)
goto invalid;
*nvlp = nvl;
@@ -264,7 +281,7 @@
}
static int
-nvmf_establish_connection(struct nvmf_softc *sc, const nvlist_t *nvl)
+nvmf_establish_connection(struct nvmf_softc *sc, nvlist_t *nvl)
{
const nvlist_t *const *io;
const nvlist_t *admin;
@@ -294,7 +311,7 @@
sc->io[i] = nvmf_init_qp(sc, trtype, io[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);
}
}
@@ -314,6 +331,10 @@
memcpy(sc->cdata, nvlist_get_binary(nvl, "cdata", NULL),
sizeof(*sc->cdata));
+ /* Save reconnect parameters. */
+ nvlist_destroy(sc->rparams);
+ sc->rparams = nvlist_take_nvlist(nvl, "rparams");
+
return (0);
}
@@ -467,7 +488,7 @@
{
struct make_dev_args mda;
struct nvmf_softc *sc = device_get_softc(dev);
- const nvlist_t *nvl = device_get_ivars(dev);
+ nvlist_t *nvl = device_get_ivars(dev);
const nvlist_t * const *io;
struct sysctl_oid *oid;
uint64_t val;
@@ -584,6 +605,7 @@
taskqueue_drain(taskqueue_thread, &sc->disconnect_task);
sx_destroy(&sc->connection_lock);
+ nvlist_destroy(sc->rparams);
free(sc->cdata, M_NVMF);
return (error);
}
@@ -837,6 +859,7 @@
nvmf_destroy_aer(sc);
sx_destroy(&sc->connection_lock);
+ nvlist_destroy(sc->rparams);
free(sc->cdata, M_NVMF);
return (0);
}
@@ -1053,21 +1076,12 @@
static int
nvmf_reconnect_params(struct nvmf_softc *sc, struct nvmf_ioc_nv *nv)
{
- nvlist_t *nvl;
int error;
- nvl = nvlist_create(0);
-
sx_slock(&sc->connection_lock);
- if ((sc->cdata->fcatt & 1) == 0)
- nvlist_add_number(nvl, "cntlid", NVMF_CNTLID_DYNAMIC);
- else
- nvlist_add_number(nvl, "cntlid", sc->cdata->ctrlr_id);
- nvlist_add_stringf(nvl, "subnqn", "%.256s", sc->cdata->subnqn);
+ error = nvmf_pack_ioc_nvlist(sc->rparams, nv);
sx_sunlock(&sc->connection_lock);
- error = nvmf_pack_ioc_nvlist(nvl, nv);
- nvlist_destroy(nvl);
return (error);
}
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
@@ -84,6 +84,8 @@
struct sysctl_oid_list *ioq_oid_list;
+ nvlist_t *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
@@ -71,13 +71,23 @@
* qpair handoff nvlist admin
* qpair handoff nvlist array io
* binary cdata struct nvme_controller_data
+ * NVMF_RECONNECT_PARAMS nvlist rparams
*/
/*
* The fields in the nvlist for NVMF_RECONNECT_PARAMS are:
*
- * number cntlid
- * string subnqn
+ * binary dle struct nvme_discovery_log_entry
+ * string hostnqn
+ * number num_io_queues
+ * number kato (optional)
+ * number io_qsize
+ * bool sq_flow_control
+ *
+ * TCP transport:
+ *
+ * bool header_digests
+ * bool data_digests
*/
/*

File Metadata

Mime Type
text/plain
Expires
Sat, Apr 25, 6:32 AM (13 h, 8 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
28426976
Default Alt Text
D48214.1777098771.diff (27 KB)

Event Timeline