]> git.proxmox.com Git - ceph.git/blob - ceph/src/spdk/lib/nvme/nvme_fabric.c
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / spdk / lib / nvme / nvme_fabric.c
1 /*-
2 * BSD LICENSE
3 *
4 * Copyright (c) Intel Corporation.
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * * Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * * Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in
15 * the documentation and/or other materials provided with the
16 * distribution.
17 * * Neither the name of Intel Corporation nor the names of its
18 * contributors may be used to endorse or promote products derived
19 * from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 /*
35 * NVMe over Fabrics transport-independent functions
36 */
37
38 #include "nvme_internal.h"
39
40 #include "spdk/endian.h"
41 #include "spdk/string.h"
42
43 static int
44 nvme_fabric_prop_set_cmd(struct spdk_nvme_ctrlr *ctrlr,
45 uint32_t offset, uint8_t size, uint64_t value)
46 {
47 struct spdk_nvmf_fabric_prop_set_cmd cmd = {};
48 struct nvme_completion_poll_status status;
49 int rc;
50
51 assert(size == SPDK_NVMF_PROP_SIZE_4 || size == SPDK_NVMF_PROP_SIZE_8);
52
53 cmd.opcode = SPDK_NVME_OPC_FABRIC;
54 cmd.fctype = SPDK_NVMF_FABRIC_COMMAND_PROPERTY_SET;
55 cmd.ofst = offset;
56 cmd.attrib.size = size;
57 cmd.value.u64 = value;
58
59 rc = spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, (struct spdk_nvme_cmd *)&cmd,
60 NULL, 0,
61 nvme_completion_poll_cb, &status);
62 if (rc < 0) {
63 return rc;
64 }
65
66 if (spdk_nvme_wait_for_completion(ctrlr->adminq, &status)) {
67 SPDK_ERRLOG("Property Set failed\n");
68 return -1;
69 }
70
71 return 0;
72 }
73
74 static int
75 nvme_fabric_prop_get_cmd(struct spdk_nvme_ctrlr *ctrlr,
76 uint32_t offset, uint8_t size, uint64_t *value)
77 {
78 struct spdk_nvmf_fabric_prop_set_cmd cmd = {};
79 struct nvme_completion_poll_status status;
80 struct spdk_nvmf_fabric_prop_get_rsp *response;
81 int rc;
82
83 assert(size == SPDK_NVMF_PROP_SIZE_4 || size == SPDK_NVMF_PROP_SIZE_8);
84
85 cmd.opcode = SPDK_NVME_OPC_FABRIC;
86 cmd.fctype = SPDK_NVMF_FABRIC_COMMAND_PROPERTY_GET;
87 cmd.ofst = offset;
88 cmd.attrib.size = size;
89
90 rc = spdk_nvme_ctrlr_cmd_admin_raw(ctrlr, (struct spdk_nvme_cmd *)&cmd,
91 NULL, 0, nvme_completion_poll_cb,
92 &status);
93 if (rc < 0) {
94 return rc;
95 }
96
97 if (spdk_nvme_wait_for_completion(ctrlr->adminq, &status)) {
98 SPDK_ERRLOG("Property Get failed\n");
99 return -1;
100 }
101
102 response = (struct spdk_nvmf_fabric_prop_get_rsp *)&status.cpl;
103
104 if (size == SPDK_NVMF_PROP_SIZE_4) {
105 *value = response->value.u32.low;
106 } else {
107 *value = response->value.u64;
108 }
109
110 return 0;
111 }
112
113 int
114 nvme_fabric_ctrlr_set_reg_4(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint32_t value)
115 {
116 return nvme_fabric_prop_set_cmd(ctrlr, offset, SPDK_NVMF_PROP_SIZE_4, value);
117 }
118
119 int
120 nvme_fabric_ctrlr_set_reg_8(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint64_t value)
121 {
122 return nvme_fabric_prop_set_cmd(ctrlr, offset, SPDK_NVMF_PROP_SIZE_8, value);
123 }
124
125 int
126 nvme_fabric_ctrlr_get_reg_4(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint32_t *value)
127 {
128 uint64_t tmp_value;
129 int rc;
130 rc = nvme_fabric_prop_get_cmd(ctrlr, offset, SPDK_NVMF_PROP_SIZE_4, &tmp_value);
131
132 if (!rc) {
133 *value = (uint32_t)tmp_value;
134 }
135 return rc;
136 }
137
138 int
139 nvme_fabric_ctrlr_get_reg_8(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint64_t *value)
140 {
141 return nvme_fabric_prop_get_cmd(ctrlr, offset, SPDK_NVMF_PROP_SIZE_8, value);
142 }
143
144 static void
145 nvme_fabric_discover_probe(struct spdk_nvmf_discovery_log_page_entry *entry,
146 void *cb_ctx, spdk_nvme_probe_cb probe_cb)
147 {
148 struct spdk_nvme_transport_id trid;
149 uint8_t *end;
150 size_t len;
151
152 memset(&trid, 0, sizeof(trid));
153
154 if (entry->subtype == SPDK_NVMF_SUBTYPE_DISCOVERY) {
155 SPDK_WARNLOG("Skipping unsupported discovery service referral\n");
156 return;
157 } else if (entry->subtype != SPDK_NVMF_SUBTYPE_NVME) {
158 SPDK_WARNLOG("Skipping unknown subtype %u\n", entry->subtype);
159 return;
160 }
161
162 trid.trtype = entry->trtype;
163 if (!spdk_nvme_transport_available(trid.trtype)) {
164 SPDK_WARNLOG("NVMe transport type %u not available; skipping probe\n",
165 trid.trtype);
166 return;
167 }
168
169 trid.adrfam = entry->adrfam;
170
171 /* Ensure that subnqn is null terminated. */
172 end = memchr(entry->subnqn, '\0', SPDK_NVMF_NQN_MAX_LEN + 1);
173 if (!end) {
174 SPDK_ERRLOG("Discovery entry SUBNQN is not null terminated\n");
175 return;
176 }
177 len = end - entry->subnqn;
178 memcpy(trid.subnqn, entry->subnqn, len);
179 trid.subnqn[len] = '\0';
180
181 /* Convert traddr to a null terminated string. */
182 len = spdk_strlen_pad(entry->traddr, sizeof(entry->traddr), ' ');
183 memcpy(trid.traddr, entry->traddr, len);
184 if (spdk_str_chomp(trid.traddr) != 0) {
185 SPDK_DEBUGLOG(SPDK_LOG_NVME, "Trailing newlines removed from discovery TRADDR\n");
186 }
187
188 /* Convert trsvcid to a null terminated string. */
189 len = spdk_strlen_pad(entry->trsvcid, sizeof(entry->trsvcid), ' ');
190 memcpy(trid.trsvcid, entry->trsvcid, len);
191 if (spdk_str_chomp(trid.trsvcid) != 0) {
192 SPDK_DEBUGLOG(SPDK_LOG_NVME, "Trailing newlines removed from discovery TRSVCID\n");
193 }
194
195 SPDK_DEBUGLOG(SPDK_LOG_NVME, "subnqn=%s, trtype=%u, traddr=%s, trsvcid=%s\n",
196 trid.subnqn, trid.trtype,
197 trid.traddr, trid.trsvcid);
198
199 nvme_ctrlr_probe(&trid, NULL, probe_cb, cb_ctx);
200 }
201
202 static int
203 nvme_fabric_get_discovery_log_page(struct spdk_nvme_ctrlr *ctrlr,
204 void *log_page, uint32_t size, uint64_t offset)
205 {
206 struct nvme_completion_poll_status status;
207 int rc;
208
209 rc = spdk_nvme_ctrlr_cmd_get_log_page(ctrlr, SPDK_NVME_LOG_DISCOVERY, 0, log_page, size, offset,
210 nvme_completion_poll_cb, &status);
211 if (rc < 0) {
212 return -1;
213 }
214
215 if (spdk_nvme_wait_for_completion(ctrlr->adminq, &status)) {
216 return -1;
217 }
218
219 return 0;
220 }
221
222 int
223 nvme_fabric_ctrlr_discover(struct spdk_nvme_ctrlr *ctrlr,
224 void *cb_ctx, spdk_nvme_probe_cb probe_cb)
225 {
226 struct spdk_nvmf_discovery_log_page *log_page;
227 struct spdk_nvmf_discovery_log_page_entry *log_page_entry;
228 char buffer[4096];
229 int rc;
230 uint64_t i, numrec, buffer_max_entries_first, buffer_max_entries, log_page_offset = 0;
231 uint64_t remaining_num_rec = 0;
232 uint16_t recfmt;
233
234 memset(buffer, 0x0, 4096);
235 buffer_max_entries_first = (sizeof(buffer) - offsetof(struct spdk_nvmf_discovery_log_page,
236 entries[0])) /
237 sizeof(struct spdk_nvmf_discovery_log_page_entry);
238 buffer_max_entries = sizeof(buffer) / sizeof(struct spdk_nvmf_discovery_log_page_entry);
239 do {
240 rc = nvme_fabric_get_discovery_log_page(ctrlr, buffer, sizeof(buffer), log_page_offset);
241 if (rc < 0) {
242 SPDK_DEBUGLOG(SPDK_LOG_NVME, "Get Log Page - Discovery error\n");
243 return rc;
244 }
245
246 if (!remaining_num_rec) {
247 log_page = (struct spdk_nvmf_discovery_log_page *)buffer;
248 recfmt = from_le16(&log_page->recfmt);
249 if (recfmt != 0) {
250 SPDK_ERRLOG("Unrecognized discovery log record format %" PRIu16 "\n", recfmt);
251 return -EPROTO;
252 }
253 remaining_num_rec = log_page->numrec;
254 log_page_offset = offsetof(struct spdk_nvmf_discovery_log_page, entries[0]);
255 log_page_entry = &log_page->entries[0];
256 numrec = spdk_min(remaining_num_rec, buffer_max_entries_first);
257 } else {
258 numrec = spdk_min(remaining_num_rec, buffer_max_entries);
259 log_page_entry = (struct spdk_nvmf_discovery_log_page_entry *)buffer;
260 }
261
262 for (i = 0; i < numrec; i++) {
263 nvme_fabric_discover_probe(log_page_entry++, cb_ctx, probe_cb);
264 }
265 remaining_num_rec -= numrec;
266 log_page_offset += numrec * sizeof(struct spdk_nvmf_discovery_log_page_entry);
267 } while (remaining_num_rec != 0);
268
269 return 0;
270 }
271
272 int
273 nvme_fabric_qpair_connect(struct spdk_nvme_qpair *qpair, uint32_t num_entries)
274 {
275 struct nvme_completion_poll_status status;
276 struct spdk_nvmf_fabric_connect_rsp *rsp;
277 struct spdk_nvmf_fabric_connect_cmd cmd;
278 struct spdk_nvmf_fabric_connect_data *nvmf_data;
279 struct spdk_nvme_ctrlr *ctrlr;
280 int rc;
281
282 if (num_entries == 0 || num_entries > SPDK_NVME_IO_QUEUE_MAX_ENTRIES) {
283 return -EINVAL;
284 }
285
286 ctrlr = qpair->ctrlr;
287 if (!ctrlr) {
288 return -EINVAL;
289 }
290
291 nvmf_data = spdk_dma_zmalloc(sizeof(*nvmf_data), 0, NULL);
292 if (!nvmf_data) {
293 SPDK_ERRLOG("nvmf_data allocation error\n");
294 return -ENOMEM;
295 }
296
297 memset(&cmd, 0, sizeof(cmd));
298 cmd.opcode = SPDK_NVME_OPC_FABRIC;
299 cmd.fctype = SPDK_NVMF_FABRIC_COMMAND_CONNECT;
300 cmd.qid = qpair->id;
301 cmd.sqsize = num_entries - 1;
302 cmd.kato = ctrlr->opts.keep_alive_timeout_ms;
303
304 if (nvme_qpair_is_admin_queue(qpair)) {
305 nvmf_data->cntlid = 0xFFFF;
306 } else {
307 nvmf_data->cntlid = ctrlr->cntlid;
308 }
309
310 SPDK_STATIC_ASSERT(sizeof(nvmf_data->hostid) == sizeof(ctrlr->opts.extended_host_id),
311 "host ID size mismatch");
312 memcpy(nvmf_data->hostid, ctrlr->opts.extended_host_id, sizeof(nvmf_data->hostid));
313 snprintf(nvmf_data->hostnqn, sizeof(nvmf_data->hostnqn), "%s", ctrlr->opts.hostnqn);
314 snprintf(nvmf_data->subnqn, sizeof(nvmf_data->subnqn), "%s", ctrlr->trid.subnqn);
315
316 rc = spdk_nvme_ctrlr_cmd_io_raw(ctrlr, qpair,
317 (struct spdk_nvme_cmd *)&cmd,
318 nvmf_data, sizeof(*nvmf_data),
319 nvme_completion_poll_cb, &status);
320 if (rc < 0) {
321 SPDK_ERRLOG("Connect command failed\n");
322 spdk_dma_free(nvmf_data);
323 return rc;
324 }
325
326 if (spdk_nvme_wait_for_completion(qpair, &status)) {
327 SPDK_ERRLOG("Connect command failed\n");
328 spdk_dma_free(nvmf_data);
329 return -EIO;
330 }
331
332 if (nvme_qpair_is_admin_queue(qpair)) {
333 rsp = (struct spdk_nvmf_fabric_connect_rsp *)&status.cpl;
334 ctrlr->cntlid = rsp->status_code_specific.success.cntlid;
335 SPDK_DEBUGLOG(SPDK_LOG_NVME, "CNTLID 0x%04" PRIx16 "\n", ctrlr->cntlid);
336 }
337
338 spdk_dma_free(nvmf_data);
339 return 0;
340 }