aboutsummaryrefslogtreecommitdiff
path: root/usr.sbin/nvmfd/io.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin/nvmfd/io.c')
-rw-r--r--usr.sbin/nvmfd/io.c676
1 files changed, 0 insertions, 676 deletions
diff --git a/usr.sbin/nvmfd/io.c b/usr.sbin/nvmfd/io.c
deleted file mode 100644
index 4407360257a2..000000000000
--- a/usr.sbin/nvmfd/io.c
+++ /dev/null
@@ -1,676 +0,0 @@
-/*-
- * SPDX-License-Identifier: BSD-2-Clause
- *
- * Copyright (c) 2023-2024 Chelsio Communications, Inc.
- * Written by: John Baldwin <jhb@FreeBSD.org>
- */
-
-#include <sys/sysctl.h>
-#include <err.h>
-#include <errno.h>
-#include <libnvmf.h>
-#include <pthread.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <unistd.h>
-
-#include "internal.h"
-
-struct io_controller {
- struct controller *c;
-
- u_int num_io_queues;
- u_int active_io_queues;
- struct nvmf_qpair **io_qpairs;
- int *io_sockets;
-
- struct nvme_firmware_page fp;
- struct nvme_health_information_page hip;
- uint16_t partial_dur;
- uint16_t partial_duw;
-
- uint16_t cntlid;
- char hostid[16];
- char hostnqn[NVME_NQN_FIELD_SIZE];
-};
-
-static struct nvmf_association *io_na;
-static pthread_cond_t io_cond;
-static pthread_mutex_t io_na_mutex;
-static struct io_controller *io_controller;
-static const char *nqn;
-static char serial[NVME_SERIAL_NUMBER_LENGTH];
-
-void
-init_io(const char *subnqn)
-{
- struct nvmf_association_params aparams;
- u_long hostid;
- size_t len;
-
- memset(&aparams, 0, sizeof(aparams));
- aparams.sq_flow_control = !flow_control_disable;
- aparams.dynamic_controller_model = true;
- aparams.max_admin_qsize = NVME_MAX_ADMIN_ENTRIES;
- aparams.max_io_qsize = NVMF_MAX_IO_ENTRIES;
- aparams.tcp.pda = 0;
- aparams.tcp.header_digests = header_digests;
- aparams.tcp.data_digests = data_digests;
- aparams.tcp.maxh2cdata = maxh2cdata;
- io_na = nvmf_allocate_association(NVMF_TRTYPE_TCP, true,
- &aparams);
- if (io_na == NULL)
- err(1, "Failed to create I/O controller association");
-
- nqn = subnqn;
-
- /* Generate a serial number from the kern.hostid node. */
- len = sizeof(hostid);
- if (sysctlbyname("kern.hostid", &hostid, &len, NULL, 0) == -1)
- err(1, "sysctl: kern.hostid");
-
- nvmf_controller_serial(serial, sizeof(serial), hostid);
-
- pthread_cond_init(&io_cond, NULL);
- pthread_mutex_init(&io_na_mutex, NULL);
-
- if (kernel_io)
- init_ctl_port(subnqn, &aparams);
-}
-
-void
-shutdown_io(void)
-{
- if (kernel_io)
- shutdown_ctl_port(nqn);
-}
-
-static void
-handle_get_log_page(struct io_controller *ioc, const struct nvmf_capsule *nc,
- const struct nvme_command *cmd)
-{
- uint64_t offset;
- uint32_t numd;
- size_t len;
- uint8_t lid;
-
- lid = le32toh(cmd->cdw10) & 0xff;
- numd = le32toh(cmd->cdw10) >> 16 | le32toh(cmd->cdw11) << 16;
- offset = le32toh(cmd->cdw12) | (uint64_t)le32toh(cmd->cdw13) << 32;
-
- if (offset % 3 != 0)
- goto error;
-
- len = (numd + 1) * 4;
-
- switch (lid) {
- case NVME_LOG_ERROR:
- {
- void *buf;
-
- if (len % sizeof(struct nvme_error_information_entry) != 0)
- goto error;
-
- buf = calloc(1, len);
- nvmf_send_controller_data(nc, buf, len);
- free(buf);
- return;
- }
- case NVME_LOG_HEALTH_INFORMATION:
- if (len != sizeof(ioc->hip))
- goto error;
-
- nvmf_send_controller_data(nc, &ioc->hip, sizeof(ioc->hip));
- return;
- case NVME_LOG_FIRMWARE_SLOT:
- if (len != sizeof(ioc->fp))
- goto error;
-
- nvmf_send_controller_data(nc, &ioc->fp, sizeof(ioc->fp));
- return;
- default:
- warnx("Unsupported page %#x for GET_LOG_PAGE\n", lid);
- goto error;
- }
-
-error:
- nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD);
-}
-
-static bool
-handle_io_identify_command(const struct nvmf_capsule *nc,
- const struct nvme_command *cmd)
-{
- struct nvme_namespace_data nsdata;
- struct nvme_ns_list nslist;
- uint32_t nsid;
- uint8_t cns;
-
- cns = le32toh(cmd->cdw10) & 0xFF;
- switch (cns) {
- case 0: /* Namespace data. */
- if (!device_namespace_data(le32toh(cmd->nsid), &nsdata)) {
- nvmf_send_generic_error(nc,
- NVME_SC_INVALID_NAMESPACE_OR_FORMAT);
- return (true);
- }
-
- nvmf_send_controller_data(nc, &nsdata, sizeof(nsdata));
- return (true);
- case 2: /* Active namespace list. */
- nsid = le32toh(cmd->nsid);
- if (nsid >= 0xfffffffe) {
- nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD);
- return (true);
- }
-
- device_active_nslist(nsid, &nslist);
- nvmf_send_controller_data(nc, &nslist, sizeof(nslist));
- return (true);
- case 3: /* Namespace Identification Descriptor list. */
- if (!device_identification_descriptor(le32toh(cmd->nsid),
- &nsdata)) {
- nvmf_send_generic_error(nc,
- NVME_SC_INVALID_NAMESPACE_OR_FORMAT);
- return (true);
- }
-
- nvmf_send_controller_data(nc, &nsdata, sizeof(nsdata));
- return (true);
- default:
- return (false);
- }
-}
-
-static void
-handle_set_features(struct io_controller *ioc, const struct nvmf_capsule *nc,
- const struct nvme_command *cmd)
-{
- struct nvme_completion cqe;
- uint8_t fid;
-
- fid = NVMEV(NVME_FEAT_SET_FID, le32toh(cmd->cdw10));
- switch (fid) {
- case NVME_FEAT_NUMBER_OF_QUEUES:
- {
- uint32_t num_queues;
-
- if (ioc->num_io_queues != 0) {
- nvmf_send_generic_error(nc,
- NVME_SC_COMMAND_SEQUENCE_ERROR);
- return;
- }
-
- num_queues = le32toh(cmd->cdw11) & 0xffff;
-
- /* 5.12.1.7: 65535 is invalid. */
- if (num_queues == 65535)
- goto error;
-
- /* Fabrics requires the same number of SQs and CQs. */
- if (le32toh(cmd->cdw11) >> 16 != num_queues)
- goto error;
-
- /* Convert to 1's based */
- num_queues++;
-
- /* Lock to synchronize with handle_io_qpair. */
- pthread_mutex_lock(&io_na_mutex);
- ioc->num_io_queues = num_queues;
- ioc->io_qpairs = calloc(num_queues, sizeof(*ioc->io_qpairs));
- ioc->io_sockets = calloc(num_queues, sizeof(*ioc->io_sockets));
- pthread_mutex_unlock(&io_na_mutex);
-
- nvmf_init_cqe(&cqe, nc, 0);
- cqe.cdw0 = cmd->cdw11;
- nvmf_send_response(nc, &cqe);
- return;
- }
- case NVME_FEAT_ASYNC_EVENT_CONFIGURATION:
- {
- uint32_t aer_mask;
-
- aer_mask = le32toh(cmd->cdw11);
-
- /* Check for any reserved or unimplemented feature bits. */
- if ((aer_mask & 0xffffc000) != 0)
- goto error;
-
- /* No AERs are generated by this daemon. */
- nvmf_send_success(nc);
- return;
- }
- default:
- warnx("Unsupported feature ID %u for SET_FEATURES", fid);
- goto error;
- }
-
-error:
- nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD);
-}
-
-static bool
-admin_command(const struct nvmf_capsule *nc, const struct nvme_command *cmd,
- void *arg)
-{
- struct io_controller *ioc = arg;
-
- switch (cmd->opc) {
- case NVME_OPC_GET_LOG_PAGE:
- handle_get_log_page(ioc, nc, cmd);
- return (true);
- case NVME_OPC_IDENTIFY:
- return (handle_io_identify_command(nc, cmd));
- case NVME_OPC_SET_FEATURES:
- handle_set_features(ioc, nc, cmd);
- return (true);
- case NVME_OPC_ASYNC_EVENT_REQUEST:
- /* Ignore and never complete. */
- return (true);
- case NVME_OPC_KEEP_ALIVE:
- nvmf_send_success(nc);
- return (true);
- default:
- return (false);
- }
-}
-
-static void
-handle_admin_qpair(struct io_controller *ioc)
-{
- pthread_setname_np(pthread_self(), "admin queue");
-
- controller_handle_admin_commands(ioc->c, admin_command, ioc);
-
- pthread_mutex_lock(&io_na_mutex);
- for (u_int i = 0; i < ioc->num_io_queues; i++) {
- if (ioc->io_qpairs[i] == NULL || ioc->io_sockets[i] == -1)
- continue;
- close(ioc->io_sockets[i]);
- ioc->io_sockets[i] = -1;
- }
-
- /* Wait for I/O threads to notice. */
- while (ioc->active_io_queues > 0)
- pthread_cond_wait(&io_cond, &io_na_mutex);
-
- io_controller = NULL;
- pthread_mutex_unlock(&io_na_mutex);
-
- free_controller(ioc->c);
-
- free(ioc);
-}
-
-static bool
-handle_io_fabrics_command(const struct nvmf_capsule *nc,
- const struct nvmf_fabric_cmd *fc)
-{
- switch (fc->fctype) {
- case NVMF_FABRIC_COMMAND_CONNECT:
- warnx("CONNECT command on connected queue");
- nvmf_send_generic_error(nc, NVME_SC_COMMAND_SEQUENCE_ERROR);
- break;
- case NVMF_FABRIC_COMMAND_DISCONNECT:
- {
- const struct nvmf_fabric_disconnect_cmd *dis =
- (const struct nvmf_fabric_disconnect_cmd *)fc;
- if (dis->recfmt != htole16(0)) {
- nvmf_send_error(nc, NVME_SCT_COMMAND_SPECIFIC,
- NVMF_FABRIC_SC_INCOMPATIBLE_FORMAT);
- break;
- }
- nvmf_send_success(nc);
- return (true);
- }
- default:
- warnx("Unsupported fabrics command %#x", fc->fctype);
- nvmf_send_generic_error(nc, NVME_SC_INVALID_OPCODE);
- break;
- }
-
- return (false);
-}
-
-static void
-hip_add(uint64_t pair[2], uint64_t addend)
-{
- uint64_t old, new;
-
- old = le64toh(pair[0]);
- new = old + addend;
- pair[0] = htole64(new);
- if (new < old)
- pair[1] += htole64(1);
-}
-
-static uint64_t
-cmd_lba(const struct nvme_command *cmd)
-{
- return ((uint64_t)le32toh(cmd->cdw11) << 32 | le32toh(cmd->cdw10));
-}
-
-static u_int
-cmd_nlb(const struct nvme_command *cmd)
-{
- return ((le32toh(cmd->cdw12) & 0xffff) + 1);
-}
-
-static void
-handle_read(struct io_controller *ioc, const struct nvmf_capsule *nc,
- const struct nvme_command *cmd)
-{
- size_t len;
-
- len = nvmf_capsule_data_len(nc);
- device_read(le32toh(cmd->nsid), cmd_lba(cmd), cmd_nlb(cmd), nc);
- hip_add(ioc->hip.host_read_commands, 1);
-
- len /= 512;
- len += ioc->partial_dur;
- if (len > 1000)
- hip_add(ioc->hip.data_units_read, len / 1000);
- ioc->partial_dur = len % 1000;
-}
-
-static void
-handle_write(struct io_controller *ioc, const struct nvmf_capsule *nc,
- const struct nvme_command *cmd)
-{
- size_t len;
-
- len = nvmf_capsule_data_len(nc);
- device_write(le32toh(cmd->nsid), cmd_lba(cmd), cmd_nlb(cmd), nc);
- hip_add(ioc->hip.host_write_commands, 1);
-
- len /= 512;
- len += ioc->partial_duw;
- if (len > 1000)
- hip_add(ioc->hip.data_units_written, len / 1000);
- ioc->partial_duw = len % 1000;
-}
-
-static void
-handle_flush(const struct nvmf_capsule *nc, const struct nvme_command *cmd)
-{
- device_flush(le32toh(cmd->nsid), nc);
-}
-
-static bool
-handle_io_commands(struct io_controller *ioc, struct nvmf_qpair *qp)
-{
- const struct nvme_command *cmd;
- struct nvmf_capsule *nc;
- int error;
- bool disconnect;
-
- disconnect = false;
-
- while (!disconnect) {
- error = nvmf_controller_receive_capsule(qp, &nc);
- if (error != 0) {
- if (error != ECONNRESET)
- warnc(error, "Failed to read command capsule");
- break;
- }
-
- cmd = nvmf_capsule_sqe(nc);
-
- switch (cmd->opc) {
- case NVME_OPC_FLUSH:
- if (cmd->nsid == htole32(0xffffffff)) {
- nvmf_send_generic_error(nc,
- NVME_SC_INVALID_NAMESPACE_OR_FORMAT);
- break;
- }
- handle_flush(nc, cmd);
- break;
- case NVME_OPC_WRITE:
- handle_write(ioc, nc, cmd);
- break;
- case NVME_OPC_READ:
- handle_read(ioc, nc, cmd);
- break;
- case NVME_OPC_FABRICS_COMMANDS:
- disconnect = handle_io_fabrics_command(nc,
- (const struct nvmf_fabric_cmd *)cmd);
- break;
- default:
- warnx("Unsupported NVM opcode %#x", cmd->opc);
- nvmf_send_generic_error(nc, NVME_SC_INVALID_OPCODE);
- break;
- }
- nvmf_free_capsule(nc);
- }
-
- return (disconnect);
-}
-
-static void
-handle_io_qpair(struct io_controller *ioc, struct nvmf_qpair *qp, int qid)
-{
- char name[64];
- bool disconnect;
-
- snprintf(name, sizeof(name), "I/O queue %d", qid);
- pthread_setname_np(pthread_self(), name);
-
- disconnect = handle_io_commands(ioc, qp);
-
- pthread_mutex_lock(&io_na_mutex);
- if (disconnect)
- ioc->io_qpairs[qid - 1] = NULL;
- if (ioc->io_sockets[qid - 1] != -1) {
- close(ioc->io_sockets[qid - 1]);
- ioc->io_sockets[qid - 1] = -1;
- }
- ioc->active_io_queues--;
- if (ioc->active_io_queues == 0)
- pthread_cond_broadcast(&io_cond);
- pthread_mutex_unlock(&io_na_mutex);
-}
-
-static void
-connect_admin_qpair(int s, struct nvmf_qpair *qp, struct nvmf_capsule *nc,
- const struct nvmf_fabric_connect_data *data)
-{
- struct nvme_controller_data cdata;
- struct io_controller *ioc;
- int error;
-
- /* Can only have one active I/O controller at a time. */
- pthread_mutex_lock(&io_na_mutex);
- if (io_controller != NULL) {
- pthread_mutex_unlock(&io_na_mutex);
- nvmf_send_error(nc, NVME_SCT_COMMAND_SPECIFIC,
- NVMF_FABRIC_SC_CONTROLLER_BUSY);
- goto error;
- }
-
- error = nvmf_finish_accept(nc, 2);
- if (error != 0) {
- pthread_mutex_unlock(&io_na_mutex);
- warnc(error, "Failed to send CONNECT response");
- goto error;
- }
-
- ioc = calloc(1, sizeof(*ioc));
- ioc->cntlid = 2;
- memcpy(ioc->hostid, data->hostid, sizeof(ioc->hostid));
- memcpy(ioc->hostnqn, data->hostnqn, sizeof(ioc->hostnqn));
-
- nvmf_init_io_controller_data(qp, serial, nqn, device_count(),
- NVMF_IOCCSZ, &cdata);
-
- ioc->fp.afi = NVMEF(NVME_FIRMWARE_PAGE_AFI_SLOT, 1);
- memcpy(ioc->fp.revision[0], cdata.fr, sizeof(cdata.fr));
-
- ioc->hip.power_cycles[0] = 1;
-
- ioc->c = init_controller(qp, &cdata);
-
- io_controller = ioc;
- pthread_mutex_unlock(&io_na_mutex);
-
- nvmf_free_capsule(nc);
-
- handle_admin_qpair(ioc);
- close(s);
- return;
-
-error:
- nvmf_free_capsule(nc);
- close(s);
-}
-
-static void
-connect_io_qpair(int s, struct nvmf_qpair *qp, struct nvmf_capsule *nc,
- const struct nvmf_fabric_connect_data *data, uint16_t qid)
-{
- struct io_controller *ioc;
- int error;
-
- pthread_mutex_lock(&io_na_mutex);
- if (io_controller == NULL) {
- pthread_mutex_unlock(&io_na_mutex);
- warnx("Attempt to create I/O qpair without admin qpair");
- nvmf_send_generic_error(nc, NVME_SC_COMMAND_SEQUENCE_ERROR);
- goto error;
- }
-
- if (memcmp(io_controller->hostid, data->hostid,
- sizeof(data->hostid)) != 0) {
- pthread_mutex_unlock(&io_na_mutex);
- warnx("hostid mismatch for I/O qpair CONNECT");
- nvmf_connect_invalid_parameters(nc, true,
- offsetof(struct nvmf_fabric_connect_data, hostid));
- goto error;
- }
- if (le16toh(data->cntlid) != io_controller->cntlid) {
- pthread_mutex_unlock(&io_na_mutex);
- warnx("cntlid mismatch for I/O qpair CONNECT");
- nvmf_connect_invalid_parameters(nc, true,
- offsetof(struct nvmf_fabric_connect_data, cntlid));
- goto error;
- }
- if (memcmp(io_controller->hostnqn, data->hostnqn,
- sizeof(data->hostid)) != 0) {
- pthread_mutex_unlock(&io_na_mutex);
- warnx("host NQN mismatch for I/O qpair CONNECT");
- nvmf_connect_invalid_parameters(nc, true,
- offsetof(struct nvmf_fabric_connect_data, hostnqn));
- goto error;
- }
-
- if (io_controller->num_io_queues == 0) {
- pthread_mutex_unlock(&io_na_mutex);
- warnx("Attempt to create I/O qpair without enabled queues");
- nvmf_send_generic_error(nc, NVME_SC_COMMAND_SEQUENCE_ERROR);
- goto error;
- }
- if (qid > io_controller->num_io_queues) {
- pthread_mutex_unlock(&io_na_mutex);
- warnx("Attempt to create invalid I/O qpair %u", qid);
- nvmf_connect_invalid_parameters(nc, false,
- offsetof(struct nvmf_fabric_connect_cmd, qid));
- goto error;
- }
- if (io_controller->io_qpairs[qid - 1] != NULL) {
- pthread_mutex_unlock(&io_na_mutex);
- warnx("Attempt to re-create I/O qpair %u", qid);
- nvmf_send_generic_error(nc, NVME_SC_COMMAND_SEQUENCE_ERROR);
- goto error;
- }
-
- error = nvmf_finish_accept(nc, io_controller->cntlid);
- if (error != 0) {
- pthread_mutex_unlock(&io_na_mutex);
- warnc(error, "Failed to send CONNECT response");
- goto error;
- }
-
- ioc = io_controller;
- ioc->active_io_queues++;
- ioc->io_qpairs[qid - 1] = qp;
- ioc->io_sockets[qid - 1] = s;
- pthread_mutex_unlock(&io_na_mutex);
-
- nvmf_free_capsule(nc);
-
- handle_io_qpair(ioc, qp, qid);
- return;
-
-error:
- nvmf_free_capsule(nc);
- close(s);
-}
-
-static void *
-io_socket_thread(void *arg)
-{
- struct nvmf_fabric_connect_data data;
- struct nvmf_qpair_params qparams;
- const struct nvmf_fabric_connect_cmd *cmd;
- struct nvmf_capsule *nc;
- struct nvmf_qpair *qp;
- int s;
-
- pthread_detach(pthread_self());
-
- s = (intptr_t)arg;
- memset(&qparams, 0, sizeof(qparams));
- qparams.tcp.fd = s;
-
- nc = NULL;
- qp = nvmf_accept(io_na, &qparams, &nc, &data);
- if (qp == NULL) {
- warnx("Failed to create I/O qpair: %s",
- nvmf_association_error(io_na));
- goto error;
- }
-
- if (kernel_io) {
- ctl_handoff_qpair(qp, nvmf_capsule_sqe(nc), &data);
- goto error;
- }
-
- if (strcmp(data.subnqn, nqn) != 0) {
- warn("I/O qpair with invalid SubNQN: %.*s",
- (int)sizeof(data.subnqn), data.subnqn);
- nvmf_connect_invalid_parameters(nc, true,
- offsetof(struct nvmf_fabric_connect_data, subnqn));
- goto error;
- }
-
- /* Is this an admin or I/O queue pair? */
- cmd = nvmf_capsule_sqe(nc);
- if (cmd->qid == 0)
- connect_admin_qpair(s, qp, nc, &data);
- else
- connect_io_qpair(s, qp, nc, &data, le16toh(cmd->qid));
- nvmf_free_qpair(qp);
- return (NULL);
-
-error:
- if (nc != NULL)
- nvmf_free_capsule(nc);
- if (qp != NULL)
- nvmf_free_qpair(qp);
- close(s);
- return (NULL);
-}
-
-void
-handle_io_socket(int s)
-{
- pthread_t thr;
- int error;
-
- error = pthread_create(&thr, NULL, io_socket_thread,
- (void *)(uintptr_t)s);
- if (error != 0) {
- warnc(error, "Failed to create I/O qpair thread");
- close(s);
- }
-}