]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/nvmfd/controller.c
zfs: merge openzfs/zfs@8f1b7a6fa
[FreeBSD/FreeBSD.git] / usr.sbin / nvmfd / controller.c
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 whiled 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 }