xref: /freebsd-src/lib/libnvmf/nvmf_host.c (revision 8bba2c0f8958443790b1f3abc0675719da987e87)
12da066efSJohn Baldwin /*-
22da066efSJohn Baldwin  * SPDX-License-Identifier: BSD-2-Clause
32da066efSJohn Baldwin  *
42da066efSJohn Baldwin  * Copyright (c) 2024 Chelsio Communications, Inc.
52da066efSJohn Baldwin  * Written by: John Baldwin <jhb@FreeBSD.org>
62da066efSJohn Baldwin  */
72da066efSJohn Baldwin 
82da066efSJohn Baldwin #include <sys/sysctl.h>
92da066efSJohn Baldwin #include <errno.h>
102da066efSJohn Baldwin #include <fcntl.h>
112da066efSJohn Baldwin #include <stdio.h>
122da066efSJohn Baldwin #include <stdlib.h>
132da066efSJohn Baldwin #include <string.h>
142da066efSJohn Baldwin #include <unistd.h>
152da066efSJohn Baldwin #include <uuid.h>
162da066efSJohn Baldwin 
172da066efSJohn Baldwin #include "libnvmf.h"
182da066efSJohn Baldwin #include "internal.h"
192da066efSJohn Baldwin 
202da066efSJohn Baldwin static void
212da066efSJohn Baldwin nvmf_init_sqe(void *sqe, uint8_t opcode)
222da066efSJohn Baldwin {
232da066efSJohn Baldwin 	struct nvme_command *cmd = sqe;
242da066efSJohn Baldwin 
252da066efSJohn Baldwin 	memset(cmd, 0, sizeof(*cmd));
262da066efSJohn Baldwin 	cmd->opc = opcode;
272da066efSJohn Baldwin }
282da066efSJohn Baldwin 
292da066efSJohn Baldwin static void
302da066efSJohn Baldwin nvmf_init_fabrics_sqe(void *sqe, uint8_t fctype)
312da066efSJohn Baldwin {
322da066efSJohn Baldwin 	struct nvmf_capsule_cmd *cmd = sqe;
332da066efSJohn Baldwin 
342da066efSJohn Baldwin 	nvmf_init_sqe(sqe, NVME_OPC_FABRICS_COMMANDS);
352da066efSJohn Baldwin 	cmd->fctype = fctype;
362da066efSJohn Baldwin }
372da066efSJohn Baldwin 
382da066efSJohn Baldwin struct nvmf_qpair *
392da066efSJohn Baldwin nvmf_connect(struct nvmf_association *na,
402da066efSJohn Baldwin     const struct nvmf_qpair_params *params, uint16_t qid, u_int queue_size,
412da066efSJohn Baldwin     const uint8_t hostid[16], uint16_t cntlid, const char *subnqn,
422da066efSJohn Baldwin     const char *hostnqn, uint32_t kato)
432da066efSJohn Baldwin {
442da066efSJohn Baldwin 	struct nvmf_fabric_connect_cmd cmd;
452da066efSJohn Baldwin 	struct nvmf_fabric_connect_data data;
462da066efSJohn Baldwin 	const struct nvmf_fabric_connect_rsp *rsp;
472da066efSJohn Baldwin 	struct nvmf_qpair *qp;
482da066efSJohn Baldwin 	struct nvmf_capsule *cc, *rc;
492da066efSJohn Baldwin 	int error;
502da066efSJohn Baldwin 	uint16_t sqhd, status;
512da066efSJohn Baldwin 
522da066efSJohn Baldwin 	qp = NULL;
532da066efSJohn Baldwin 	cc = NULL;
542da066efSJohn Baldwin 	rc = NULL;
552da066efSJohn Baldwin 	na_clear_error(na);
562da066efSJohn Baldwin 	if (na->na_controller) {
572da066efSJohn Baldwin 		na_error(na, "Cannot connect on a controller");
582da066efSJohn Baldwin 		goto error;
592da066efSJohn Baldwin 	}
602da066efSJohn Baldwin 
612da066efSJohn Baldwin 	if (params->admin != (qid == 0)) {
622da066efSJohn Baldwin 		na_error(na, "Admin queue must use Queue ID 0");
632da066efSJohn Baldwin 		goto error;
642da066efSJohn Baldwin 	}
652da066efSJohn Baldwin 
662da066efSJohn Baldwin 	if (qid == 0) {
672da066efSJohn Baldwin 		if (queue_size < NVME_MIN_ADMIN_ENTRIES ||
682da066efSJohn Baldwin 		    queue_size > NVME_MAX_ADMIN_ENTRIES) {
692da066efSJohn Baldwin 			na_error(na, "Invalid queue size %u", queue_size);
702da066efSJohn Baldwin 			goto error;
712da066efSJohn Baldwin 		}
722da066efSJohn Baldwin 	} else {
732da066efSJohn Baldwin 		if (queue_size < NVME_MIN_IO_ENTRIES ||
742da066efSJohn Baldwin 		    queue_size > NVME_MAX_IO_ENTRIES) {
752da066efSJohn Baldwin 			na_error(na, "Invalid queue size %u", queue_size);
762da066efSJohn Baldwin 			goto error;
772da066efSJohn Baldwin 		}
782da066efSJohn Baldwin 
792da066efSJohn Baldwin 		/* KATO is only for Admin queues. */
802da066efSJohn Baldwin 		if (kato != 0) {
812da066efSJohn Baldwin 			na_error(na, "Cannot set KATO on I/O queues");
822da066efSJohn Baldwin 			goto error;
832da066efSJohn Baldwin 		}
842da066efSJohn Baldwin 	}
852da066efSJohn Baldwin 
862da066efSJohn Baldwin 	qp = nvmf_allocate_qpair(na, params);
872da066efSJohn Baldwin 	if (qp == NULL)
882da066efSJohn Baldwin 		goto error;
892da066efSJohn Baldwin 
902da066efSJohn Baldwin 	nvmf_init_fabrics_sqe(&cmd, NVMF_FABRIC_COMMAND_CONNECT);
912da066efSJohn Baldwin 	cmd.recfmt = 0;
922da066efSJohn Baldwin 	cmd.qid = htole16(qid);
932da066efSJohn Baldwin 
942da066efSJohn Baldwin 	/* N.B. sqsize is 0's based. */
952da066efSJohn Baldwin 	cmd.sqsize = htole16(queue_size - 1);
962da066efSJohn Baldwin 	if (!na->na_params.sq_flow_control)
972da066efSJohn Baldwin 		cmd.cattr |= NVMF_CONNECT_ATTR_DISABLE_SQ_FC;
982da066efSJohn Baldwin 	cmd.kato = htole32(kato);
992da066efSJohn Baldwin 
1002da066efSJohn Baldwin 	cc = nvmf_allocate_command(qp, &cmd);
1012da066efSJohn Baldwin 	if (cc == NULL) {
1022da066efSJohn Baldwin 		na_error(na, "Failed to allocate command capsule: %s",
1032da066efSJohn Baldwin 		    strerror(errno));
1042da066efSJohn Baldwin 		goto error;
1052da066efSJohn Baldwin 	}
1062da066efSJohn Baldwin 
1072da066efSJohn Baldwin 	memset(&data, 0, sizeof(data));
1082da066efSJohn Baldwin 	memcpy(data.hostid, hostid, sizeof(data.hostid));
1092da066efSJohn Baldwin 	data.cntlid = htole16(cntlid);
1102da066efSJohn Baldwin 	strlcpy(data.subnqn, subnqn, sizeof(data.subnqn));
1112da066efSJohn Baldwin 	strlcpy(data.hostnqn, hostnqn, sizeof(data.hostnqn));
1122da066efSJohn Baldwin 
1132da066efSJohn Baldwin 	error = nvmf_capsule_append_data(cc, &data, sizeof(data), true);
1142da066efSJohn Baldwin 	if (error != 0) {
1152da066efSJohn Baldwin 		na_error(na, "Failed to append data to CONNECT capsule: %s",
1162da066efSJohn Baldwin 		    strerror(error));
1172da066efSJohn Baldwin 		goto error;
1182da066efSJohn Baldwin 	}
1192da066efSJohn Baldwin 
1202da066efSJohn Baldwin 	error = nvmf_transmit_capsule(cc);
1212da066efSJohn Baldwin 	if (error != 0) {
1222da066efSJohn Baldwin 		na_error(na, "Failed to transmit CONNECT capsule: %s",
1232da066efSJohn Baldwin 		    strerror(errno));
1242da066efSJohn Baldwin 		goto error;
1252da066efSJohn Baldwin 	}
1262da066efSJohn Baldwin 
1272da066efSJohn Baldwin 	error = nvmf_receive_capsule(qp, &rc);
1282da066efSJohn Baldwin 	if (error != 0) {
1292da066efSJohn Baldwin 		na_error(na, "Failed to receive CONNECT response: %s",
1302da066efSJohn Baldwin 		    strerror(error));
1312da066efSJohn Baldwin 		goto error;
1322da066efSJohn Baldwin 	}
1332da066efSJohn Baldwin 
1342da066efSJohn Baldwin 	rsp = (const struct nvmf_fabric_connect_rsp *)&rc->nc_cqe;
1352da066efSJohn Baldwin 	status = le16toh(rc->nc_cqe.status);
1362da066efSJohn Baldwin 	if (status != 0) {
1372da066efSJohn Baldwin 		if (NVME_STATUS_GET_SC(status) == NVMF_FABRIC_SC_INVALID_PARAM)
1382da066efSJohn Baldwin 			na_error(na,
1392da066efSJohn Baldwin 			    "CONNECT invalid parameter IATTR: %#x IPO: %#x",
1402da066efSJohn Baldwin 			    rsp->status_code_specific.invalid.iattr,
1412da066efSJohn Baldwin 			    rsp->status_code_specific.invalid.ipo);
1422da066efSJohn Baldwin 		else
1432da066efSJohn Baldwin 			na_error(na, "CONNECT failed, status %#x", status);
1442da066efSJohn Baldwin 		goto error;
1452da066efSJohn Baldwin 	}
1462da066efSJohn Baldwin 
1472da066efSJohn Baldwin 	if (rc->nc_cqe.cid != cmd.cid) {
1482da066efSJohn Baldwin 		na_error(na, "Mismatched CID in CONNECT response");
1492da066efSJohn Baldwin 		goto error;
1502da066efSJohn Baldwin 	}
1512da066efSJohn Baldwin 
1522da066efSJohn Baldwin 	if (!rc->nc_sqhd_valid) {
1532da066efSJohn Baldwin 		na_error(na, "CONNECT response without valid SQHD");
1542da066efSJohn Baldwin 		goto error;
1552da066efSJohn Baldwin 	}
1562da066efSJohn Baldwin 
1572da066efSJohn Baldwin 	sqhd = le16toh(rsp->sqhd);
1582da066efSJohn Baldwin 	if (sqhd == 0xffff) {
1592da066efSJohn Baldwin 		if (na->na_params.sq_flow_control) {
1602da066efSJohn Baldwin 			na_error(na, "Controller disabled SQ flow control");
1612da066efSJohn Baldwin 			goto error;
1622da066efSJohn Baldwin 		}
1632da066efSJohn Baldwin 		qp->nq_flow_control = false;
1642da066efSJohn Baldwin 	} else {
1652da066efSJohn Baldwin 		qp->nq_flow_control = true;
1662da066efSJohn Baldwin 		qp->nq_sqhd = sqhd;
1672da066efSJohn Baldwin 		qp->nq_sqtail = sqhd;
1682da066efSJohn Baldwin 	}
1692da066efSJohn Baldwin 
1702da066efSJohn Baldwin 	if (rsp->status_code_specific.success.authreq) {
1712da066efSJohn Baldwin 		na_error(na, "CONNECT response requests authentication\n");
1722da066efSJohn Baldwin 		goto error;
1732da066efSJohn Baldwin 	}
1742da066efSJohn Baldwin 
1752da066efSJohn Baldwin 	qp->nq_qsize = queue_size;
1762da066efSJohn Baldwin 	qp->nq_cntlid = le16toh(rsp->status_code_specific.success.cntlid);
1772da066efSJohn Baldwin 	qp->nq_kato = kato;
1782da066efSJohn Baldwin 	/* XXX: Save qid in qp? */
1792da066efSJohn Baldwin 	return (qp);
1802da066efSJohn Baldwin 
1812da066efSJohn Baldwin error:
1822da066efSJohn Baldwin 	if (rc != NULL)
1832da066efSJohn Baldwin 		nvmf_free_capsule(rc);
1842da066efSJohn Baldwin 	if (cc != NULL)
1852da066efSJohn Baldwin 		nvmf_free_capsule(cc);
1862da066efSJohn Baldwin 	if (qp != NULL)
1872da066efSJohn Baldwin 		nvmf_free_qpair(qp);
1882da066efSJohn Baldwin 	return (NULL);
1892da066efSJohn Baldwin }
1902da066efSJohn Baldwin 
1912da066efSJohn Baldwin uint16_t
1922da066efSJohn Baldwin nvmf_cntlid(struct nvmf_qpair *qp)
1932da066efSJohn Baldwin {
1942da066efSJohn Baldwin 	return (qp->nq_cntlid);
1952da066efSJohn Baldwin }
1962da066efSJohn Baldwin 
1972da066efSJohn Baldwin int
1982da066efSJohn Baldwin nvmf_host_transmit_command(struct nvmf_capsule *nc)
1992da066efSJohn Baldwin {
2002da066efSJohn Baldwin 	struct nvmf_qpair *qp = nc->nc_qpair;
2012da066efSJohn Baldwin 	uint16_t new_sqtail;
2022da066efSJohn Baldwin 	int error;
2032da066efSJohn Baldwin 
2042da066efSJohn Baldwin 	/* Fail if the queue is full. */
2052da066efSJohn Baldwin 	new_sqtail = (qp->nq_sqtail + 1) % qp->nq_qsize;
2062da066efSJohn Baldwin 	if (new_sqtail == qp->nq_sqhd)
2072da066efSJohn Baldwin 		return (EBUSY);
2082da066efSJohn Baldwin 
2092da066efSJohn Baldwin 	nc->nc_sqe.cid = htole16(qp->nq_cid);
2102da066efSJohn Baldwin 
2112da066efSJohn Baldwin 	/* 4.2 Skip CID of 0xFFFF. */
2122da066efSJohn Baldwin 	qp->nq_cid++;
2132da066efSJohn Baldwin 	if (qp->nq_cid == 0xFFFF)
2142da066efSJohn Baldwin 		qp->nq_cid = 0;
2152da066efSJohn Baldwin 
2162da066efSJohn Baldwin 	error = nvmf_transmit_capsule(nc);
2172da066efSJohn Baldwin 	if (error != 0)
2182da066efSJohn Baldwin 		return (error);
2192da066efSJohn Baldwin 
2202da066efSJohn Baldwin 	qp->nq_sqtail = new_sqtail;
2212da066efSJohn Baldwin 	return (0);
2222da066efSJohn Baldwin }
2232da066efSJohn Baldwin 
2242da066efSJohn Baldwin /* Receive a single capsule and update SQ FC accounting. */
2252da066efSJohn Baldwin static int
2262da066efSJohn Baldwin nvmf_host_receive_capsule(struct nvmf_qpair *qp, struct nvmf_capsule **ncp)
2272da066efSJohn Baldwin {
2282da066efSJohn Baldwin 	struct nvmf_capsule *nc;
2292da066efSJohn Baldwin 	int error;
2302da066efSJohn Baldwin 
2312da066efSJohn Baldwin 	/* If the SQ is empty, there is no response to wait for. */
2322da066efSJohn Baldwin 	if (qp->nq_sqhd == qp->nq_sqtail)
2332da066efSJohn Baldwin 		return (EWOULDBLOCK);
2342da066efSJohn Baldwin 
2352da066efSJohn Baldwin 	error = nvmf_receive_capsule(qp, &nc);
2362da066efSJohn Baldwin 	if (error != 0)
2372da066efSJohn Baldwin 		return (error);
2382da066efSJohn Baldwin 
2392da066efSJohn Baldwin 	if (qp->nq_flow_control) {
2402da066efSJohn Baldwin 		if (nc->nc_sqhd_valid)
2412da066efSJohn Baldwin 			qp->nq_sqhd = le16toh(nc->nc_cqe.sqhd);
2422da066efSJohn Baldwin 	} else {
2432da066efSJohn Baldwin 		/*
2442da066efSJohn Baldwin 		 * If SQ FC is disabled, just advance the head for
2452da066efSJohn Baldwin 		 * each response capsule received so that we track the
2462da066efSJohn Baldwin 		 * number of outstanding commands.
2472da066efSJohn Baldwin 		 */
2482da066efSJohn Baldwin 		qp->nq_sqhd = (qp->nq_sqhd + 1) % qp->nq_qsize;
2492da066efSJohn Baldwin 	}
2502da066efSJohn Baldwin 	*ncp = nc;
2512da066efSJohn Baldwin 	return (0);
2522da066efSJohn Baldwin }
2532da066efSJohn Baldwin 
2542da066efSJohn Baldwin int
2552da066efSJohn Baldwin nvmf_host_receive_response(struct nvmf_qpair *qp, struct nvmf_capsule **ncp)
2562da066efSJohn Baldwin {
2572da066efSJohn Baldwin 	struct nvmf_capsule *nc;
2582da066efSJohn Baldwin 
2592da066efSJohn Baldwin 	/* Return the oldest previously received response. */
2602da066efSJohn Baldwin 	if (!TAILQ_EMPTY(&qp->nq_rx_capsules)) {
2612da066efSJohn Baldwin 		nc = TAILQ_FIRST(&qp->nq_rx_capsules);
2622da066efSJohn Baldwin 		TAILQ_REMOVE(&qp->nq_rx_capsules, nc, nc_link);
2632da066efSJohn Baldwin 		*ncp = nc;
2642da066efSJohn Baldwin 		return (0);
2652da066efSJohn Baldwin 	}
2662da066efSJohn Baldwin 
2672da066efSJohn Baldwin 	return (nvmf_host_receive_capsule(qp, ncp));
2682da066efSJohn Baldwin }
2692da066efSJohn Baldwin 
2702da066efSJohn Baldwin int
2712da066efSJohn Baldwin nvmf_host_wait_for_response(struct nvmf_capsule *cc,
2722da066efSJohn Baldwin     struct nvmf_capsule **rcp)
2732da066efSJohn Baldwin {
2742da066efSJohn Baldwin 	struct nvmf_qpair *qp = cc->nc_qpair;
2752da066efSJohn Baldwin 	struct nvmf_capsule *rc;
2762da066efSJohn Baldwin 	int error;
2772da066efSJohn Baldwin 
2782da066efSJohn Baldwin 	/* Check if a response was already received. */
2792da066efSJohn Baldwin 	TAILQ_FOREACH(rc, &qp->nq_rx_capsules, nc_link) {
2802da066efSJohn Baldwin 		if (rc->nc_cqe.cid == cc->nc_sqe.cid) {
2812da066efSJohn Baldwin 			TAILQ_REMOVE(&qp->nq_rx_capsules, rc, nc_link);
2822da066efSJohn Baldwin 			*rcp = rc;
2832da066efSJohn Baldwin 			return (0);
2842da066efSJohn Baldwin 		}
2852da066efSJohn Baldwin 	}
2862da066efSJohn Baldwin 
2872da066efSJohn Baldwin 	/* Wait for a response. */
2882da066efSJohn Baldwin 	for (;;) {
2892da066efSJohn Baldwin 		error = nvmf_host_receive_capsule(qp, &rc);
2902da066efSJohn Baldwin 		if (error != 0)
2912da066efSJohn Baldwin 			return (error);
2922da066efSJohn Baldwin 
2932da066efSJohn Baldwin 		if (rc->nc_cqe.cid != cc->nc_sqe.cid) {
2942da066efSJohn Baldwin 			TAILQ_INSERT_TAIL(&qp->nq_rx_capsules, rc, nc_link);
2952da066efSJohn Baldwin 			continue;
2962da066efSJohn Baldwin 		}
2972da066efSJohn Baldwin 
2982da066efSJohn Baldwin 		*rcp = rc;
2992da066efSJohn Baldwin 		return (0);
3002da066efSJohn Baldwin 	}
3012da066efSJohn Baldwin }
3022da066efSJohn Baldwin 
3032da066efSJohn Baldwin struct nvmf_capsule *
3042da066efSJohn Baldwin nvmf_keepalive(struct nvmf_qpair *qp)
3052da066efSJohn Baldwin {
3062da066efSJohn Baldwin 	struct nvme_command cmd;
3072da066efSJohn Baldwin 
3082da066efSJohn Baldwin 	if (!qp->nq_admin) {
3092da066efSJohn Baldwin 		errno = EINVAL;
3102da066efSJohn Baldwin 		return (NULL);
3112da066efSJohn Baldwin 	}
3122da066efSJohn Baldwin 
3132da066efSJohn Baldwin 	nvmf_init_sqe(&cmd, NVME_OPC_KEEP_ALIVE);
3142da066efSJohn Baldwin 
3152da066efSJohn Baldwin 	return (nvmf_allocate_command(qp, &cmd));
3162da066efSJohn Baldwin }
3172da066efSJohn Baldwin 
3182da066efSJohn Baldwin static struct nvmf_capsule *
3192da066efSJohn Baldwin nvmf_get_property(struct nvmf_qpair *qp, uint32_t offset, uint8_t size)
3202da066efSJohn Baldwin {
3212da066efSJohn Baldwin 	struct nvmf_fabric_prop_get_cmd cmd;
3222da066efSJohn Baldwin 
3232da066efSJohn Baldwin 	nvmf_init_fabrics_sqe(&cmd, NVMF_FABRIC_COMMAND_PROPERTY_GET);
3242da066efSJohn Baldwin 	switch (size) {
3252da066efSJohn Baldwin 	case 4:
3262da066efSJohn Baldwin 		cmd.attrib.size = NVMF_PROP_SIZE_4;
3272da066efSJohn Baldwin 		break;
3282da066efSJohn Baldwin 	case 8:
3292da066efSJohn Baldwin 		cmd.attrib.size = NVMF_PROP_SIZE_8;
3302da066efSJohn Baldwin 		break;
3312da066efSJohn Baldwin 	default:
3322da066efSJohn Baldwin 		errno = EINVAL;
3332da066efSJohn Baldwin 		return (NULL);
3342da066efSJohn Baldwin 	}
3352da066efSJohn Baldwin 	cmd.ofst = htole32(offset);
3362da066efSJohn Baldwin 
3372da066efSJohn Baldwin 	return (nvmf_allocate_command(qp, &cmd));
3382da066efSJohn Baldwin }
3392da066efSJohn Baldwin 
3402da066efSJohn Baldwin int
3412da066efSJohn Baldwin nvmf_read_property(struct nvmf_qpair *qp, uint32_t offset, uint8_t size,
3422da066efSJohn Baldwin     uint64_t *value)
3432da066efSJohn Baldwin {
3442da066efSJohn Baldwin 	struct nvmf_capsule *cc, *rc;
3452da066efSJohn Baldwin 	const struct nvmf_fabric_prop_get_rsp *rsp;
3462da066efSJohn Baldwin 	uint16_t status;
3472da066efSJohn Baldwin 	int error;
3482da066efSJohn Baldwin 
3492da066efSJohn Baldwin 	if (!qp->nq_admin)
3502da066efSJohn Baldwin 		return (EINVAL);
3512da066efSJohn Baldwin 
3522da066efSJohn Baldwin 	cc = nvmf_get_property(qp, offset, size);
3532da066efSJohn Baldwin 	if (cc == NULL)
3542da066efSJohn Baldwin 		return (errno);
3552da066efSJohn Baldwin 
3562da066efSJohn Baldwin 	error = nvmf_host_transmit_command(cc);
3572da066efSJohn Baldwin 	if (error != 0) {
3582da066efSJohn Baldwin 		nvmf_free_capsule(cc);
3592da066efSJohn Baldwin 		return (error);
3602da066efSJohn Baldwin 	}
3612da066efSJohn Baldwin 
3622da066efSJohn Baldwin 	error = nvmf_host_wait_for_response(cc, &rc);
3632da066efSJohn Baldwin 	nvmf_free_capsule(cc);
3642da066efSJohn Baldwin 	if (error != 0)
3652da066efSJohn Baldwin 		return (error);
3662da066efSJohn Baldwin 
3672da066efSJohn Baldwin 	rsp = (const struct nvmf_fabric_prop_get_rsp *)&rc->nc_cqe;
3682da066efSJohn Baldwin 	status = le16toh(rc->nc_cqe.status);
3692da066efSJohn Baldwin 	if (status != 0) {
3702da066efSJohn Baldwin 		printf("NVMF: PROPERTY_GET failed, status %#x\n", status);
3712da066efSJohn Baldwin 		nvmf_free_capsule(rc);
3722da066efSJohn Baldwin 		return (EIO);
3732da066efSJohn Baldwin 	}
3742da066efSJohn Baldwin 
3752da066efSJohn Baldwin 	if (size == 8)
3762da066efSJohn Baldwin 		*value = le64toh(rsp->value.u64);
3772da066efSJohn Baldwin 	else
3782da066efSJohn Baldwin 		*value = le32toh(rsp->value.u32.low);
3792da066efSJohn Baldwin 	nvmf_free_capsule(rc);
3802da066efSJohn Baldwin 	return (0);
3812da066efSJohn Baldwin }
3822da066efSJohn Baldwin 
3832da066efSJohn Baldwin static struct nvmf_capsule *
3842da066efSJohn Baldwin nvmf_set_property(struct nvmf_qpair *qp, uint32_t offset, uint8_t size,
3852da066efSJohn Baldwin     uint64_t value)
3862da066efSJohn Baldwin {
3872da066efSJohn Baldwin 	struct nvmf_fabric_prop_set_cmd cmd;
3882da066efSJohn Baldwin 
3892da066efSJohn Baldwin 	nvmf_init_fabrics_sqe(&cmd, NVMF_FABRIC_COMMAND_PROPERTY_SET);
3902da066efSJohn Baldwin 	switch (size) {
3912da066efSJohn Baldwin 	case 4:
3922da066efSJohn Baldwin 		cmd.attrib.size = NVMF_PROP_SIZE_4;
3932da066efSJohn Baldwin 		cmd.value.u32.low = htole32(value);
3942da066efSJohn Baldwin 		break;
3952da066efSJohn Baldwin 	case 8:
3962da066efSJohn Baldwin 		cmd.attrib.size = NVMF_PROP_SIZE_8;
3972da066efSJohn Baldwin 		cmd.value.u64 = htole64(value);
3982da066efSJohn Baldwin 		break;
3992da066efSJohn Baldwin 	default:
4002da066efSJohn Baldwin 		errno = EINVAL;
4012da066efSJohn Baldwin 		return (NULL);
4022da066efSJohn Baldwin 	}
4032da066efSJohn Baldwin 	cmd.ofst = htole32(offset);
4042da066efSJohn Baldwin 
4052da066efSJohn Baldwin 	return (nvmf_allocate_command(qp, &cmd));
4062da066efSJohn Baldwin }
4072da066efSJohn Baldwin 
4082da066efSJohn Baldwin int
4092da066efSJohn Baldwin nvmf_write_property(struct nvmf_qpair *qp, uint32_t offset, uint8_t size,
4102da066efSJohn Baldwin     uint64_t value)
4112da066efSJohn Baldwin {
4122da066efSJohn Baldwin 	struct nvmf_capsule *cc, *rc;
4132da066efSJohn Baldwin 	uint16_t status;
4142da066efSJohn Baldwin 	int error;
4152da066efSJohn Baldwin 
4162da066efSJohn Baldwin 	if (!qp->nq_admin)
4172da066efSJohn Baldwin 		return (EINVAL);
4182da066efSJohn Baldwin 
4192da066efSJohn Baldwin 	cc = nvmf_set_property(qp, offset, size, value);
4202da066efSJohn Baldwin 	if (cc == NULL)
4212da066efSJohn Baldwin 		return (errno);
4222da066efSJohn Baldwin 
4232da066efSJohn Baldwin 	error = nvmf_host_transmit_command(cc);
4242da066efSJohn Baldwin 	if (error != 0) {
4252da066efSJohn Baldwin 		nvmf_free_capsule(cc);
4262da066efSJohn Baldwin 		return (error);
4272da066efSJohn Baldwin 	}
4282da066efSJohn Baldwin 
4292da066efSJohn Baldwin 	error = nvmf_host_wait_for_response(cc, &rc);
4302da066efSJohn Baldwin 	nvmf_free_capsule(cc);
4312da066efSJohn Baldwin 	if (error != 0)
4322da066efSJohn Baldwin 		return (error);
4332da066efSJohn Baldwin 
4342da066efSJohn Baldwin 	status = le16toh(rc->nc_cqe.status);
4352da066efSJohn Baldwin 	if (status != 0) {
4362da066efSJohn Baldwin 		printf("NVMF: PROPERTY_SET failed, status %#x\n", status);
4372da066efSJohn Baldwin 		nvmf_free_capsule(rc);
4382da066efSJohn Baldwin 		return (EIO);
4392da066efSJohn Baldwin 	}
4402da066efSJohn Baldwin 
4412da066efSJohn Baldwin 	nvmf_free_capsule(rc);
4422da066efSJohn Baldwin 	return (0);
4432da066efSJohn Baldwin }
4442da066efSJohn Baldwin 
4452da066efSJohn Baldwin int
4462da066efSJohn Baldwin nvmf_hostid_from_hostuuid(uint8_t hostid[16])
4472da066efSJohn Baldwin {
4482da066efSJohn Baldwin 	char hostuuid_str[64];
4492da066efSJohn Baldwin 	uuid_t hostuuid;
4502da066efSJohn Baldwin 	size_t len;
4512da066efSJohn Baldwin 	uint32_t status;
4522da066efSJohn Baldwin 
4532da066efSJohn Baldwin 	len = sizeof(hostuuid_str);
4542da066efSJohn Baldwin 	if (sysctlbyname("kern.hostuuid", hostuuid_str, &len, NULL, 0) != 0)
4552da066efSJohn Baldwin 		return (errno);
4562da066efSJohn Baldwin 
4572da066efSJohn Baldwin 	uuid_from_string(hostuuid_str, &hostuuid, &status);
4582da066efSJohn Baldwin 	switch (status) {
4592da066efSJohn Baldwin 	case uuid_s_ok:
4602da066efSJohn Baldwin 		break;
4612da066efSJohn Baldwin 	case uuid_s_no_memory:
4622da066efSJohn Baldwin 		return (ENOMEM);
4632da066efSJohn Baldwin 	default:
4642da066efSJohn Baldwin 		return (EINVAL);
4652da066efSJohn Baldwin 	}
4662da066efSJohn Baldwin 
4672da066efSJohn Baldwin 	uuid_enc_le(hostid, &hostuuid);
4682da066efSJohn Baldwin 	return (0);
4692da066efSJohn Baldwin }
4702da066efSJohn Baldwin 
4712da066efSJohn Baldwin int
4722da066efSJohn Baldwin nvmf_nqn_from_hostuuid(char nqn[NVMF_NQN_MAX_LEN])
4732da066efSJohn Baldwin {
4742da066efSJohn Baldwin 	char hostuuid_str[64];
4752da066efSJohn Baldwin 	size_t len;
4762da066efSJohn Baldwin 
4772da066efSJohn Baldwin 	len = sizeof(hostuuid_str);
4782da066efSJohn Baldwin 	if (sysctlbyname("kern.hostuuid", hostuuid_str, &len, NULL, 0) != 0)
4792da066efSJohn Baldwin 		return (errno);
4802da066efSJohn Baldwin 
4812da066efSJohn Baldwin 	strlcpy(nqn, NVMF_NQN_UUID_PRE, NVMF_NQN_MAX_LEN);
4822da066efSJohn Baldwin 	strlcat(nqn, hostuuid_str, NVMF_NQN_MAX_LEN);
4832da066efSJohn Baldwin 	return (0);
4842da066efSJohn Baldwin }
4852da066efSJohn Baldwin 
4862da066efSJohn Baldwin int
4872da066efSJohn Baldwin nvmf_host_identify_controller(struct nvmf_qpair *qp,
4882da066efSJohn Baldwin     struct nvme_controller_data *cdata)
4892da066efSJohn Baldwin {
4902da066efSJohn Baldwin 	struct nvme_command cmd;
4912da066efSJohn Baldwin 	struct nvmf_capsule *cc, *rc;
4922da066efSJohn Baldwin 	int error;
4932da066efSJohn Baldwin 	uint16_t status;
4942da066efSJohn Baldwin 
4952da066efSJohn Baldwin 	if (!qp->nq_admin)
4962da066efSJohn Baldwin 		return (EINVAL);
4972da066efSJohn Baldwin 
4982da066efSJohn Baldwin 	nvmf_init_sqe(&cmd, NVME_OPC_IDENTIFY);
4992da066efSJohn Baldwin 
5002da066efSJohn Baldwin 	/* 5.15.1 Use CNS of 0x01 for controller data. */
5012da066efSJohn Baldwin 	cmd.cdw10 = htole32(1);
5022da066efSJohn Baldwin 
5032da066efSJohn Baldwin 	cc = nvmf_allocate_command(qp, &cmd);
5042da066efSJohn Baldwin 	if (cc == NULL)
5052da066efSJohn Baldwin 		return (errno);
5062da066efSJohn Baldwin 
5072da066efSJohn Baldwin 	error = nvmf_capsule_append_data(cc, cdata, sizeof(*cdata), false);
5082da066efSJohn Baldwin 	if (error != 0) {
5092da066efSJohn Baldwin 		nvmf_free_capsule(cc);
5102da066efSJohn Baldwin 		return (error);
5112da066efSJohn Baldwin 	}
5122da066efSJohn Baldwin 
5132da066efSJohn Baldwin 	error = nvmf_host_transmit_command(cc);
5142da066efSJohn Baldwin 	if (error != 0) {
5152da066efSJohn Baldwin 		nvmf_free_capsule(cc);
5162da066efSJohn Baldwin 		return (error);
5172da066efSJohn Baldwin 	}
5182da066efSJohn Baldwin 
5192da066efSJohn Baldwin 	error = nvmf_host_wait_for_response(cc, &rc);
5202da066efSJohn Baldwin 	nvmf_free_capsule(cc);
5212da066efSJohn Baldwin 	if (error != 0)
5222da066efSJohn Baldwin 		return (error);
5232da066efSJohn Baldwin 
5242da066efSJohn Baldwin 	status = le16toh(rc->nc_cqe.status);
5252da066efSJohn Baldwin 	if (status != 0) {
5262da066efSJohn Baldwin 		printf("NVMF: IDENTIFY failed, status %#x\n", status);
5272da066efSJohn Baldwin 		nvmf_free_capsule(rc);
5282da066efSJohn Baldwin 		return (EIO);
5292da066efSJohn Baldwin 	}
5302da066efSJohn Baldwin 
5312da066efSJohn Baldwin 	nvmf_free_capsule(rc);
5322da066efSJohn Baldwin 	return (0);
5332da066efSJohn Baldwin }
5342da066efSJohn Baldwin 
5352da066efSJohn Baldwin int
5362da066efSJohn Baldwin nvmf_host_identify_namespace(struct nvmf_qpair *qp, uint32_t nsid,
5372da066efSJohn Baldwin     struct nvme_namespace_data *nsdata)
5382da066efSJohn Baldwin {
5392da066efSJohn Baldwin 	struct nvme_command cmd;
5402da066efSJohn Baldwin 	struct nvmf_capsule *cc, *rc;
5412da066efSJohn Baldwin 	int error;
5422da066efSJohn Baldwin 	uint16_t status;
5432da066efSJohn Baldwin 
5442da066efSJohn Baldwin 	if (!qp->nq_admin)
5452da066efSJohn Baldwin 		return (EINVAL);
5462da066efSJohn Baldwin 
5472da066efSJohn Baldwin 	nvmf_init_sqe(&cmd, NVME_OPC_IDENTIFY);
5482da066efSJohn Baldwin 
5492da066efSJohn Baldwin 	/* 5.15.1 Use CNS of 0x00 for namespace data. */
5502da066efSJohn Baldwin 	cmd.cdw10 = htole32(0);
5512da066efSJohn Baldwin 	cmd.nsid = htole32(nsid);
5522da066efSJohn Baldwin 
5532da066efSJohn Baldwin 	cc = nvmf_allocate_command(qp, &cmd);
5542da066efSJohn Baldwin 	if (cc == NULL)
5552da066efSJohn Baldwin 		return (errno);
5562da066efSJohn Baldwin 
5572da066efSJohn Baldwin 	error = nvmf_capsule_append_data(cc, nsdata, sizeof(*nsdata), false);
5582da066efSJohn Baldwin 	if (error != 0) {
5592da066efSJohn Baldwin 		nvmf_free_capsule(cc);
5602da066efSJohn Baldwin 		return (error);
5612da066efSJohn Baldwin 	}
5622da066efSJohn Baldwin 
5632da066efSJohn Baldwin 	error = nvmf_host_transmit_command(cc);
5642da066efSJohn Baldwin 	if (error != 0) {
5652da066efSJohn Baldwin 		nvmf_free_capsule(cc);
5662da066efSJohn Baldwin 		return (error);
5672da066efSJohn Baldwin 	}
5682da066efSJohn Baldwin 
5692da066efSJohn Baldwin 	error = nvmf_host_wait_for_response(cc, &rc);
5702da066efSJohn Baldwin 	nvmf_free_capsule(cc);
5712da066efSJohn Baldwin 	if (error != 0)
5722da066efSJohn Baldwin 		return (error);
5732da066efSJohn Baldwin 
5742da066efSJohn Baldwin 	status = le16toh(rc->nc_cqe.status);
5752da066efSJohn Baldwin 	if (status != 0) {
5762da066efSJohn Baldwin 		printf("NVMF: IDENTIFY failed, status %#x\n", status);
5772da066efSJohn Baldwin 		nvmf_free_capsule(rc);
5782da066efSJohn Baldwin 		return (EIO);
5792da066efSJohn Baldwin 	}
5802da066efSJohn Baldwin 
5812da066efSJohn Baldwin 	nvmf_free_capsule(rc);
5822da066efSJohn Baldwin 	return (0);
5832da066efSJohn Baldwin }
5842da066efSJohn Baldwin 
5852da066efSJohn Baldwin static int
5862da066efSJohn Baldwin nvmf_get_discovery_log_page(struct nvmf_qpair *qp, uint64_t offset, void *buf,
5872da066efSJohn Baldwin     size_t len)
5882da066efSJohn Baldwin {
5892da066efSJohn Baldwin 	struct nvme_command cmd;
5902da066efSJohn Baldwin 	struct nvmf_capsule *cc, *rc;
5912da066efSJohn Baldwin 	size_t numd;
5922da066efSJohn Baldwin 	int error;
5932da066efSJohn Baldwin 	uint16_t status;
5942da066efSJohn Baldwin 
5952da066efSJohn Baldwin 	if (len % 4 != 0 || len == 0 || offset % 4 != 0)
5962da066efSJohn Baldwin 		return (EINVAL);
5972da066efSJohn Baldwin 
5982da066efSJohn Baldwin 	numd = (len / 4) - 1;
5992da066efSJohn Baldwin 	nvmf_init_sqe(&cmd, NVME_OPC_GET_LOG_PAGE);
6002da066efSJohn Baldwin 	cmd.cdw10 = htole32(numd << 16 | NVME_LOG_DISCOVERY);
6012da066efSJohn Baldwin 	cmd.cdw11 = htole32(numd >> 16);
6022da066efSJohn Baldwin 	cmd.cdw12 = htole32(offset);
6032da066efSJohn Baldwin 	cmd.cdw13 = htole32(offset >> 32);
6042da066efSJohn Baldwin 
6052da066efSJohn Baldwin 	cc = nvmf_allocate_command(qp, &cmd);
6062da066efSJohn Baldwin 	if (cc == NULL)
6072da066efSJohn Baldwin 		return (errno);
6082da066efSJohn Baldwin 
6092da066efSJohn Baldwin 	error = nvmf_capsule_append_data(cc, buf, len, false);
6102da066efSJohn Baldwin 	if (error != 0) {
6112da066efSJohn Baldwin 		nvmf_free_capsule(cc);
6122da066efSJohn Baldwin 		return (error);
6132da066efSJohn Baldwin 	}
6142da066efSJohn Baldwin 
6152da066efSJohn Baldwin 	error = nvmf_host_transmit_command(cc);
6162da066efSJohn Baldwin 	if (error != 0) {
6172da066efSJohn Baldwin 		nvmf_free_capsule(cc);
6182da066efSJohn Baldwin 		return (error);
6192da066efSJohn Baldwin 	}
6202da066efSJohn Baldwin 
6212da066efSJohn Baldwin 	error = nvmf_host_wait_for_response(cc, &rc);
6222da066efSJohn Baldwin 	nvmf_free_capsule(cc);
6232da066efSJohn Baldwin 	if (error != 0)
6242da066efSJohn Baldwin 		return (error);
6252da066efSJohn Baldwin 
6262da066efSJohn Baldwin 	status = le16toh(rc->nc_cqe.status);
6272da066efSJohn Baldwin 	if (NVMEV(NVME_STATUS_SC, status) ==
6282da066efSJohn Baldwin 	    NVMF_FABRIC_SC_LOG_RESTART_DISCOVERY) {
6292da066efSJohn Baldwin 		nvmf_free_capsule(rc);
6302da066efSJohn Baldwin 		return (EAGAIN);
6312da066efSJohn Baldwin 	}
6322da066efSJohn Baldwin 	if (status != 0) {
6332da066efSJohn Baldwin 		printf("NVMF: GET_LOG_PAGE failed, status %#x\n", status);
6342da066efSJohn Baldwin 		nvmf_free_capsule(rc);
6352da066efSJohn Baldwin 		return (EIO);
6362da066efSJohn Baldwin 	}
6372da066efSJohn Baldwin 
6382da066efSJohn Baldwin 	nvmf_free_capsule(rc);
6392da066efSJohn Baldwin 	return (0);
6402da066efSJohn Baldwin }
6412da066efSJohn Baldwin 
6422da066efSJohn Baldwin int
6432da066efSJohn Baldwin nvmf_host_fetch_discovery_log_page(struct nvmf_qpair *qp,
6442da066efSJohn Baldwin     struct nvme_discovery_log **logp)
6452da066efSJohn Baldwin {
6462da066efSJohn Baldwin 	struct nvme_discovery_log hdr, *log;
6472da066efSJohn Baldwin 	size_t payload_len;
6482da066efSJohn Baldwin 	int error;
6492da066efSJohn Baldwin 
6502da066efSJohn Baldwin 	if (!qp->nq_admin)
6512da066efSJohn Baldwin 		return (EINVAL);
6522da066efSJohn Baldwin 
6532da066efSJohn Baldwin 	log = NULL;
6542da066efSJohn Baldwin 	for (;;) {
6552da066efSJohn Baldwin 		error = nvmf_get_discovery_log_page(qp, 0, &hdr, sizeof(hdr));
656408572a2SPierre Pronchery 		if (error != 0) {
657408572a2SPierre Pronchery 			free(log);
6582da066efSJohn Baldwin 			return (error);
659408572a2SPierre Pronchery 		}
6602da066efSJohn Baldwin 		nvme_discovery_log_swapbytes(&hdr);
6612da066efSJohn Baldwin 
6622da066efSJohn Baldwin 		if (hdr.recfmt != 0) {
6632da066efSJohn Baldwin 			printf("NVMF: Unsupported discovery log format: %d\n",
6642da066efSJohn Baldwin 			    hdr.recfmt);
665408572a2SPierre Pronchery 			free(log);
6662da066efSJohn Baldwin 			return (EINVAL);
6672da066efSJohn Baldwin 		}
6682da066efSJohn Baldwin 
6692da066efSJohn Baldwin 		if (hdr.numrec > 1024) {
6702da066efSJohn Baldwin 			printf("NVMF: Too many discovery log entries: %ju\n",
6712da066efSJohn Baldwin 			    (uintmax_t)hdr.numrec);
672408572a2SPierre Pronchery 			free(log);
6732da066efSJohn Baldwin 			return (EFBIG);
6742da066efSJohn Baldwin 		}
6752da066efSJohn Baldwin 
6762da066efSJohn Baldwin 		payload_len = sizeof(log->entries[0]) * hdr.numrec;
6772da066efSJohn Baldwin 		log = reallocf(log, sizeof(*log) + payload_len);
6782da066efSJohn Baldwin 		if (log == NULL)
6792da066efSJohn Baldwin 			return (ENOMEM);
6802da066efSJohn Baldwin 		*log = hdr;
6812da066efSJohn Baldwin 		if (hdr.numrec == 0)
6822da066efSJohn Baldwin 			break;
6832da066efSJohn Baldwin 
6842da066efSJohn Baldwin 		error = nvmf_get_discovery_log_page(qp, sizeof(hdr),
6852da066efSJohn Baldwin 		    log->entries, payload_len);
6862da066efSJohn Baldwin 		if (error == EAGAIN)
6872da066efSJohn Baldwin 			continue;
6882da066efSJohn Baldwin 		if (error != 0) {
6892da066efSJohn Baldwin 			free(log);
6902da066efSJohn Baldwin 			return (error);
6912da066efSJohn Baldwin 		}
6922da066efSJohn Baldwin 
6932da066efSJohn Baldwin 		/* Re-read the header and check the generation count. */
6942da066efSJohn Baldwin 		error = nvmf_get_discovery_log_page(qp, 0, &hdr, sizeof(hdr));
6952da066efSJohn Baldwin 		if (error != 0) {
6962da066efSJohn Baldwin 			free(log);
6972da066efSJohn Baldwin 			return (error);
6982da066efSJohn Baldwin 		}
6992da066efSJohn Baldwin 		nvme_discovery_log_swapbytes(&hdr);
7002da066efSJohn Baldwin 
7012da066efSJohn Baldwin 		if (log->genctr != hdr.genctr)
7022da066efSJohn Baldwin 			continue;
7032da066efSJohn Baldwin 
7042da066efSJohn Baldwin 		for (u_int i = 0; i < log->numrec; i++)
7052da066efSJohn Baldwin 			nvme_discovery_log_entry_swapbytes(&log->entries[i]);
7062da066efSJohn Baldwin 		break;
7072da066efSJohn Baldwin 	}
7082da066efSJohn Baldwin 	*logp = log;
7092da066efSJohn Baldwin 	return (0);
7102da066efSJohn Baldwin }
7112da066efSJohn Baldwin 
7122da066efSJohn Baldwin int
713*8bba2c0fSJohn Baldwin nvmf_init_dle_from_admin_qp(struct nvmf_qpair *qp,
714*8bba2c0fSJohn Baldwin     const struct nvme_controller_data *cdata,
715*8bba2c0fSJohn Baldwin     struct nvme_discovery_log_entry *dle)
716*8bba2c0fSJohn Baldwin {
717*8bba2c0fSJohn Baldwin 	int error;
718*8bba2c0fSJohn Baldwin 	uint16_t cntlid;
719*8bba2c0fSJohn Baldwin 
720*8bba2c0fSJohn Baldwin 	memset(dle, 0, sizeof(*dle));
721*8bba2c0fSJohn Baldwin 	error = nvmf_populate_dle(qp, dle);
722*8bba2c0fSJohn Baldwin 	if (error != 0)
723*8bba2c0fSJohn Baldwin 		return (error);
724*8bba2c0fSJohn Baldwin 	if ((cdata->fcatt & 1) == 0)
725*8bba2c0fSJohn Baldwin 		cntlid = NVMF_CNTLID_DYNAMIC;
726*8bba2c0fSJohn Baldwin 	else
727*8bba2c0fSJohn Baldwin 		cntlid = cdata->ctrlr_id;
728*8bba2c0fSJohn Baldwin 	dle->cntlid = htole16(cntlid);
729*8bba2c0fSJohn Baldwin 	memcpy(dle->subnqn, cdata->subnqn, sizeof(dle->subnqn));
730*8bba2c0fSJohn Baldwin 	return (0);
731*8bba2c0fSJohn Baldwin }
732*8bba2c0fSJohn Baldwin 
733*8bba2c0fSJohn Baldwin int
7342da066efSJohn Baldwin nvmf_host_request_queues(struct nvmf_qpair *qp, u_int requested, u_int *actual)
7352da066efSJohn Baldwin {
7362da066efSJohn Baldwin 	struct nvme_command cmd;
7372da066efSJohn Baldwin 	struct nvmf_capsule *cc, *rc;
7382da066efSJohn Baldwin 	int error;
7392da066efSJohn Baldwin 	uint16_t status;
7402da066efSJohn Baldwin 
7412da066efSJohn Baldwin 	if (!qp->nq_admin || requested < 1 || requested > 65535)
7422da066efSJohn Baldwin 		return (EINVAL);
7432da066efSJohn Baldwin 
7442da066efSJohn Baldwin 	/* The number of queues is 0's based. */
7452da066efSJohn Baldwin 	requested--;
7462da066efSJohn Baldwin 
7472da066efSJohn Baldwin 	nvmf_init_sqe(&cmd, NVME_OPC_SET_FEATURES);
7482da066efSJohn Baldwin 	cmd.cdw10 = htole32(NVME_FEAT_NUMBER_OF_QUEUES);
7492da066efSJohn Baldwin 
7502da066efSJohn Baldwin 	/* Same number of completion and submission queues. */
7512da066efSJohn Baldwin 	cmd.cdw11 = htole32((requested << 16) | requested);
7522da066efSJohn Baldwin 
7532da066efSJohn Baldwin 	cc = nvmf_allocate_command(qp, &cmd);
7542da066efSJohn Baldwin 	if (cc == NULL)
7552da066efSJohn Baldwin 		return (errno);
7562da066efSJohn Baldwin 
7572da066efSJohn Baldwin 	error = nvmf_host_transmit_command(cc);
7582da066efSJohn Baldwin 	if (error != 0) {
7592da066efSJohn Baldwin 		nvmf_free_capsule(cc);
7602da066efSJohn Baldwin 		return (error);
7612da066efSJohn Baldwin 	}
7622da066efSJohn Baldwin 
7632da066efSJohn Baldwin 	error = nvmf_host_wait_for_response(cc, &rc);
7642da066efSJohn Baldwin 	nvmf_free_capsule(cc);
7652da066efSJohn Baldwin 	if (error != 0)
7662da066efSJohn Baldwin 		return (error);
7672da066efSJohn Baldwin 
7682da066efSJohn Baldwin 	status = le16toh(rc->nc_cqe.status);
7692da066efSJohn Baldwin 	if (status != 0) {
7702da066efSJohn Baldwin 		printf("NVMF: SET_FEATURES failed, status %#x\n", status);
7712da066efSJohn Baldwin 		nvmf_free_capsule(rc);
7722da066efSJohn Baldwin 		return (EIO);
7732da066efSJohn Baldwin 	}
7742da066efSJohn Baldwin 
7752da066efSJohn Baldwin 	*actual = (le32toh(rc->nc_cqe.cdw0) & 0xffff) + 1;
7762da066efSJohn Baldwin 	nvmf_free_capsule(rc);
7772da066efSJohn Baldwin 	return (0);
7782da066efSJohn Baldwin }
7792da066efSJohn Baldwin 
7802da066efSJohn Baldwin static bool
7812da066efSJohn Baldwin is_queue_pair_idle(struct nvmf_qpair *qp)
7822da066efSJohn Baldwin {
7832da066efSJohn Baldwin 	if (qp->nq_sqhd != qp->nq_sqtail)
7842da066efSJohn Baldwin 		return (false);
7852da066efSJohn Baldwin 	if (!TAILQ_EMPTY(&qp->nq_rx_capsules))
7862da066efSJohn Baldwin 		return (false);
7872da066efSJohn Baldwin 	return (true);
7882da066efSJohn Baldwin }
7892da066efSJohn Baldwin 
7902da066efSJohn Baldwin static int
791*8bba2c0fSJohn Baldwin prepare_queues_for_handoff(struct nvmf_ioc_nv *nv,
792*8bba2c0fSJohn Baldwin     const struct nvme_discovery_log_entry *dle, const char *hostnqn,
793*8bba2c0fSJohn Baldwin     struct nvmf_qpair *admin_qp, u_int num_queues,
794*8bba2c0fSJohn Baldwin     struct nvmf_qpair **io_queues, const struct nvme_controller_data *cdata)
7952da066efSJohn Baldwin {
796*8bba2c0fSJohn Baldwin 	const struct nvmf_association *na = admin_qp->nq_association;
797*8bba2c0fSJohn Baldwin 	nvlist_t *nvl, *nvl_qp, *nvl_rparams;
7982da066efSJohn Baldwin 	u_int i;
7992da066efSJohn Baldwin 	int error;
8002da066efSJohn Baldwin 
801365b89e8SJohn Baldwin 	if (num_queues == 0)
802365b89e8SJohn Baldwin 		return (EINVAL);
8032da066efSJohn Baldwin 
804*8bba2c0fSJohn Baldwin 	/* Ensure trtype matches. */
805*8bba2c0fSJohn Baldwin 	if (dle->trtype != na->na_trtype)
806*8bba2c0fSJohn Baldwin 		return (EINVAL);
807*8bba2c0fSJohn Baldwin 
8082da066efSJohn Baldwin 	/* All queue pairs must be idle. */
8092da066efSJohn Baldwin 	if (!is_queue_pair_idle(admin_qp))
8102da066efSJohn Baldwin 		return (EBUSY);
8112da066efSJohn Baldwin 	for (i = 0; i < num_queues; i++) {
8122da066efSJohn Baldwin 		if (!is_queue_pair_idle(io_queues[i]))
8132da066efSJohn Baldwin 			return (EBUSY);
8142da066efSJohn Baldwin 	}
8152da066efSJohn Baldwin 
816*8bba2c0fSJohn Baldwin 	/* Fill out reconnect parameters. */
817*8bba2c0fSJohn Baldwin 	nvl_rparams = nvlist_create(0);
818*8bba2c0fSJohn Baldwin 	nvlist_add_binary(nvl_rparams, "dle", dle, sizeof(*dle));
819*8bba2c0fSJohn Baldwin 	nvlist_add_string(nvl_rparams, "hostnqn", hostnqn);
820*8bba2c0fSJohn Baldwin 	nvlist_add_number(nvl_rparams, "num_io_queues", num_queues);
821*8bba2c0fSJohn Baldwin 	nvlist_add_number(nvl_rparams, "kato", admin_qp->nq_kato);
822*8bba2c0fSJohn Baldwin 	nvlist_add_number(nvl_rparams, "io_qsize", io_queues[0]->nq_qsize);
823*8bba2c0fSJohn Baldwin 	nvlist_add_bool(nvl_rparams, "sq_flow_control",
824*8bba2c0fSJohn Baldwin 	    na->na_params.sq_flow_control);
825*8bba2c0fSJohn Baldwin 	switch (na->na_trtype) {
826*8bba2c0fSJohn Baldwin 	case NVMF_TRTYPE_TCP:
827*8bba2c0fSJohn Baldwin 		nvlist_add_bool(nvl_rparams, "header_digests",
828*8bba2c0fSJohn Baldwin 		    na->na_params.tcp.header_digests);
829*8bba2c0fSJohn Baldwin 		nvlist_add_bool(nvl_rparams, "data_digests",
830*8bba2c0fSJohn Baldwin 		    na->na_params.tcp.data_digests);
831*8bba2c0fSJohn Baldwin 		break;
832*8bba2c0fSJohn Baldwin 	default:
833*8bba2c0fSJohn Baldwin 		__unreachable();
834*8bba2c0fSJohn Baldwin 	}
835*8bba2c0fSJohn Baldwin 	error = nvlist_error(nvl_rparams);
836*8bba2c0fSJohn Baldwin 	if (error != 0) {
837*8bba2c0fSJohn Baldwin 		nvlist_destroy(nvl_rparams);
838*8bba2c0fSJohn Baldwin 		return (error);
839*8bba2c0fSJohn Baldwin 	}
840*8bba2c0fSJohn Baldwin 
841365b89e8SJohn Baldwin 	nvl = nvlist_create(0);
842*8bba2c0fSJohn Baldwin 	nvlist_add_number(nvl, "trtype", na->na_trtype);
843365b89e8SJohn Baldwin 	nvlist_add_number(nvl, "kato", admin_qp->nq_kato);
844*8bba2c0fSJohn Baldwin 	nvlist_move_nvlist(nvl, "rparams", nvl_rparams);
845365b89e8SJohn Baldwin 
8462da066efSJohn Baldwin 	/* First, the admin queue. */
847365b89e8SJohn Baldwin 	error = nvmf_kernel_handoff_params(admin_qp, &nvl_qp);
848365b89e8SJohn Baldwin 	if (error) {
849365b89e8SJohn Baldwin 		nvlist_destroy(nvl);
8502da066efSJohn Baldwin 		return (error);
851365b89e8SJohn Baldwin 	}
852365b89e8SJohn Baldwin 	nvlist_move_nvlist(nvl, "admin", nvl_qp);
8532da066efSJohn Baldwin 
8542da066efSJohn Baldwin 	/* Next, the I/O queues. */
8552da066efSJohn Baldwin 	for (i = 0; i < num_queues; i++) {
856365b89e8SJohn Baldwin 		error = nvmf_kernel_handoff_params(io_queues[i], &nvl_qp);
8572da066efSJohn Baldwin 		if (error) {
858365b89e8SJohn Baldwin 			nvlist_destroy(nvl);
8592da066efSJohn Baldwin 			return (error);
8602da066efSJohn Baldwin 		}
861365b89e8SJohn Baldwin 		nvlist_append_nvlist_array(nvl, "io", nvl_qp);
8622da066efSJohn Baldwin 	}
8632da066efSJohn Baldwin 
864365b89e8SJohn Baldwin 	nvlist_add_binary(nvl, "cdata", cdata, sizeof(*cdata));
865365b89e8SJohn Baldwin 
866365b89e8SJohn Baldwin 	error = nvmf_pack_ioc_nvlist(nv, nvl);
867365b89e8SJohn Baldwin 	nvlist_destroy(nvl);
868365b89e8SJohn Baldwin 	return (error);
8692da066efSJohn Baldwin }
8702da066efSJohn Baldwin 
8712da066efSJohn Baldwin int
872*8bba2c0fSJohn Baldwin nvmf_handoff_host(const struct nvme_discovery_log_entry *dle,
873*8bba2c0fSJohn Baldwin     const char *hostnqn, struct nvmf_qpair *admin_qp, u_int num_queues,
8742da066efSJohn Baldwin     struct nvmf_qpair **io_queues, const struct nvme_controller_data *cdata)
8752da066efSJohn Baldwin {
876365b89e8SJohn Baldwin 	struct nvmf_ioc_nv nv;
8772da066efSJohn Baldwin 	u_int i;
8782da066efSJohn Baldwin 	int error, fd;
8792da066efSJohn Baldwin 
8802da066efSJohn Baldwin 	fd = open("/dev/nvmf", O_RDWR);
8812da066efSJohn Baldwin 	if (fd == -1) {
8822da066efSJohn Baldwin 		error = errno;
8832da066efSJohn Baldwin 		goto out;
8842da066efSJohn Baldwin 	}
8852da066efSJohn Baldwin 
886*8bba2c0fSJohn Baldwin 	error = prepare_queues_for_handoff(&nv, dle, hostnqn, admin_qp,
887*8bba2c0fSJohn Baldwin 	    num_queues, io_queues, cdata);
8882da066efSJohn Baldwin 	if (error != 0)
8892da066efSJohn Baldwin 		goto out;
8902da066efSJohn Baldwin 
891365b89e8SJohn Baldwin 	if (ioctl(fd, NVMF_HANDOFF_HOST, &nv) == -1)
8922da066efSJohn Baldwin 		error = errno;
893365b89e8SJohn Baldwin 	free(nv.data);
8942da066efSJohn Baldwin 
8952da066efSJohn Baldwin out:
8962da066efSJohn Baldwin 	if (fd >= 0)
8972da066efSJohn Baldwin 		close(fd);
8982da066efSJohn Baldwin 	for (i = 0; i < num_queues; i++)
8992da066efSJohn Baldwin 		(void)nvmf_free_qpair(io_queues[i]);
9002da066efSJohn Baldwin 	(void)nvmf_free_qpair(admin_qp);
9012da066efSJohn Baldwin 	return (error);
9022da066efSJohn Baldwin }
9032da066efSJohn Baldwin 
9042da066efSJohn Baldwin int
9052da066efSJohn Baldwin nvmf_disconnect_host(const char *host)
9062da066efSJohn Baldwin {
9072da066efSJohn Baldwin 	int error, fd;
9082da066efSJohn Baldwin 
9092da066efSJohn Baldwin 	error = 0;
9102da066efSJohn Baldwin 	fd = open("/dev/nvmf", O_RDWR);
9112da066efSJohn Baldwin 	if (fd == -1) {
9122da066efSJohn Baldwin 		error = errno;
9132da066efSJohn Baldwin 		goto out;
9142da066efSJohn Baldwin 	}
9152da066efSJohn Baldwin 
9162da066efSJohn Baldwin 	if (ioctl(fd, NVMF_DISCONNECT_HOST, &host) == -1)
9172da066efSJohn Baldwin 		error = errno;
9182da066efSJohn Baldwin 
9192da066efSJohn Baldwin out:
9202da066efSJohn Baldwin 	if (fd >= 0)
9212da066efSJohn Baldwin 		close(fd);
9222da066efSJohn Baldwin 	return (error);
9232da066efSJohn Baldwin }
9242da066efSJohn Baldwin 
9252da066efSJohn Baldwin int
9262da066efSJohn Baldwin nvmf_disconnect_all(void)
9272da066efSJohn Baldwin {
9282da066efSJohn Baldwin 	int error, fd;
9292da066efSJohn Baldwin 
9302da066efSJohn Baldwin 	error = 0;
9312da066efSJohn Baldwin 	fd = open("/dev/nvmf", O_RDWR);
9322da066efSJohn Baldwin 	if (fd == -1) {
9332da066efSJohn Baldwin 		error = errno;
9342da066efSJohn Baldwin 		goto out;
9352da066efSJohn Baldwin 	}
9362da066efSJohn Baldwin 
9372da066efSJohn Baldwin 	if (ioctl(fd, NVMF_DISCONNECT_ALL) == -1)
9382da066efSJohn Baldwin 		error = errno;
9392da066efSJohn Baldwin 
9402da066efSJohn Baldwin out:
9412da066efSJohn Baldwin 	if (fd >= 0)
9422da066efSJohn Baldwin 		close(fd);
9432da066efSJohn Baldwin 	return (error);
9442da066efSJohn Baldwin }
9452da066efSJohn Baldwin 
946365b89e8SJohn Baldwin static int
947365b89e8SJohn Baldwin nvmf_read_ioc_nv(int fd, u_long com, nvlist_t **nvlp)
9482da066efSJohn Baldwin {
949365b89e8SJohn Baldwin 	struct nvmf_ioc_nv nv;
950365b89e8SJohn Baldwin 	nvlist_t *nvl;
951365b89e8SJohn Baldwin 	int error;
952365b89e8SJohn Baldwin 
953365b89e8SJohn Baldwin 	memset(&nv, 0, sizeof(nv));
954365b89e8SJohn Baldwin 	if (ioctl(fd, com, &nv) == -1)
9552da066efSJohn Baldwin 		return (errno);
956365b89e8SJohn Baldwin 
957365b89e8SJohn Baldwin 	nv.data = malloc(nv.len);
958365b89e8SJohn Baldwin 	nv.size = nv.len;
959365b89e8SJohn Baldwin 	if (ioctl(fd, com, &nv) == -1) {
960365b89e8SJohn Baldwin 		error = errno;
961365b89e8SJohn Baldwin 		free(nv.data);
962365b89e8SJohn Baldwin 		return (error);
963365b89e8SJohn Baldwin 	}
964365b89e8SJohn Baldwin 
965365b89e8SJohn Baldwin 	nvl = nvlist_unpack(nv.data, nv.len, 0);
966365b89e8SJohn Baldwin 	free(nv.data);
967365b89e8SJohn Baldwin 	if (nvl == NULL)
968365b89e8SJohn Baldwin 		return (EINVAL);
969365b89e8SJohn Baldwin 
970365b89e8SJohn Baldwin 	*nvlp = nvl;
9712da066efSJohn Baldwin 	return (0);
9722da066efSJohn Baldwin }
9732da066efSJohn Baldwin 
9742da066efSJohn Baldwin int
975365b89e8SJohn Baldwin nvmf_reconnect_params(int fd, nvlist_t **nvlp)
976365b89e8SJohn Baldwin {
977365b89e8SJohn Baldwin 	return (nvmf_read_ioc_nv(fd, NVMF_RECONNECT_PARAMS, nvlp));
978365b89e8SJohn Baldwin }
979365b89e8SJohn Baldwin 
980365b89e8SJohn Baldwin int
981*8bba2c0fSJohn Baldwin nvmf_reconnect_host(int fd, const struct nvme_discovery_log_entry *dle,
982*8bba2c0fSJohn Baldwin     const char *hostnqn, struct nvmf_qpair *admin_qp, u_int num_queues,
9832da066efSJohn Baldwin     struct nvmf_qpair **io_queues, const struct nvme_controller_data *cdata)
9842da066efSJohn Baldwin {
985365b89e8SJohn Baldwin 	struct nvmf_ioc_nv nv;
9862da066efSJohn Baldwin 	u_int i;
9872da066efSJohn Baldwin 	int error;
9882da066efSJohn Baldwin 
989*8bba2c0fSJohn Baldwin 	error = prepare_queues_for_handoff(&nv, dle, hostnqn, admin_qp,
990*8bba2c0fSJohn Baldwin 	    num_queues, io_queues, cdata);
9912da066efSJohn Baldwin 	if (error != 0)
9922da066efSJohn Baldwin 		goto out;
9932da066efSJohn Baldwin 
994365b89e8SJohn Baldwin 	if (ioctl(fd, NVMF_RECONNECT_HOST, &nv) == -1)
9952da066efSJohn Baldwin 		error = errno;
996365b89e8SJohn Baldwin 	free(nv.data);
9972da066efSJohn Baldwin 
9982da066efSJohn Baldwin out:
9992da066efSJohn Baldwin 	for (i = 0; i < num_queues; i++)
10002da066efSJohn Baldwin 		(void)nvmf_free_qpair(io_queues[i]);
10012da066efSJohn Baldwin 	(void)nvmf_free_qpair(admin_qp);
10022da066efSJohn Baldwin 	return (error);
10032da066efSJohn Baldwin }
1004