opnsense-src/usr.sbin/nvmfd/ctl.c
John Baldwin 365b89e8ea nvmf: Switch several ioctls to using nvlists
For requests that handoff queues from userspace to the kernel as well
as the request to fetch reconnect parameters from the kernel, switch
from using flat structures to nvlists.  In particular, this will
permit adding support for additional transports in the future without
breaking the ABI of the structures.

Note that this is an ABI break for the ioctls used by nvmf(4) and
nvmft(4).  Since this is only present in main I did not bother
implementing compatability shims.

Inspired by:	imp (suggestion on a different review)
Reviewed by:	imp
Sponsored by:	Chelsio Communications
Differential Revision:	https://reviews.freebsd.org/D48230
2024-12-30 13:52:21 -05:00

137 lines
3.3 KiB
C

/*-
* 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)");
}