Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F144988273
D48214.1777098771.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
27 KB
Referenced Files
None
Subscribers
None
D48214.1777098771.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D48214: nvmf: Refactor reconnection support
Attached
Detach File
Event Timeline
Log In to Comment