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