xref: /freebsd/usr.sbin/nvmfd/ctl.c (revision e63d20b70ee1dbee9b075f29de6f30cdcfe1abe1)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2023 Chelsio Communications, Inc.
5  * Written by: John Baldwin <jhb@FreeBSD.org>
6  */
7 
8 #include <sys/param.h>
9 #include <sys/linker.h>
10 #include <sys/nv.h>
11 #include <sys/time.h>
12 #include <err.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <libnvmf.h>
16 #include <string.h>
17 
18 #include <cam/ctl/ctl.h>
19 #include <cam/ctl/ctl_io.h>
20 #include <cam/ctl/ctl_ioctl.h>
21 
22 #include "internal.h"
23 
24 static int ctl_fd = -1;
25 static int ctl_port;
26 
27 static void
28 open_ctl(void)
29 {
30 	if (ctl_fd > 0)
31 		return;
32 
33 	ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR);
34 	if (ctl_fd == -1 && errno == ENOENT) {
35 		if (kldload("ctl") == -1)
36 			err(1, "Failed to load ctl.ko");
37 		ctl_fd = open(CTL_DEFAULT_DEV, O_RDWR);
38 	}
39 	if (ctl_fd == -1)
40 		err(1, "Failed to open %s", CTL_DEFAULT_DEV);
41 }
42 
43 void
44 init_ctl_port(const char *subnqn, const struct nvmf_association_params *params)
45 {
46 	char result_buf[256];
47 	struct ctl_port_entry entry;
48 	struct ctl_req req;
49 	nvlist_t *nvl;
50 
51 	open_ctl();
52 
53 	nvl = nvlist_create(0);
54 
55 	nvlist_add_string(nvl, "subnqn", subnqn);
56 
57 	/* XXX: Hardcoded in discovery.c */
58 	nvlist_add_stringf(nvl, "portid", "%u", 1);
59 
60 	nvlist_add_stringf(nvl, "max_io_qsize", "%u", params->max_io_qsize);
61 
62 	memset(&req, 0, sizeof(req));
63 	strlcpy(req.driver, "nvmf", sizeof(req.driver));
64 	req.reqtype = CTL_REQ_CREATE;
65 	req.args = nvlist_pack(nvl, &req.args_len);
66 	if (req.args == NULL)
67 		errx(1, "Failed to pack nvlist for CTL_PORT/CTL_REQ_CREATE");
68 	req.result = result_buf;
69 	req.result_len = sizeof(result_buf);
70 	if (ioctl(ctl_fd, CTL_PORT_REQ, &req) != 0)
71 		err(1, "ioctl(CTL_PORT/CTL_REQ_CREATE)");
72 	if (req.status == CTL_LUN_ERROR)
73 		errx(1, "Failed to create CTL port: %s", req.error_str);
74 	if (req.status != CTL_LUN_OK)
75 		errx(1, "Failed to create CTL port: %d", req.status);
76 
77 	nvlist_destroy(nvl);
78 	nvl = nvlist_unpack(result_buf, req.result_len, 0);
79 	if (nvl == NULL)
80 		errx(1, "Failed to unpack nvlist from CTL_PORT/CTL_REQ_CREATE");
81 
82 	ctl_port = nvlist_get_number(nvl, "port_id");
83 	nvlist_destroy(nvl);
84 
85 	memset(&entry, 0, sizeof(entry));
86 	entry.targ_port = ctl_port;
87 	if (ioctl(ctl_fd, CTL_ENABLE_PORT, &entry) != 0)
88 		errx(1, "ioctl(CTL_ENABLE_PORT)");
89 }
90 
91 void
92 shutdown_ctl_port(const char *subnqn)
93 {
94 	struct ctl_req req;
95 	nvlist_t *nvl;
96 
97 	open_ctl();
98 
99 	nvl = nvlist_create(0);
100 
101 	nvlist_add_string(nvl, "subnqn", subnqn);
102 
103 	memset(&req, 0, sizeof(req));
104 	strlcpy(req.driver, "nvmf", sizeof(req.driver));
105 	req.reqtype = CTL_REQ_REMOVE;
106 	req.args = nvlist_pack(nvl, &req.args_len);
107 	if (req.args == NULL)
108 		errx(1, "Failed to pack nvlist for CTL_PORT/CTL_REQ_REMOVE");
109 	if (ioctl(ctl_fd, CTL_PORT_REQ, &req) != 0)
110 		err(1, "ioctl(CTL_PORT/CTL_REQ_REMOVE)");
111 	if (req.status == CTL_LUN_ERROR)
112 		errx(1, "Failed to remove CTL port: %s", req.error_str);
113 	if (req.status != CTL_LUN_OK)
114 		errx(1, "Failed to remove CTL port: %d", req.status);
115 
116 	nvlist_destroy(nvl);
117 }
118 
119 void
120 ctl_handoff_qpair(struct nvmf_qpair *qp,
121     const struct nvmf_fabric_connect_cmd *cmd,
122     const struct nvmf_fabric_connect_data *data)
123 {
124 	struct ctl_nvmf req;
125 	int error;
126 
127 	memset(&req, 0, sizeof(req));
128 	req.type = CTL_NVMF_HANDOFF;
129 	error = nvmf_handoff_controller_qpair(qp, &req.data.handoff);
130 	if (error != 0) {
131 		warnc(error, "Failed to prepare qpair for handoff");
132 		return;
133 	}
134 
135 	req.data.handoff.cmd = cmd;
136 	req.data.handoff.data = data;
137 	if (ioctl(ctl_fd, CTL_NVMF, &req) != 0)
138 		warn("ioctl(CTL_NVMF/CTL_NVMF_HANDOFF)");
139 }
140