diff options
Diffstat (limited to 'usr.sbin/nvmfd')
-rw-r--r-- | usr.sbin/nvmfd/Makefile | 14 | ||||
-rw-r--r-- | usr.sbin/nvmfd/Makefile.depend | 20 | ||||
-rw-r--r-- | usr.sbin/nvmfd/controller.c | 244 | ||||
-rw-r--r-- | usr.sbin/nvmfd/ctl.c | 137 | ||||
-rw-r--r-- | usr.sbin/nvmfd/devices.c | 386 | ||||
-rw-r--r-- | usr.sbin/nvmfd/discovery.c | 342 | ||||
-rw-r--r-- | usr.sbin/nvmfd/internal.h | 66 | ||||
-rw-r--r-- | usr.sbin/nvmfd/io.c | 676 | ||||
-rw-r--r-- | usr.sbin/nvmfd/nvmfd.8 | 131 | ||||
-rw-r--r-- | usr.sbin/nvmfd/nvmfd.c | 271 |
10 files changed, 0 insertions, 2287 deletions
diff --git a/usr.sbin/nvmfd/Makefile b/usr.sbin/nvmfd/Makefile deleted file mode 100644 index dc3dcc5e3a5c..000000000000 --- a/usr.sbin/nvmfd/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -.include <src.opts.mk> -.PATH: ${SRCTOP}/sys/libkern - -PACKAGE=nvme-tools -PROG= nvmfd -SRCS= nvmfd.c controller.c ctl.c devices.c discovery.c gsb_crc32.c io.c -CFLAGS+= -I${SRCTOP}/lib/libnvmf -MAN= nvmfd.8 -LIBADD+= nvmf pthread util nv - -.include <bsd.prog.mk> - -CFLAGS.ctl.c= -I${SRCTOP}/sys -CWARNFLAGS.gsb_crc32.c= -Wno-cast-align diff --git a/usr.sbin/nvmfd/Makefile.depend b/usr.sbin/nvmfd/Makefile.depend deleted file mode 100644 index c4c6125c7a7c..000000000000 --- a/usr.sbin/nvmfd/Makefile.depend +++ /dev/null @@ -1,20 +0,0 @@ -# Autogenerated - do NOT edit! - -DIRDEPS = \ - include \ - include/arpa \ - include/xlocale \ - lib/${CSU_DIR} \ - lib/libc \ - lib/libcompiler_rt \ - lib/libnv \ - lib/libnvmf \ - lib/libthr \ - lib/libutil \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.sbin/nvmfd/controller.c b/usr.sbin/nvmfd/controller.c deleted file mode 100644 index e9435bce69da..000000000000 --- a/usr.sbin/nvmfd/controller.c +++ /dev/null @@ -1,244 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2023-2024 Chelsio Communications, Inc. - * Written by: John Baldwin <jhb@FreeBSD.org> - */ - -#include <err.h> -#include <errno.h> -#include <libnvmf.h> -#include <stdlib.h> - -#include "internal.h" - -struct controller { - struct nvmf_qpair *qp; - - uint64_t cap; - uint32_t vs; - uint32_t cc; - uint32_t csts; - - bool shutdown; - - struct nvme_controller_data cdata; -}; - -static bool -update_cc(struct controller *c, uint32_t new_cc) -{ - uint32_t changes; - - if (c->shutdown) - return (false); - if (!nvmf_validate_cc(c->qp, c->cap, c->cc, new_cc)) - return (false); - - changes = c->cc ^ new_cc; - c->cc = new_cc; - - /* Handle shutdown requests. */ - if (NVMEV(NVME_CC_REG_SHN, changes) != 0 && - NVMEV(NVME_CC_REG_SHN, new_cc) != 0) { - c->csts &= ~NVMEM(NVME_CSTS_REG_SHST); - c->csts |= NVMEF(NVME_CSTS_REG_SHST, NVME_SHST_COMPLETE); - c->shutdown = true; - } - - if (NVMEV(NVME_CC_REG_EN, changes) != 0) { - if (NVMEV(NVME_CC_REG_EN, new_cc) == 0) { - /* Controller reset. */ - c->csts = 0; - c->shutdown = true; - } else - c->csts |= NVMEF(NVME_CSTS_REG_RDY, 1); - } - return (true); -} - -static void -handle_property_get(const struct controller *c, const struct nvmf_capsule *nc, - const struct nvmf_fabric_prop_get_cmd *pget) -{ - struct nvmf_fabric_prop_get_rsp rsp; - - nvmf_init_cqe(&rsp, nc, 0); - - switch (le32toh(pget->ofst)) { - case NVMF_PROP_CAP: - if (pget->attrib.size != NVMF_PROP_SIZE_8) - goto error; - rsp.value.u64 = htole64(c->cap); - break; - case NVMF_PROP_VS: - if (pget->attrib.size != NVMF_PROP_SIZE_4) - goto error; - rsp.value.u32.low = htole32(c->vs); - break; - case NVMF_PROP_CC: - if (pget->attrib.size != NVMF_PROP_SIZE_4) - goto error; - rsp.value.u32.low = htole32(c->cc); - break; - case NVMF_PROP_CSTS: - if (pget->attrib.size != NVMF_PROP_SIZE_4) - goto error; - rsp.value.u32.low = htole32(c->csts); - break; - default: - goto error; - } - - nvmf_send_response(nc, &rsp); - return; -error: - nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); -} - -static void -handle_property_set(struct controller *c, const struct nvmf_capsule *nc, - const struct nvmf_fabric_prop_set_cmd *pset) -{ - switch (le32toh(pset->ofst)) { - case NVMF_PROP_CC: - if (pset->attrib.size != NVMF_PROP_SIZE_4) - goto error; - if (!update_cc(c, le32toh(pset->value.u32.low))) - goto error; - break; - default: - goto error; - } - - nvmf_send_success(nc); - return; -error: - nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); -} - -static void -handle_fabrics_command(struct controller *c, - const struct nvmf_capsule *nc, const struct nvmf_fabric_cmd *fc) -{ - switch (fc->fctype) { - case NVMF_FABRIC_COMMAND_PROPERTY_GET: - handle_property_get(c, nc, - (const struct nvmf_fabric_prop_get_cmd *)fc); - break; - case NVMF_FABRIC_COMMAND_PROPERTY_SET: - handle_property_set(c, nc, - (const struct nvmf_fabric_prop_set_cmd *)fc); - break; - 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: - warnx("DISCONNECT command on admin queue"); - nvmf_send_error(nc, NVME_SCT_COMMAND_SPECIFIC, - NVMF_FABRIC_SC_INVALID_QUEUE_TYPE); - break; - default: - warnx("Unsupported fabrics command %#x", fc->fctype); - nvmf_send_generic_error(nc, NVME_SC_INVALID_OPCODE); - break; - } -} - -static void -handle_identify_command(const struct controller *c, - const struct nvmf_capsule *nc, const struct nvme_command *cmd) -{ - uint8_t cns; - - cns = le32toh(cmd->cdw10) & 0xFF; - switch (cns) { - case 1: - break; - default: - warnx("Unsupported CNS %#x for IDENTIFY", cns); - goto error; - } - - nvmf_send_controller_data(nc, &c->cdata, sizeof(c->cdata)); - return; -error: - nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); -} - -void -controller_handle_admin_commands(struct controller *c, handle_command *cb, - void *cb_arg) -{ - struct nvmf_qpair *qp = c->qp; - const struct nvme_command *cmd; - struct nvmf_capsule *nc; - int error; - - for (;;) { - 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); - - /* - * Only permit Fabrics commands while a controller is - * disabled. - */ - if (NVMEV(NVME_CC_REG_EN, c->cc) == 0 && - cmd->opc != NVME_OPC_FABRICS_COMMANDS) { - warnx("Unsupported admin opcode %#x while disabled\n", - cmd->opc); - nvmf_send_generic_error(nc, - NVME_SC_COMMAND_SEQUENCE_ERROR); - nvmf_free_capsule(nc); - continue; - } - - if (cb(nc, cmd, cb_arg)) { - nvmf_free_capsule(nc); - continue; - } - - switch (cmd->opc) { - case NVME_OPC_FABRICS_COMMANDS: - handle_fabrics_command(c, nc, - (const struct nvmf_fabric_cmd *)cmd); - break; - case NVME_OPC_IDENTIFY: - handle_identify_command(c, nc, cmd); - break; - default: - warnx("Unsupported admin opcode %#x", cmd->opc); - nvmf_send_generic_error(nc, NVME_SC_INVALID_OPCODE); - break; - } - nvmf_free_capsule(nc); - } -} - -struct controller * -init_controller(struct nvmf_qpair *qp, - const struct nvme_controller_data *cdata) -{ - struct controller *c; - - c = calloc(1, sizeof(*c)); - c->qp = qp; - c->cap = nvmf_controller_cap(c->qp); - c->vs = cdata->ver; - c->cdata = *cdata; - - return (c); -} - -void -free_controller(struct controller *c) -{ - free(c); -} diff --git a/usr.sbin/nvmfd/ctl.c b/usr.sbin/nvmfd/ctl.c deleted file mode 100644 index 73e90e1411bd..000000000000 --- a/usr.sbin/nvmfd/ctl.c +++ /dev/null @@ -1,137 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2023 Chelsio Communications, Inc. - * Written by: John Baldwin <jhb@FreeBSD.org> - */ - -#include <sys/param.h> -#include <sys/linker.h> -#include <sys/nv.h> -#include <sys/time.h> -#include <err.h> -#include <errno.h> -#include <fcntl.h> -#include <libnvmf.h> -#include <string.h> - -#include <cam/ctl/ctl.h> -#include <cam/ctl/ctl_io.h> -#include <cam/ctl/ctl_ioctl.h> - -#include "internal.h" - -static int ctl_fd = -1; -static int ctl_port; - -static void -open_ctl(void) -{ - if (ctl_fd > 0) - return; - - ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR); - if (ctl_fd == -1 && errno == ENOENT) { - if (kldload("ctl") == -1) - err(1, "Failed to load ctl.ko"); - ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR); - } - if (ctl_fd == -1) - err(1, "Failed to open %s", CTL_DEFAULT_DEV); -} - -void -init_ctl_port(const char *subnqn, const struct nvmf_association_params *params) -{ - char result_buf[256]; - struct ctl_port_entry entry; - struct ctl_req req; - nvlist_t *nvl; - - open_ctl(); - - nvl = nvlist_create(0); - - nvlist_add_string(nvl, "subnqn", subnqn); - - /* XXX: Hardcoded in discovery.c */ - nvlist_add_stringf(nvl, "portid", "%u", 1); - - nvlist_add_stringf(nvl, "max_io_qsize", "%u", params->max_io_qsize); - - memset(&req, 0, sizeof(req)); - strlcpy(req.driver, "nvmf", sizeof(req.driver)); - req.reqtype = CTL_REQ_CREATE; - req.args = nvlist_pack(nvl, &req.args_len); - if (req.args == NULL) - errx(1, "Failed to pack nvlist for CTL_PORT/CTL_REQ_CREATE"); - req.result = result_buf; - req.result_len = sizeof(result_buf); - if (ioctl(ctl_fd, CTL_PORT_REQ, &req) != 0) - err(1, "ioctl(CTL_PORT/CTL_REQ_CREATE)"); - if (req.status == CTL_LUN_ERROR) - errx(1, "Failed to create CTL port: %s", req.error_str); - if (req.status != CTL_LUN_OK) - errx(1, "Failed to create CTL port: %d", req.status); - - nvlist_destroy(nvl); - nvl = nvlist_unpack(result_buf, req.result_len, 0); - if (nvl == NULL) - errx(1, "Failed to unpack nvlist from CTL_PORT/CTL_REQ_CREATE"); - - ctl_port = nvlist_get_number(nvl, "port_id"); - nvlist_destroy(nvl); - - memset(&entry, 0, sizeof(entry)); - entry.targ_port = ctl_port; - if (ioctl(ctl_fd, CTL_ENABLE_PORT, &entry) != 0) - errx(1, "ioctl(CTL_ENABLE_PORT)"); -} - -void -shutdown_ctl_port(const char *subnqn) -{ - struct ctl_req req; - nvlist_t *nvl; - - open_ctl(); - - nvl = nvlist_create(0); - - nvlist_add_string(nvl, "subnqn", subnqn); - - memset(&req, 0, sizeof(req)); - strlcpy(req.driver, "nvmf", sizeof(req.driver)); - req.reqtype = CTL_REQ_REMOVE; - req.args = nvlist_pack(nvl, &req.args_len); - if (req.args == NULL) - errx(1, "Failed to pack nvlist for CTL_PORT/CTL_REQ_REMOVE"); - if (ioctl(ctl_fd, CTL_PORT_REQ, &req) != 0) - err(1, "ioctl(CTL_PORT/CTL_REQ_REMOVE)"); - if (req.status == CTL_LUN_ERROR) - errx(1, "Failed to remove CTL port: %s", req.error_str); - if (req.status != CTL_LUN_OK) - errx(1, "Failed to remove CTL port: %d", req.status); - - nvlist_destroy(nvl); -} - -void -ctl_handoff_qpair(struct nvmf_qpair *qp, - const struct nvmf_fabric_connect_cmd *cmd, - const struct nvmf_fabric_connect_data *data) -{ - struct ctl_nvmf req; - int error; - - memset(&req, 0, sizeof(req)); - req.type = CTL_NVMF_HANDOFF; - error = nvmf_handoff_controller_qpair(qp, cmd, data, &req.data.handoff); - if (error != 0) { - warnc(error, "Failed to prepare qpair for handoff"); - return; - } - - if (ioctl(ctl_fd, CTL_NVMF, &req) != 0) - warn("ioctl(CTL_NVMF/CTL_NVMF_HANDOFF)"); -} diff --git a/usr.sbin/nvmfd/devices.c b/usr.sbin/nvmfd/devices.c deleted file mode 100644 index fafc1077f207..000000000000 --- a/usr.sbin/nvmfd/devices.c +++ /dev/null @@ -1,386 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2023-2024 Chelsio Communications, Inc. - * Written by: John Baldwin <jhb@FreeBSD.org> - */ - -#include <sys/disk.h> -#include <sys/gsb_crc32.h> -#include <sys/ioctl.h> -#include <sys/stat.h> -#include <net/ieee_oui.h> -#include <err.h> -#include <errno.h> -#include <fcntl.h> -#include <libnvmf.h> -#include <libutil.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "internal.h" - -#define RAMDISK_PREFIX "ramdisk:" - -struct backing_device { - enum { RAMDISK, FILE, CDEV } type; - union { - int fd; /* FILE, CDEV */ - void *mem; /* RAMDISK */ - }; - u_int sector_size; - uint64_t nlbas; - uint64_t eui64; -}; - -static struct backing_device *devices; -static u_int ndevices; - -static uint64_t -generate_eui64(uint32_t low) -{ - return (OUI_FREEBSD_NVME_LOW << 16 | low); -} - -static uint32_t -crc32(const void *buf, size_t len) -{ - return (calculate_crc32c(0xffffffff, buf, len) ^ 0xffffffff); -} - -static void -init_ramdisk(const char *config, struct backing_device *dev) -{ - static uint32_t ramdisk_idx = 1; - uint64_t num; - - dev->type = RAMDISK; - dev->sector_size = 512; - if (expand_number(config, &num)) - errx(1, "Invalid ramdisk specification: %s", config); - if ((num % dev->sector_size) != 0) - errx(1, "Invalid ramdisk size %ju", (uintmax_t)num); - dev->mem = calloc(num, 1); - dev->nlbas = num / dev->sector_size; - dev->eui64 = generate_eui64('M' << 24 | ramdisk_idx++); -} - -static void -init_filedevice(const char *config, int fd, struct stat *sb, - struct backing_device *dev) -{ - dev->type = FILE; - dev->fd = fd; - dev->sector_size = 512; - if ((sb->st_size % dev->sector_size) != 0) - errx(1, "File size is not a multiple of 512: %s", config); - dev->nlbas = sb->st_size / dev->sector_size; - dev->eui64 = generate_eui64('F' << 24 | - (crc32(config, strlen(config)) & 0xffffff)); -} - -static void -init_chardevice(const char *config, int fd, struct backing_device *dev) -{ - off_t len; - - dev->type = CDEV; - dev->fd = fd; - if (ioctl(fd, DIOCGSECTORSIZE, &dev->sector_size) != 0) - err(1, "Failed to fetch sector size for %s", config); - if (ioctl(fd, DIOCGMEDIASIZE, &len) != 0) - err(1, "Failed to fetch sector size for %s", config); - dev->nlbas = len / dev->sector_size; - dev->eui64 = generate_eui64('C' << 24 | - (crc32(config, strlen(config)) & 0xffffff)); -} - -static void -init_device(const char *config, struct backing_device *dev) -{ - struct stat sb; - int fd; - - /* Check for a RAM disk. */ - if (strncmp(RAMDISK_PREFIX, config, strlen(RAMDISK_PREFIX)) == 0) { - init_ramdisk(config + strlen(RAMDISK_PREFIX), dev); - return; - } - - fd = open(config, O_RDWR); - if (fd == -1) - err(1, "Failed to open %s", config); - if (fstat(fd, &sb) == -1) - err(1, "fstat"); - switch (sb.st_mode & S_IFMT) { - case S_IFCHR: - init_chardevice(config, fd, dev); - break; - case S_IFREG: - init_filedevice(config, fd, &sb, dev); - break; - default: - errx(1, "Invalid file type for %s", config); - } -} - -void -register_devices(int ac, char **av) -{ - ndevices = ac; - devices = calloc(ndevices, sizeof(*devices)); - - for (int i = 0; i < ac; i++) - init_device(av[i], &devices[i]); -} - -u_int -device_count(void) -{ - return (ndevices); -} - -static struct backing_device * -lookup_device(uint32_t nsid) -{ - if (nsid == 0 || nsid > ndevices) - return (NULL); - return (&devices[nsid - 1]); -} - -void -device_active_nslist(uint32_t nsid, struct nvme_ns_list *nslist) -{ - u_int count; - - memset(nslist, 0, sizeof(*nslist)); - count = 0; - nsid++; - while (nsid <= ndevices) { - nslist->ns[count] = htole32(nsid); - count++; - if (count == nitems(nslist->ns)) - break; - nsid++; - } -} - -bool -device_identification_descriptor(uint32_t nsid, void *buf) -{ - struct backing_device *dev; - char *p; - - dev = lookup_device(nsid); - if (dev == NULL) - return (false); - - memset(buf, 0, 4096); - - p = buf; - - /* EUI64 */ - *p++ = 1; - *p++ = 8; - p += 2; - be64enc(p, dev->eui64); - return (true); -} - -bool -device_namespace_data(uint32_t nsid, struct nvme_namespace_data *nsdata) -{ - struct backing_device *dev; - - dev = lookup_device(nsid); - if (dev == NULL) - return (false); - - memset(nsdata, 0, sizeof(*nsdata)); - nsdata->nsze = htole64(dev->nlbas); - nsdata->ncap = nsdata->nsze; - nsdata->nuse = nsdata->ncap; - nsdata->nlbaf = 1 - 1; - nsdata->flbas = NVMEF(NVME_NS_DATA_FLBAS_FORMAT, 0); - nsdata->lbaf[0] = NVMEF(NVME_NS_DATA_LBAF_LBADS, - ffs(dev->sector_size) - 1); - - be64enc(nsdata->eui64, dev->eui64); - return (true); -} - -static bool -read_buffer(int fd, void *buf, size_t len, off_t offset) -{ - ssize_t nread; - char *dst; - - dst = buf; - while (len > 0) { - nread = pread(fd, dst, len, offset); - if (nread == -1 && errno == EINTR) - continue; - if (nread <= 0) - return (false); - dst += nread; - len -= nread; - offset += nread; - } - return (true); -} - -void -device_read(uint32_t nsid, uint64_t lba, u_int nlb, - const struct nvmf_capsule *nc) -{ - struct backing_device *dev; - char *p, *src; - off_t off; - size_t len; - - dev = lookup_device(nsid); - if (dev == NULL) { - nvmf_send_generic_error(nc, - NVME_SC_INVALID_NAMESPACE_OR_FORMAT); - return; - } - - if (lba + nlb < lba || lba + nlb > dev->nlbas) { - nvmf_send_generic_error(nc, NVME_SC_LBA_OUT_OF_RANGE); - return; - } - - off = lba * dev->sector_size; - len = nlb * dev->sector_size; - if (nvmf_capsule_data_len(nc) != len) { - nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); - return; - } - - if (dev->type == RAMDISK) { - p = NULL; - src = (char *)dev->mem + off; - } else { - p = malloc(len); - if (!read_buffer(dev->fd, p, len, off)) { - free(p); - nvmf_send_generic_error(nc, - NVME_SC_INTERNAL_DEVICE_ERROR); - return; - } - src = p; - } - - nvmf_send_controller_data(nc, src, len); - free(p); -} - -static bool -write_buffer(int fd, const void *buf, size_t len, off_t offset) -{ - ssize_t nwritten; - const char *src; - - src = buf; - while (len > 0) { - nwritten = pwrite(fd, src, len, offset); - if (nwritten == -1 && errno == EINTR) - continue; - if (nwritten <= 0) - return (false); - src += nwritten; - len -= nwritten; - offset += nwritten; - } - return (true); -} - -void -device_write(uint32_t nsid, uint64_t lba, u_int nlb, - const struct nvmf_capsule *nc) -{ - struct backing_device *dev; - char *p, *dst; - off_t off; - size_t len; - int error; - - dev = lookup_device(nsid); - if (dev == NULL) { - nvmf_send_generic_error(nc, - NVME_SC_INVALID_NAMESPACE_OR_FORMAT); - return; - } - - if (lba + nlb < lba || lba + nlb > dev->nlbas) { - nvmf_send_generic_error(nc, NVME_SC_LBA_OUT_OF_RANGE); - return; - } - - off = lba * dev->sector_size; - len = nlb * dev->sector_size; - if (nvmf_capsule_data_len(nc) != len) { - nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); - return; - } - - if (dev->type == RAMDISK) { - p = NULL; - dst = (char *)dev->mem + off; - } else { - p = malloc(len); - dst = p; - } - - error = nvmf_receive_controller_data(nc, 0, dst, len); - if (error != 0) { - nvmf_send_generic_error(nc, NVME_SC_TRANSIENT_TRANSPORT_ERROR); - free(p); - return; - } - - if (dev->type != RAMDISK) { - if (!write_buffer(dev->fd, p, len, off)) { - free(p); - nvmf_send_generic_error(nc, - NVME_SC_INTERNAL_DEVICE_ERROR); - return; - } - } - free(p); - nvmf_send_success(nc); -} - -void -device_flush(uint32_t nsid, const struct nvmf_capsule *nc) -{ - struct backing_device *dev; - - dev = lookup_device(nsid); - if (dev == NULL) { - nvmf_send_generic_error(nc, - NVME_SC_INVALID_NAMESPACE_OR_FORMAT); - return; - } - - switch (dev->type) { - case RAMDISK: - break; - case FILE: - if (fdatasync(dev->fd) == -1) { - nvmf_send_error(nc, NVME_SCT_MEDIA_ERROR, - NVME_SC_WRITE_FAULTS); - return; - } - break; - case CDEV: - if (ioctl(dev->fd, DIOCGFLUSH) == -1) { - nvmf_send_error(nc, NVME_SCT_MEDIA_ERROR, - NVME_SC_WRITE_FAULTS); - return; - } - } - - nvmf_send_success(nc); -} diff --git a/usr.sbin/nvmfd/discovery.c b/usr.sbin/nvmfd/discovery.c deleted file mode 100644 index 2cfe56731d7c..000000000000 --- a/usr.sbin/nvmfd/discovery.c +++ /dev/null @@ -1,342 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2023-2024 Chelsio Communications, Inc. - * Written by: John Baldwin <jhb@FreeBSD.org> - */ - -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <assert.h> -#include <err.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_data { - struct nvme_discovery_log_entry entry; - bool wildcard; -}; - -struct discovery_controller { - struct nvme_discovery_log *discovery_log; - size_t discovery_log_len; - int s; -}; - -struct discovery_thread_arg { - struct controller *c; - struct nvmf_qpair *qp; - int s; -}; - -static struct io_controller_data *io_controllers; -static struct nvmf_association *discovery_na; -static u_int num_io_controllers; - -static bool -init_discovery_log_entry(struct nvme_discovery_log_entry *entry, int s, - const char *subnqn) -{ - struct sockaddr_storage ss; - socklen_t len; - bool wildcard; - - len = sizeof(ss); - if (getsockname(s, (struct sockaddr *)&ss, &len) == -1) - err(1, "getsockname"); - - memset(entry, 0, sizeof(*entry)); - entry->trtype = NVMF_TRTYPE_TCP; - switch (ss.ss_family) { - case AF_INET: - { - struct sockaddr_in *sin; - - sin = (struct sockaddr_in *)&ss; - entry->adrfam = NVMF_ADRFAM_IPV4; - snprintf(entry->trsvcid, sizeof(entry->trsvcid), "%u", - htons(sin->sin_port)); - if (inet_ntop(AF_INET, &sin->sin_addr, entry->traddr, - sizeof(entry->traddr)) == NULL) - err(1, "inet_ntop"); - wildcard = (sin->sin_addr.s_addr == htonl(INADDR_ANY)); - break; - } - case AF_INET6: - { - struct sockaddr_in6 *sin6; - - sin6 = (struct sockaddr_in6 *)&ss; - entry->adrfam = NVMF_ADRFAM_IPV6; - snprintf(entry->trsvcid, sizeof(entry->trsvcid), "%u", - htons(sin6->sin6_port)); - if (inet_ntop(AF_INET6, &sin6->sin6_addr, entry->traddr, - sizeof(entry->traddr)) == NULL) - err(1, "inet_ntop"); - wildcard = (memcmp(&sin6->sin6_addr, &in6addr_any, - sizeof(in6addr_any)) == 0); - break; - } - default: - errx(1, "Unsupported address family %u", ss.ss_family); - } - entry->subtype = NVMF_SUBTYPE_NVME; - if (flow_control_disable) - entry->treq |= (1 << 2); - entry->portid = htole16(1); - entry->cntlid = htole16(NVMF_CNTLID_DYNAMIC); - entry->aqsz = NVME_MAX_ADMIN_ENTRIES; - strlcpy(entry->subnqn, subnqn, sizeof(entry->subnqn)); - return (wildcard); -} - -void -init_discovery(void) -{ - struct nvmf_association_params aparams; - - memset(&aparams, 0, sizeof(aparams)); - aparams.sq_flow_control = false; - aparams.dynamic_controller_model = true; - aparams.max_admin_qsize = NVME_MAX_ADMIN_ENTRIES; - aparams.tcp.pda = 0; - aparams.tcp.header_digests = header_digests; - aparams.tcp.data_digests = data_digests; - aparams.tcp.maxh2cdata = maxh2cdata; - discovery_na = nvmf_allocate_association(NVMF_TRTYPE_TCP, true, - &aparams); - if (discovery_na == NULL) - err(1, "Failed to create discovery association"); -} - -void -discovery_add_io_controller(int s, const char *subnqn) -{ - struct io_controller_data *icd; - - io_controllers = reallocf(io_controllers, (num_io_controllers + 1) * - sizeof(*io_controllers)); - - icd = &io_controllers[num_io_controllers]; - num_io_controllers++; - - icd->wildcard = init_discovery_log_entry(&icd->entry, s, subnqn); -} - -static void -build_discovery_log_page(struct discovery_controller *dc) -{ - struct sockaddr_storage ss; - socklen_t len; - char traddr[256]; - u_int i, nentries; - uint8_t adrfam; - - if (dc->discovery_log != NULL) - return; - - len = sizeof(ss); - if (getsockname(dc->s, (struct sockaddr *)&ss, &len) == -1) { - warn("build_discovery_log_page: getsockname"); - return; - } - - memset(traddr, 0, sizeof(traddr)); - switch (ss.ss_family) { - case AF_INET: - { - struct sockaddr_in *sin; - - sin = (struct sockaddr_in *)&ss; - adrfam = NVMF_ADRFAM_IPV4; - if (inet_ntop(AF_INET, &sin->sin_addr, traddr, - sizeof(traddr)) == NULL) { - warn("build_discovery_log_page: inet_ntop"); - return; - } - break; - } - case AF_INET6: - { - struct sockaddr_in6 *sin6; - - sin6 = (struct sockaddr_in6 *)&ss; - adrfam = NVMF_ADRFAM_IPV6; - if (inet_ntop(AF_INET6, &sin6->sin6_addr, traddr, - sizeof(traddr)) == NULL) { - warn("build_discovery_log_page: inet_ntop"); - return; - } - break; - } - default: - assert(false); - } - - nentries = 0; - for (i = 0; i < num_io_controllers; i++) { - if (io_controllers[i].wildcard && - io_controllers[i].entry.adrfam != adrfam) - continue; - nentries++; - } - - dc->discovery_log_len = sizeof(*dc->discovery_log) + - nentries * sizeof(struct nvme_discovery_log_entry); - dc->discovery_log = calloc(dc->discovery_log_len, 1); - dc->discovery_log->numrec = nentries; - dc->discovery_log->recfmt = 0; - nentries = 0; - for (i = 0; i < num_io_controllers; i++) { - if (io_controllers[i].wildcard && - io_controllers[i].entry.adrfam != adrfam) - continue; - - dc->discovery_log->entries[nentries] = io_controllers[i].entry; - if (io_controllers[i].wildcard) - memcpy(dc->discovery_log->entries[nentries].traddr, - traddr, sizeof(traddr)); - } -} - -static void -handle_get_log_page_command(const struct nvmf_capsule *nc, - const struct nvme_command *cmd, struct discovery_controller *dc) -{ - uint64_t offset; - uint32_t length; - - switch (nvmf_get_log_page_id(cmd)) { - case NVME_LOG_DISCOVERY: - break; - default: - warnx("Unsupported log page %u for discovery controller", - nvmf_get_log_page_id(cmd)); - goto error; - } - - build_discovery_log_page(dc); - - offset = nvmf_get_log_page_offset(cmd); - if (offset >= dc->discovery_log_len) - goto error; - - length = nvmf_get_log_page_length(cmd); - if (length > dc->discovery_log_len - offset) - length = dc->discovery_log_len - offset; - - nvmf_send_controller_data(nc, (char *)dc->discovery_log + offset, - length); - return; -error: - nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD); -} - -static bool -discovery_command(const struct nvmf_capsule *nc, const struct nvme_command *cmd, - void *arg) -{ - struct discovery_controller *dc = arg; - - switch (cmd->opc) { - case NVME_OPC_GET_LOG_PAGE: - handle_get_log_page_command(nc, cmd, dc); - return (true); - default: - return (false); - } -} - -static void * -discovery_thread(void *arg) -{ - struct discovery_thread_arg *dta = arg; - struct discovery_controller dc; - - pthread_detach(pthread_self()); - - memset(&dc, 0, sizeof(dc)); - dc.s = dta->s; - - controller_handle_admin_commands(dta->c, discovery_command, &dc); - - free(dc.discovery_log); - free_controller(dta->c); - - nvmf_free_qpair(dta->qp); - - close(dta->s); - free(dta); - return (NULL); -} - -void -handle_discovery_socket(int s) -{ - struct nvmf_fabric_connect_data data; - struct nvme_controller_data cdata; - struct nvmf_qpair_params qparams; - struct discovery_thread_arg *dta; - struct nvmf_capsule *nc; - struct nvmf_qpair *qp; - pthread_t thr; - int error; - - memset(&qparams, 0, sizeof(qparams)); - qparams.tcp.fd = s; - - nc = NULL; - qp = nvmf_accept(discovery_na, &qparams, &nc, &data); - if (qp == NULL) { - warnx("Failed to create discovery qpair: %s", - nvmf_association_error(discovery_na)); - goto error; - } - - if (strcmp(data.subnqn, NVMF_DISCOVERY_NQN) != 0) { - warn("Discovery 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; - } - - /* Just use a controller ID of 1 for all discovery controllers. */ - error = nvmf_finish_accept(nc, 1); - if (error != 0) { - warnc(error, "Failed to send CONNECT reponse"); - goto error; - } - - nvmf_init_discovery_controller_data(qp, &cdata); - - dta = malloc(sizeof(*dta)); - dta->qp = qp; - dta->s = s; - dta->c = init_controller(qp, &cdata); - - error = pthread_create(&thr, NULL, discovery_thread, dta); - if (error != 0) { - warnc(error, "Failed to create discovery thread"); - free_controller(dta->c); - free(dta); - goto error; - } - - nvmf_free_capsule(nc); - return; - -error: - if (nc != NULL) - nvmf_free_capsule(nc); - if (qp != NULL) - nvmf_free_qpair(qp); - close(s); -} diff --git a/usr.sbin/nvmfd/internal.h b/usr.sbin/nvmfd/internal.h deleted file mode 100644 index f70dc78881c6..000000000000 --- a/usr.sbin/nvmfd/internal.h +++ /dev/null @@ -1,66 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2023-2024 Chelsio Communications, Inc. - * Written by: John Baldwin <jhb@FreeBSD.org> - */ - -#ifndef __INTERNAL_H__ -#define __INTERNAL_H__ - -#include <stdbool.h> - -struct controller; -struct nvme_command; -struct nvme_controller_data; -struct nvme_ns_list; -struct nvmf_capsule; -struct nvmf_qpair; - -typedef bool handle_command(const struct nvmf_capsule *, - const struct nvme_command *, void *); - -extern bool data_digests; -extern bool header_digests; -extern bool flow_control_disable; -extern bool kernel_io; -extern uint32_t maxh2cdata; - -/* controller.c */ -void controller_handle_admin_commands(struct controller *c, - handle_command *cb, void *cb_arg); -struct controller *init_controller(struct nvmf_qpair *qp, - const struct nvme_controller_data *cdata); -void free_controller(struct controller *c); - -/* discovery.c */ -void init_discovery(void); -void handle_discovery_socket(int s); -void discovery_add_io_controller(int s, const char *subnqn); - -/* io.c */ -void init_io(const char *subnqn); -void handle_io_socket(int s); -void shutdown_io(void); - -/* devices.c */ -void register_devices(int ac, char **av); -u_int device_count(void); -void device_active_nslist(uint32_t nsid, struct nvme_ns_list *nslist); -bool device_identification_descriptor(uint32_t nsid, void *buf); -bool device_namespace_data(uint32_t nsid, struct nvme_namespace_data *nsdata); -void device_read(uint32_t nsid, uint64_t lba, u_int nlb, - const struct nvmf_capsule *nc); -void device_write(uint32_t nsid, uint64_t lba, u_int nlb, - const struct nvmf_capsule *nc); -void device_flush(uint32_t nsid, const struct nvmf_capsule *nc); - -/* ctl.c */ -void init_ctl_port(const char *subnqn, - const struct nvmf_association_params *params); -void ctl_handoff_qpair(struct nvmf_qpair *qp, - const struct nvmf_fabric_connect_cmd *cmd, - const struct nvmf_fabric_connect_data *data); -void shutdown_ctl_port(const char *subnqn); - -#endif /* !__INTERNAL_H__ */ 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); - } -} diff --git a/usr.sbin/nvmfd/nvmfd.8 b/usr.sbin/nvmfd/nvmfd.8 deleted file mode 100644 index 1076583c417c..000000000000 --- a/usr.sbin/nvmfd/nvmfd.8 +++ /dev/null @@ -1,131 +0,0 @@ -.\" -.\" SPDX-License-Identifier: BSD-2-Clause -.\" -.\" Copyright (c) 2024 Chelsio Communications, Inc. -.\" -.Dd July 25, 2024 -.Dt NVMFD 8 -.Os -.Sh NAME -.Nm nvmfd -.Nd "NVMeoF controller daemon" -.Sh SYNOPSIS -.Nm -.Fl K -.Op Fl dFGg -.Op Fl H Ar MAXH2CDATA -.Op Fl P Ar port -.Op Fl p Ar port -.Op Fl t Ar transport -.Op Fl n Ar subnqn -.Nm -.Op Fl dFGg -.Op Fl H Ar MAXH2CDATA -.Op Fl P Ar port -.Op Fl p Ar port -.Op Fl t Ar transport -.Op Fl n Ar subnqn -.Ar device -.Op Ar device ... -.Sh DESCRIPTION -.Nm -accepts incoming NVMeoF connections for both I/O and discovery controllers. -.Nm -can either implement a single dynamic I/O controller in user mode or hand -off incoming I/O controller connections to -.Xr nvmft 4 . -A dynamic discovery controller service is always provided in user mode. -.Pp -The following options are available: -.Bl -tag -width "-t transport" -.It Fl F -Permit remote hosts to disable SQ flow control. -.It Fl G -Permit remote hosts to enable PDU data digests for the TCP transport. -.It Fl g -Permit remote hosts to enable PDU header digests for the TCP transport. -.It Fl H -Set the MAXH2CDATA value advertised to the remote host for the TCP transport. -This value is in bytes and determines the maximum data payload size for -data PDUs sent by the remote host. -The value must be at least 4096 and defaults to 256KiB. -.It Fl K -Enable kernel mode which hands off incoming I/O controller connections to -.Xr nvmft 4 . -.It Fl P Ar port -Use -.Ar port -as the listen TCP port for the discovery controller service. -The default value is 8009. -.It Fl d -Enable debug mode. -The daemon sends any errors to standard output and does not place -itself in the background. -.It Fl p Ar port -Use -.Ar port -as the listen TCP port for the I/O controller service. -By default an unused ephemeral port will be chosen. -.It Fl n Ar subnqn -The Subsystem NVMe Qualified Name for the I/O controller. -If an explicit NQN is not given, a default value is generated from the -current host's UUID obtained from the -.Vt kern.hostuuid -sysctl. -.It Fl t Ar transport -The transport type to use. -The default transport is -.Dq tcp . -.It Ar device -When implementing a user mode I/O controller, -one or more -.Ar device -arguments must be specified. -Each -.Ar device -describes the backing store for a namespace exported to remote hosts. -Devices can be specified using one of the following syntaxes: -.Bl -tag -width "ramdisk:size" -.It Pa pathname -File or disk device -.It ramdisk : Ns Ar size -Allocate a memory disk with the given -.Ar size . -.Ar size -may use any of the suffixes supported by -.Xr expand_number 3 . -.El -.El -.Sh FILES -.Bl -tag -width "/var/run/nvmfd.pid" -compact -.It Pa /var/run/nvmfd.pid -The default location of the -.Nm -PID file. -.El -.Sh EXIT STATUS -.Ex -std -.Sh SEE ALSO -.Xr ctl 4 , -.Xr nvmft 4 , -.Xr ctladm 8 , -.Xr ctld 8 -.Sh HISTORY -The -.Nm -module first appeared in -.Fx 15.0 . -.Sh AUTHORS -The -.Nm -subsystem was developed by -.An John Baldwin Aq Mt jhb@FreeBSD.org -under sponsorship from Chelsio Communications, Inc. -.Sh BUGS -The discovery controller and kernel mode functionality of -.Nm -should be merged into -.Xr ctld 8 . -.Pp -Additional parameters such as -queue sizes should be configurable. diff --git a/usr.sbin/nvmfd/nvmfd.c b/usr.sbin/nvmfd/nvmfd.c deleted file mode 100644 index df6f400b40e5..000000000000 --- a/usr.sbin/nvmfd/nvmfd.c +++ /dev/null @@ -1,271 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause - * - * Copyright (c) 2023-2024 Chelsio Communications, Inc. - * Written by: John Baldwin <jhb@FreeBSD.org> - */ - -#include <sys/param.h> -#include <sys/event.h> -#include <sys/linker.h> -#include <sys/module.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <assert.h> -#include <err.h> -#include <errno.h> -#include <libnvmf.h> -#include <libutil.h> -#include <netdb.h> -#include <signal.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#include "internal.h" - -bool data_digests = false; -bool header_digests = false; -bool flow_control_disable = false; -bool kernel_io = false; -uint32_t maxh2cdata = 256 * 1024; - -static const char *subnqn; -static volatile bool quit = false; - -static void -usage(void) -{ - fprintf(stderr, "nvmfd -K [-dFGg] [-H MAXH2CDATA] [-P port] [-p port] [-t transport] [-n subnqn]\n" - "nvmfd [-dFGg] [-H MAXH2CDATA] [-P port] [-p port] [-t transport] [-n subnqn]\n" - "\tdevice [device [...]]\n" - "\n" - "Devices use one of the following syntaxes:\n" - "\tpathame - file or disk device\n" - "\tramdisk:size - memory disk of given size\n"); - exit(1); -} - -static void -handle_sig(int sig __unused) -{ - quit = true; -} - -static void -register_listen_socket(int kqfd, int s, void *udata) -{ - struct kevent kev; - - if (listen(s, -1) != 0) - err(1, "listen"); - - EV_SET(&kev, s, EVFILT_READ, EV_ADD, 0, 0, udata); - if (kevent(kqfd, &kev, 1, NULL, 0, NULL) == -1) - err(1, "kevent: failed to add listen socket"); -} - -static void -create_passive_sockets(int kqfd, const char *port, bool discovery) -{ - struct addrinfo hints, *ai, *list; - bool created; - int error, s; - - memset(&hints, 0, sizeof(hints)); - hints.ai_flags = AI_PASSIVE; - hints.ai_family = AF_UNSPEC; - hints.ai_protocol = IPPROTO_TCP; - error = getaddrinfo(NULL, port, &hints, &list); - if (error != 0) - errx(1, "%s", gai_strerror(error)); - created = false; - - for (ai = list; ai != NULL; ai = ai->ai_next) { - s = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol); - if (s == -1) - continue; - - if (bind(s, ai->ai_addr, ai->ai_addrlen) != 0) { - close(s); - continue; - } - - if (discovery) { - register_listen_socket(kqfd, s, (void *)1); - } else { - register_listen_socket(kqfd, s, (void *)2); - discovery_add_io_controller(s, subnqn); - } - created = true; - } - - freeaddrinfo(list); - if (!created) - err(1, "Failed to create any listen sockets"); -} - -static void -handle_connections(int kqfd) -{ - struct kevent ev; - int s; - - signal(SIGHUP, handle_sig); - signal(SIGINT, handle_sig); - signal(SIGQUIT, handle_sig); - signal(SIGTERM, handle_sig); - - while (!quit) { - if (kevent(kqfd, NULL, 0, &ev, 1, NULL) == -1) { - if (errno == EINTR) - continue; - err(1, "kevent"); - } - - assert(ev.filter == EVFILT_READ); - - s = accept(ev.ident, NULL, NULL); - if (s == -1) { - warn("accept"); - continue; - } - - switch ((uintptr_t)ev.udata) { - case 1: - handle_discovery_socket(s); - break; - case 2: - handle_io_socket(s); - break; - default: - __builtin_unreachable(); - } - } -} - -int -main(int ac, char **av) -{ - struct pidfh *pfh; - const char *dport, *ioport, *transport; - pid_t pid; - uint64_t value; - int ch, error, kqfd; - bool daemonize; - static char nqn[NVMF_NQN_MAX_LEN]; - - /* 7.4.9.3 Default port for discovery */ - dport = "8009"; - - pfh = NULL; - daemonize = true; - ioport = "0"; - subnqn = NULL; - transport = "tcp"; - while ((ch = getopt(ac, av, "dFgGH:Kn:P:p:t:")) != -1) { - switch (ch) { - case 'd': - daemonize = false; - break; - case 'F': - flow_control_disable = true; - break; - case 'G': - data_digests = true; - break; - case 'g': - header_digests = true; - break; - case 'H': - if (expand_number(optarg, &value) != 0) - errx(1, "Invalid MAXH2CDATA value %s", optarg); - if (value < 4096 || value > UINT32_MAX || - value % 4 != 0) - errx(1, "Invalid MAXH2CDATA value %s", optarg); - maxh2cdata = value; - break; - case 'K': - kernel_io = true; - break; - case 'n': - subnqn = optarg; - break; - case 'P': - dport = optarg; - break; - case 'p': - ioport = optarg; - break; - case 't': - transport = optarg; - break; - default: - usage(); - } - } - - av += optind; - ac -= optind; - - if (kernel_io) { - if (ac > 0) - usage(); - if (modfind("nvmft") == -1 && kldload("nvmft") == -1) - warn("couldn't load nvmft"); - } else { - if (ac < 1) - usage(); - } - - if (strcasecmp(transport, "tcp") == 0) { - } else - errx(1, "Invalid transport %s", transport); - - if (subnqn == NULL) { - error = nvmf_nqn_from_hostuuid(nqn); - if (error != 0) - errc(1, error, "Failed to generate NQN"); - subnqn = nqn; - } - - if (!kernel_io) - register_devices(ac, av); - - init_discovery(); - init_io(subnqn); - - if (daemonize) { - pfh = pidfile_open(NULL, 0600, &pid); - if (pfh == NULL) { - if (errno == EEXIST) - errx(1, "Daemon already running, pid: %jd", - (intmax_t)pid); - warn("Cannot open or create pidfile"); - } - - if (daemon(0, 0) != 0) { - pidfile_remove(pfh); - err(1, "Failed to fork into the background"); - } - - pidfile_write(pfh); - } - - kqfd = kqueue(); - if (kqfd == -1) { - pidfile_remove(pfh); - err(1, "kqueue"); - } - - create_passive_sockets(kqfd, dport, true); - create_passive_sockets(kqfd, ioport, false); - - handle_connections(kqfd); - shutdown_io(); - if (pfh != NULL) - pidfile_remove(pfh); - return (0); -} |