xref: /freebsd/usr.sbin/nvmfd/controller.c (revision 96190b4fef3b4a0cc3ca0606b0c4e3e69a5e6717)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause
3  *
4  * Copyright (c) 2023-2024 Chelsio Communications, Inc.
5  * Written by: John Baldwin <jhb@FreeBSD.org>
6  */
7 
8 #include <err.h>
9 #include <errno.h>
10 #include <libnvmf.h>
11 #include <stdlib.h>
12 
13 #include "internal.h"
14 
15 struct controller {
16 	struct nvmf_qpair *qp;
17 
18 	uint64_t cap;
19 	uint32_t vs;
20 	uint32_t cc;
21 	uint32_t csts;
22 
23 	bool shutdown;
24 
25 	struct nvme_controller_data cdata;
26 };
27 
28 static bool
29 update_cc(struct controller *c, uint32_t new_cc)
30 {
31 	uint32_t changes;
32 
33 	if (c->shutdown)
34 		return (false);
35 	if (!nvmf_validate_cc(c->qp, c->cap, c->cc, new_cc))
36 		return (false);
37 
38 	changes = c->cc ^ new_cc;
39 	c->cc = new_cc;
40 
41 	/* Handle shutdown requests. */
42 	if (NVMEV(NVME_CC_REG_SHN, changes) != 0 &&
43 	    NVMEV(NVME_CC_REG_SHN, new_cc) != 0) {
44 		c->csts &= ~NVMEM(NVME_CSTS_REG_SHST);
45 		c->csts |= NVMEF(NVME_CSTS_REG_SHST, NVME_SHST_COMPLETE);
46 		c->shutdown = true;
47 	}
48 
49 	if (NVMEV(NVME_CC_REG_EN, changes) != 0) {
50 		if (NVMEV(NVME_CC_REG_EN, new_cc) == 0) {
51 			/* Controller reset. */
52 			c->csts = 0;
53 			c->shutdown = true;
54 		} else
55 			c->csts |= NVMEF(NVME_CSTS_REG_RDY, 1);
56 	}
57 	return (true);
58 }
59 
60 static void
61 handle_property_get(const struct controller *c, const struct nvmf_capsule *nc,
62     const struct nvmf_fabric_prop_get_cmd *pget)
63 {
64 	struct nvmf_fabric_prop_get_rsp rsp;
65 
66 	nvmf_init_cqe(&rsp, nc, 0);
67 
68 	switch (le32toh(pget->ofst)) {
69 	case NVMF_PROP_CAP:
70 		if (pget->attrib.size != NVMF_PROP_SIZE_8)
71 			goto error;
72 		rsp.value.u64 = htole64(c->cap);
73 		break;
74 	case NVMF_PROP_VS:
75 		if (pget->attrib.size != NVMF_PROP_SIZE_4)
76 			goto error;
77 		rsp.value.u32.low = htole32(c->vs);
78 		break;
79 	case NVMF_PROP_CC:
80 		if (pget->attrib.size != NVMF_PROP_SIZE_4)
81 			goto error;
82 		rsp.value.u32.low = htole32(c->cc);
83 		break;
84 	case NVMF_PROP_CSTS:
85 		if (pget->attrib.size != NVMF_PROP_SIZE_4)
86 			goto error;
87 		rsp.value.u32.low = htole32(c->csts);
88 		break;
89 	default:
90 		goto error;
91 	}
92 
93 	nvmf_send_response(nc, &rsp);
94 	return;
95 error:
96 	nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD);
97 }
98 
99 static void
100 handle_property_set(struct controller *c, const struct nvmf_capsule *nc,
101     const struct nvmf_fabric_prop_set_cmd *pset)
102 {
103 	switch (le32toh(pset->ofst)) {
104 	case NVMF_PROP_CC:
105 		if (pset->attrib.size != NVMF_PROP_SIZE_4)
106 			goto error;
107 		if (!update_cc(c, le32toh(pset->value.u32.low)))
108 			goto error;
109 		break;
110 	default:
111 		goto error;
112 	}
113 
114 	nvmf_send_success(nc);
115 	return;
116 error:
117 	nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD);
118 }
119 
120 static void
121 handle_fabrics_command(struct controller *c,
122     const struct nvmf_capsule *nc, const struct nvmf_fabric_cmd *fc)
123 {
124 	switch (fc->fctype) {
125 	case NVMF_FABRIC_COMMAND_PROPERTY_GET:
126 		handle_property_get(c, nc,
127 		    (const struct nvmf_fabric_prop_get_cmd *)fc);
128 		break;
129 	case NVMF_FABRIC_COMMAND_PROPERTY_SET:
130 		handle_property_set(c, nc,
131 		    (const struct nvmf_fabric_prop_set_cmd *)fc);
132 		break;
133 	case NVMF_FABRIC_COMMAND_CONNECT:
134 		warnx("CONNECT command on connected queue");
135 		nvmf_send_generic_error(nc, NVME_SC_COMMAND_SEQUENCE_ERROR);
136 		break;
137 	case NVMF_FABRIC_COMMAND_DISCONNECT:
138 		warnx("DISCONNECT command on admin queue");
139 		nvmf_send_error(nc, NVME_SCT_COMMAND_SPECIFIC,
140 		    NVMF_FABRIC_SC_INVALID_QUEUE_TYPE);
141 		break;
142 	default:
143 		warnx("Unsupported fabrics command %#x", fc->fctype);
144 		nvmf_send_generic_error(nc, NVME_SC_INVALID_OPCODE);
145 		break;
146 	}
147 }
148 
149 static void
150 handle_identify_command(const struct controller *c,
151     const struct nvmf_capsule *nc, const struct nvme_command *cmd)
152 {
153 	uint8_t cns;
154 
155 	cns = le32toh(cmd->cdw10) & 0xFF;
156 	switch (cns) {
157 	case 1:
158 		break;
159 	default:
160 		warnx("Unsupported CNS %#x for IDENTIFY", cns);
161 		goto error;
162 	}
163 
164 	nvmf_send_controller_data(nc, &c->cdata, sizeof(c->cdata));
165 	return;
166 error:
167 	nvmf_send_generic_error(nc, NVME_SC_INVALID_FIELD);
168 }
169 
170 void
171 controller_handle_admin_commands(struct controller *c, handle_command *cb,
172     void *cb_arg)
173 {
174 	struct nvmf_qpair *qp = c->qp;
175 	const struct nvme_command *cmd;
176 	struct nvmf_capsule *nc;
177 	int error;
178 
179 	for (;;) {
180 		error = nvmf_controller_receive_capsule(qp, &nc);
181 		if (error != 0) {
182 			if (error != ECONNRESET)
183 				warnc(error, "Failed to read command capsule");
184 			break;
185 		}
186 
187 		cmd = nvmf_capsule_sqe(nc);
188 
189 		/*
190 		 * Only permit Fabrics commands while a controller is
191 		 * disabled.
192 		 */
193 		if (NVMEV(NVME_CC_REG_EN, c->cc) == 0 &&
194 		    cmd->opc != NVME_OPC_FABRICS_COMMANDS) {
195 			warnx("Unsupported admin opcode %#x while disabled\n",
196 			    cmd->opc);
197 			nvmf_send_generic_error(nc,
198 			    NVME_SC_COMMAND_SEQUENCE_ERROR);
199 			nvmf_free_capsule(nc);
200 			continue;
201 		}
202 
203 		if (cb(nc, cmd, cb_arg)) {
204 			nvmf_free_capsule(nc);
205 			continue;
206 		}
207 
208 		switch (cmd->opc) {
209 		case NVME_OPC_FABRICS_COMMANDS:
210 			handle_fabrics_command(c, nc,
211 			    (const struct nvmf_fabric_cmd *)cmd);
212 			break;
213 		case NVME_OPC_IDENTIFY:
214 			handle_identify_command(c, nc, cmd);
215 			break;
216 		default:
217 			warnx("Unsupported admin opcode %#x", cmd->opc);
218 			nvmf_send_generic_error(nc, NVME_SC_INVALID_OPCODE);
219 			break;
220 		}
221 		nvmf_free_capsule(nc);
222 	}
223 }
224 
225 struct controller *
226 init_controller(struct nvmf_qpair *qp,
227     const struct nvme_controller_data *cdata)
228 {
229 	struct controller *c;
230 
231 	c = calloc(1, sizeof(*c));
232 	c->qp = qp;
233 	c->cap = nvmf_controller_cap(c->qp);
234 	c->vs = cdata->ver;
235 	c->cdata = *cdata;
236 
237 	return (c);
238 }
239 
240 void
241 free_controller(struct controller *c)
242 {
243 	free(c);
244 }
245