]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 | #include "spdk/nvmf_spec.h" | |
35 | #include "nvme_internal.h" | |
36 | ||
37 | #define SPDK_NVME_DRIVER_NAME "spdk_nvme_driver" | |
38 | ||
39 | struct nvme_driver *g_spdk_nvme_driver; | |
11fdf7f2 | 40 | pid_t g_spdk_nvme_pid; |
7c673cae FG |
41 | |
42 | int32_t spdk_nvme_retry_count; | |
43 | ||
11fdf7f2 TL |
44 | /* gross timeout of 180 seconds in milliseconds */ |
45 | static int g_nvme_driver_timeout_ms = 3 * 60 * 1000; | |
46 | ||
11fdf7f2 TL |
47 | /* Per-process attached controller list */ |
48 | static TAILQ_HEAD(, spdk_nvme_ctrlr) g_nvme_attached_ctrlrs = | |
49 | TAILQ_HEAD_INITIALIZER(g_nvme_attached_ctrlrs); | |
50 | ||
51 | /* Returns true if ctrlr should be stored on the multi-process shared_attached_ctrlrs list */ | |
52 | static bool | |
53 | nvme_ctrlr_shared(const struct spdk_nvme_ctrlr *ctrlr) | |
54 | { | |
55 | return ctrlr->trid.trtype == SPDK_NVME_TRANSPORT_PCIE; | |
56 | } | |
57 | ||
11fdf7f2 | 58 | void |
9f95a23c TL |
59 | nvme_ctrlr_connected(struct spdk_nvme_probe_ctx *probe_ctx, |
60 | struct spdk_nvme_ctrlr *ctrlr) | |
11fdf7f2 | 61 | { |
9f95a23c | 62 | TAILQ_INSERT_TAIL(&probe_ctx->init_ctrlrs, ctrlr, tailq); |
11fdf7f2 TL |
63 | } |
64 | ||
7c673cae FG |
65 | int |
66 | spdk_nvme_detach(struct spdk_nvme_ctrlr *ctrlr) | |
67 | { | |
68 | nvme_robust_mutex_lock(&g_spdk_nvme_driver->lock); | |
69 | ||
70 | nvme_ctrlr_proc_put_ref(ctrlr); | |
71 | ||
72 | if (nvme_ctrlr_get_ref_count(ctrlr) == 0) { | |
11fdf7f2 TL |
73 | if (nvme_ctrlr_shared(ctrlr)) { |
74 | TAILQ_REMOVE(&g_spdk_nvme_driver->shared_attached_ctrlrs, ctrlr, tailq); | |
75 | } else { | |
76 | TAILQ_REMOVE(&g_nvme_attached_ctrlrs, ctrlr, tailq); | |
77 | } | |
7c673cae FG |
78 | nvme_ctrlr_destruct(ctrlr); |
79 | } | |
80 | ||
81 | nvme_robust_mutex_unlock(&g_spdk_nvme_driver->lock); | |
82 | return 0; | |
83 | } | |
84 | ||
85 | void | |
86 | nvme_completion_poll_cb(void *arg, const struct spdk_nvme_cpl *cpl) | |
87 | { | |
88 | struct nvme_completion_poll_status *status = arg; | |
89 | ||
90 | /* | |
91 | * Copy status into the argument passed by the caller, so that | |
92 | * the caller can check the status to determine if the | |
93 | * the request passed or failed. | |
94 | */ | |
95 | memcpy(&status->cpl, cpl, sizeof(*cpl)); | |
96 | status->done = true; | |
97 | } | |
98 | ||
11fdf7f2 TL |
99 | /** |
100 | * Poll qpair for completions until a command completes. | |
101 | * | |
102 | * \param qpair queue to poll | |
103 | * \param status completion status | |
104 | * \param robust_mutex optional robust mutex to lock while polling qpair | |
105 | * | |
106 | * \return 0 if command completed without error, negative errno on failure | |
107 | * | |
108 | * The command to wait upon must be submitted with nvme_completion_poll_cb as the callback | |
109 | * and status as the callback argument. | |
110 | */ | |
111 | int | |
112 | spdk_nvme_wait_for_completion_robust_lock( | |
113 | struct spdk_nvme_qpair *qpair, | |
114 | struct nvme_completion_poll_status *status, | |
115 | pthread_mutex_t *robust_mutex) | |
7c673cae | 116 | { |
11fdf7f2 TL |
117 | memset(&status->cpl, 0, sizeof(status->cpl)); |
118 | status->done = false; | |
7c673cae | 119 | |
11fdf7f2 TL |
120 | while (status->done == false) { |
121 | if (robust_mutex) { | |
122 | nvme_robust_mutex_lock(robust_mutex); | |
123 | } | |
7c673cae | 124 | |
11fdf7f2 | 125 | spdk_nvme_qpair_process_completions(qpair, 0); |
7c673cae | 126 | |
11fdf7f2 TL |
127 | if (robust_mutex) { |
128 | nvme_robust_mutex_unlock(robust_mutex); | |
129 | } | |
130 | } | |
7c673cae | 131 | |
11fdf7f2 | 132 | return spdk_nvme_cpl_is_error(&status->cpl) ? -EIO : 0; |
7c673cae FG |
133 | } |
134 | ||
11fdf7f2 TL |
135 | int |
136 | spdk_nvme_wait_for_completion(struct spdk_nvme_qpair *qpair, | |
137 | struct nvme_completion_poll_status *status) | |
7c673cae | 138 | { |
11fdf7f2 | 139 | return spdk_nvme_wait_for_completion_robust_lock(qpair, status, NULL); |
7c673cae FG |
140 | } |
141 | ||
9f95a23c TL |
142 | int |
143 | spdk_nvme_wait_for_completion_timeout(struct spdk_nvme_qpair *qpair, | |
144 | struct nvme_completion_poll_status *status, | |
145 | uint64_t timeout_in_secs) | |
146 | { | |
147 | uint64_t timeout_tsc = 0; | |
148 | ||
149 | memset(&status->cpl, 0, sizeof(status->cpl)); | |
150 | status->done = false; | |
151 | if (timeout_in_secs) { | |
152 | timeout_tsc = spdk_get_ticks() + timeout_in_secs * spdk_get_ticks_hz(); | |
153 | } | |
154 | ||
155 | while (status->done == false) { | |
156 | spdk_nvme_qpair_process_completions(qpair, 0); | |
157 | if (timeout_tsc && spdk_get_ticks() > timeout_tsc) { | |
158 | break; | |
159 | } | |
160 | } | |
161 | ||
162 | if (status->done == false) { | |
163 | return -EIO; | |
164 | } | |
165 | ||
166 | return spdk_nvme_cpl_is_error(&status->cpl) ? -EIO : 0; | |
167 | } | |
168 | ||
7c673cae FG |
169 | static void |
170 | nvme_user_copy_cmd_complete(void *arg, const struct spdk_nvme_cpl *cpl) | |
171 | { | |
172 | struct nvme_request *req = arg; | |
173 | enum spdk_nvme_data_transfer xfer; | |
174 | ||
175 | if (req->user_buffer && req->payload_size) { | |
176 | /* Copy back to the user buffer and free the contig buffer */ | |
11fdf7f2 | 177 | assert(nvme_payload_type(&req->payload) == NVME_PAYLOAD_TYPE_CONTIG); |
7c673cae FG |
178 | xfer = spdk_nvme_opc_get_data_transfer(req->cmd.opc); |
179 | if (xfer == SPDK_NVME_DATA_CONTROLLER_TO_HOST || | |
180 | xfer == SPDK_NVME_DATA_BIDIRECTIONAL) { | |
181 | assert(req->pid == getpid()); | |
11fdf7f2 | 182 | memcpy(req->user_buffer, req->payload.contig_or_cb_arg, req->payload_size); |
7c673cae FG |
183 | } |
184 | ||
11fdf7f2 | 185 | spdk_dma_free(req->payload.contig_or_cb_arg); |
7c673cae FG |
186 | } |
187 | ||
188 | /* Call the user's original callback now that the buffer has been copied */ | |
189 | req->user_cb_fn(req->user_cb_arg, cpl); | |
190 | } | |
191 | ||
192 | /** | |
11fdf7f2 | 193 | * Allocate a request as well as a DMA-capable buffer to copy to/from the user's buffer. |
7c673cae FG |
194 | * |
195 | * This is intended for use in non-fast-path functions (admin commands, reservations, etc.) | |
196 | * where the overhead of a copy is not a problem. | |
197 | */ | |
198 | struct nvme_request * | |
199 | nvme_allocate_request_user_copy(struct spdk_nvme_qpair *qpair, | |
200 | void *buffer, uint32_t payload_size, spdk_nvme_cmd_cb cb_fn, | |
201 | void *cb_arg, bool host_to_controller) | |
202 | { | |
203 | struct nvme_request *req; | |
11fdf7f2 | 204 | void *dma_buffer = NULL; |
7c673cae FG |
205 | |
206 | if (buffer && payload_size) { | |
9f95a23c | 207 | dma_buffer = spdk_zmalloc(payload_size, 4096, NULL, |
11fdf7f2 TL |
208 | SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_DMA); |
209 | if (!dma_buffer) { | |
7c673cae FG |
210 | return NULL; |
211 | } | |
212 | ||
213 | if (host_to_controller) { | |
11fdf7f2 | 214 | memcpy(dma_buffer, buffer, payload_size); |
7c673cae FG |
215 | } |
216 | } | |
217 | ||
11fdf7f2 | 218 | req = nvme_allocate_request_contig(qpair, dma_buffer, payload_size, nvme_user_copy_cmd_complete, |
7c673cae FG |
219 | NULL); |
220 | if (!req) { | |
11fdf7f2 | 221 | spdk_free(dma_buffer); |
7c673cae FG |
222 | return NULL; |
223 | } | |
224 | ||
225 | req->user_cb_fn = cb_fn; | |
226 | req->user_cb_arg = cb_arg; | |
227 | req->user_buffer = buffer; | |
228 | req->cb_arg = req; | |
229 | ||
230 | return req; | |
231 | } | |
232 | ||
11fdf7f2 TL |
233 | /** |
234 | * Check if a request has exceeded the controller timeout. | |
235 | * | |
236 | * \param req request to check for timeout. | |
237 | * \param cid command ID for command submitted by req (will be passed to timeout_cb_fn) | |
238 | * \param active_proc per-process data for the controller associated with req | |
239 | * \param now_tick current time from spdk_get_ticks() | |
240 | * \return 0 if requests submitted more recently than req should still be checked for timeouts, or | |
241 | * 1 if requests newer than req need not be checked. | |
242 | * | |
243 | * The request's timeout callback will be called if needed; the caller is only responsible for | |
244 | * calling this function on each outstanding request. | |
245 | */ | |
246 | int | |
247 | nvme_request_check_timeout(struct nvme_request *req, uint16_t cid, | |
248 | struct spdk_nvme_ctrlr_process *active_proc, | |
249 | uint64_t now_tick) | |
7c673cae | 250 | { |
11fdf7f2 TL |
251 | struct spdk_nvme_qpair *qpair = req->qpair; |
252 | struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr; | |
253 | ||
254 | assert(active_proc->timeout_cb_fn != NULL); | |
7c673cae | 255 | |
11fdf7f2 TL |
256 | if (req->timed_out || req->submit_tick == 0) { |
257 | return 0; | |
258 | } | |
259 | ||
260 | if (req->pid != g_spdk_nvme_pid) { | |
261 | return 0; | |
262 | } | |
263 | ||
264 | if (nvme_qpair_is_admin_queue(qpair) && | |
265 | req->cmd.opc == SPDK_NVME_OPC_ASYNC_EVENT_REQUEST) { | |
266 | return 0; | |
267 | } | |
268 | ||
269 | if (req->submit_tick + active_proc->timeout_ticks > now_tick) { | |
270 | return 1; | |
271 | } | |
272 | ||
273 | req->timed_out = true; | |
274 | ||
275 | /* | |
276 | * We don't want to expose the admin queue to the user, | |
277 | * so when we're timing out admin commands set the | |
278 | * qpair to NULL. | |
279 | */ | |
280 | active_proc->timeout_cb_fn(active_proc->timeout_cb_arg, ctrlr, | |
281 | nvme_qpair_is_admin_queue(qpair) ? NULL : qpair, | |
282 | cid); | |
283 | return 0; | |
7c673cae FG |
284 | } |
285 | ||
286 | int | |
287 | nvme_robust_mutex_init_shared(pthread_mutex_t *mtx) | |
288 | { | |
289 | int rc = 0; | |
290 | ||
291 | #ifdef __FreeBSD__ | |
292 | pthread_mutex_init(mtx, NULL); | |
293 | #else | |
294 | pthread_mutexattr_t attr; | |
295 | ||
296 | if (pthread_mutexattr_init(&attr)) { | |
297 | return -1; | |
298 | } | |
299 | if (pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED) || | |
300 | pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST) || | |
301 | pthread_mutex_init(mtx, &attr)) { | |
302 | rc = -1; | |
303 | } | |
304 | pthread_mutexattr_destroy(&attr); | |
305 | #endif | |
306 | ||
307 | return rc; | |
308 | } | |
309 | ||
11fdf7f2 | 310 | int |
7c673cae FG |
311 | nvme_driver_init(void) |
312 | { | |
313 | int ret = 0; | |
314 | /* Any socket ID */ | |
315 | int socket_id = -1; | |
316 | ||
11fdf7f2 TL |
317 | /* Each process needs its own pid. */ |
318 | g_spdk_nvme_pid = getpid(); | |
319 | ||
7c673cae FG |
320 | /* |
321 | * Only one thread from one process will do this driver init work. | |
322 | * The primary process will reserve the shared memory and do the | |
323 | * initialization. | |
324 | * The secondary process will lookup the existing reserved memory. | |
325 | */ | |
326 | if (spdk_process_is_primary()) { | |
327 | /* The unique named memzone already reserved. */ | |
328 | if (g_spdk_nvme_driver != NULL) { | |
7c673cae FG |
329 | return 0; |
330 | } else { | |
331 | g_spdk_nvme_driver = spdk_memzone_reserve(SPDK_NVME_DRIVER_NAME, | |
11fdf7f2 TL |
332 | sizeof(struct nvme_driver), socket_id, |
333 | SPDK_MEMZONE_NO_IOVA_CONTIG); | |
7c673cae FG |
334 | } |
335 | ||
336 | if (g_spdk_nvme_driver == NULL) { | |
337 | SPDK_ERRLOG("primary process failed to reserve memory\n"); | |
338 | ||
339 | return -1; | |
340 | } | |
341 | } else { | |
342 | g_spdk_nvme_driver = spdk_memzone_lookup(SPDK_NVME_DRIVER_NAME); | |
343 | ||
344 | /* The unique named memzone already reserved by the primary process. */ | |
345 | if (g_spdk_nvme_driver != NULL) { | |
11fdf7f2 TL |
346 | int ms_waited = 0; |
347 | ||
7c673cae | 348 | /* Wait the nvme driver to get initialized. */ |
11fdf7f2 TL |
349 | while ((g_spdk_nvme_driver->initialized == false) && |
350 | (ms_waited < g_nvme_driver_timeout_ms)) { | |
351 | ms_waited++; | |
352 | nvme_delay(1000); /* delay 1ms */ | |
353 | } | |
354 | if (g_spdk_nvme_driver->initialized == false) { | |
355 | SPDK_ERRLOG("timeout waiting for primary process to init\n"); | |
356 | ||
357 | return -1; | |
7c673cae FG |
358 | } |
359 | } else { | |
360 | SPDK_ERRLOG("primary process is not started yet\n"); | |
361 | ||
362 | return -1; | |
363 | } | |
364 | ||
365 | return 0; | |
366 | } | |
367 | ||
368 | /* | |
369 | * At this moment, only one thread from the primary process will do | |
370 | * the g_spdk_nvme_driver initialization | |
371 | */ | |
372 | assert(spdk_process_is_primary()); | |
373 | ||
374 | ret = nvme_robust_mutex_init_shared(&g_spdk_nvme_driver->lock); | |
375 | if (ret != 0) { | |
376 | SPDK_ERRLOG("failed to initialize mutex\n"); | |
377 | spdk_memzone_free(SPDK_NVME_DRIVER_NAME); | |
378 | return ret; | |
379 | } | |
380 | ||
381 | nvme_robust_mutex_lock(&g_spdk_nvme_driver->lock); | |
382 | ||
383 | g_spdk_nvme_driver->initialized = false; | |
384 | ||
11fdf7f2 TL |
385 | TAILQ_INIT(&g_spdk_nvme_driver->shared_attached_ctrlrs); |
386 | ||
387 | spdk_uuid_generate(&g_spdk_nvme_driver->default_extended_host_id); | |
7c673cae FG |
388 | |
389 | nvme_robust_mutex_unlock(&g_spdk_nvme_driver->lock); | |
390 | ||
391 | return ret; | |
392 | } | |
393 | ||
9f95a23c | 394 | /* This function must only be called while holding g_spdk_nvme_driver->lock */ |
7c673cae | 395 | int |
9f95a23c TL |
396 | nvme_ctrlr_probe(const struct spdk_nvme_transport_id *trid, |
397 | struct spdk_nvme_probe_ctx *probe_ctx, void *devhandle) | |
7c673cae FG |
398 | { |
399 | struct spdk_nvme_ctrlr *ctrlr; | |
400 | struct spdk_nvme_ctrlr_opts opts; | |
401 | ||
11fdf7f2 | 402 | assert(trid != NULL); |
7c673cae | 403 | |
11fdf7f2 TL |
404 | spdk_nvme_ctrlr_get_default_ctrlr_opts(&opts, sizeof(opts)); |
405 | ||
9f95a23c TL |
406 | if (!probe_ctx->probe_cb || probe_ctx->probe_cb(probe_ctx->cb_ctx, trid, &opts)) { |
407 | ctrlr = spdk_nvme_get_ctrlr_by_trid_unsafe(trid); | |
408 | if (ctrlr) { | |
409 | /* This ctrlr already exists. | |
410 | * Increase the ref count before calling attach_cb() as the user may | |
411 | * call nvme_detach() immediately. */ | |
412 | nvme_ctrlr_proc_get_ref(ctrlr); | |
413 | ||
414 | if (probe_ctx->attach_cb) { | |
415 | nvme_robust_mutex_unlock(&g_spdk_nvme_driver->lock); | |
416 | probe_ctx->attach_cb(probe_ctx->cb_ctx, &ctrlr->trid, ctrlr, &ctrlr->opts); | |
417 | nvme_robust_mutex_lock(&g_spdk_nvme_driver->lock); | |
418 | } | |
419 | return 0; | |
420 | } | |
421 | ||
7c673cae FG |
422 | ctrlr = nvme_transport_ctrlr_construct(trid, &opts, devhandle); |
423 | if (ctrlr == NULL) { | |
11fdf7f2 | 424 | SPDK_ERRLOG("Failed to construct NVMe controller for SSD: %s\n", trid->traddr); |
7c673cae FG |
425 | return -1; |
426 | } | |
427 | ||
9f95a23c | 428 | TAILQ_INSERT_TAIL(&probe_ctx->init_ctrlrs, ctrlr, tailq); |
7c673cae FG |
429 | return 0; |
430 | } | |
431 | ||
432 | return 1; | |
433 | } | |
434 | ||
435 | static int | |
9f95a23c TL |
436 | nvme_ctrlr_poll_internal(struct spdk_nvme_ctrlr *ctrlr, |
437 | struct spdk_nvme_probe_ctx *probe_ctx) | |
7c673cae | 438 | { |
9f95a23c | 439 | int rc = 0; |
7c673cae | 440 | |
9f95a23c | 441 | rc = nvme_ctrlr_process_init(ctrlr); |
7c673cae | 442 | |
9f95a23c TL |
443 | if (rc) { |
444 | /* Controller failed to initialize. */ | |
445 | TAILQ_REMOVE(&probe_ctx->init_ctrlrs, ctrlr, tailq); | |
446 | SPDK_ERRLOG("Failed to initialize SSD: %s\n", ctrlr->trid.traddr); | |
447 | nvme_ctrlr_destruct(ctrlr); | |
448 | return rc; | |
449 | } | |
7c673cae | 450 | |
9f95a23c TL |
451 | if (ctrlr->state != NVME_CTRLR_STATE_READY) { |
452 | return 0; | |
7c673cae FG |
453 | } |
454 | ||
9f95a23c TL |
455 | /* |
456 | * Controller has been initialized. | |
457 | * Move it to the attached_ctrlrs list. | |
458 | */ | |
459 | TAILQ_REMOVE(&probe_ctx->init_ctrlrs, ctrlr, tailq); | |
460 | ||
461 | nvme_robust_mutex_lock(&g_spdk_nvme_driver->lock); | |
462 | if (nvme_ctrlr_shared(ctrlr)) { | |
463 | TAILQ_INSERT_TAIL(&g_spdk_nvme_driver->shared_attached_ctrlrs, ctrlr, tailq); | |
464 | } else { | |
465 | TAILQ_INSERT_TAIL(&g_nvme_attached_ctrlrs, ctrlr, tailq); | |
466 | } | |
7c673cae | 467 | |
9f95a23c TL |
468 | /* |
469 | * Increase the ref count before calling attach_cb() as the user may | |
470 | * call nvme_detach() immediately. | |
471 | */ | |
472 | nvme_ctrlr_proc_get_ref(ctrlr); | |
7c673cae | 473 | nvme_robust_mutex_unlock(&g_spdk_nvme_driver->lock); |
9f95a23c TL |
474 | |
475 | if (probe_ctx->attach_cb) { | |
476 | probe_ctx->attach_cb(probe_ctx->cb_ctx, &ctrlr->trid, ctrlr, &ctrlr->opts); | |
477 | return 0; | |
478 | } | |
479 | ||
480 | return 0; | |
481 | } | |
482 | ||
483 | static int | |
484 | nvme_init_controllers(struct spdk_nvme_probe_ctx *probe_ctx) | |
485 | { | |
486 | int rc = 0; | |
487 | ||
488 | while (true) { | |
489 | rc = spdk_nvme_probe_poll_async(probe_ctx); | |
490 | if (rc != -EAGAIN) { | |
491 | return rc; | |
492 | } | |
493 | } | |
494 | ||
7c673cae FG |
495 | return rc; |
496 | } | |
497 | ||
11fdf7f2 TL |
498 | /* This function must not be called while holding g_spdk_nvme_driver->lock */ |
499 | static struct spdk_nvme_ctrlr * | |
500 | spdk_nvme_get_ctrlr_by_trid(const struct spdk_nvme_transport_id *trid) | |
7c673cae | 501 | { |
7c673cae | 502 | struct spdk_nvme_ctrlr *ctrlr; |
7c673cae | 503 | |
11fdf7f2 TL |
504 | nvme_robust_mutex_lock(&g_spdk_nvme_driver->lock); |
505 | ctrlr = spdk_nvme_get_ctrlr_by_trid_unsafe(trid); | |
506 | nvme_robust_mutex_unlock(&g_spdk_nvme_driver->lock); | |
507 | ||
508 | return ctrlr; | |
509 | } | |
510 | ||
511 | /* This function must be called while holding g_spdk_nvme_driver->lock */ | |
512 | struct spdk_nvme_ctrlr * | |
513 | spdk_nvme_get_ctrlr_by_trid_unsafe(const struct spdk_nvme_transport_id *trid) | |
514 | { | |
515 | struct spdk_nvme_ctrlr *ctrlr; | |
516 | ||
517 | /* Search per-process list */ | |
518 | TAILQ_FOREACH(ctrlr, &g_nvme_attached_ctrlrs, tailq) { | |
519 | if (spdk_nvme_transport_id_compare(&ctrlr->trid, trid) == 0) { | |
520 | return ctrlr; | |
521 | } | |
7c673cae FG |
522 | } |
523 | ||
11fdf7f2 TL |
524 | /* Search multi-process shared list */ |
525 | TAILQ_FOREACH(ctrlr, &g_spdk_nvme_driver->shared_attached_ctrlrs, tailq) { | |
526 | if (spdk_nvme_transport_id_compare(&ctrlr->trid, trid) == 0) { | |
527 | return ctrlr; | |
528 | } | |
7c673cae FG |
529 | } |
530 | ||
11fdf7f2 TL |
531 | return NULL; |
532 | } | |
533 | ||
534 | /* This function must only be called while holding g_spdk_nvme_driver->lock */ | |
535 | static int | |
9f95a23c TL |
536 | spdk_nvme_probe_internal(struct spdk_nvme_probe_ctx *probe_ctx, |
537 | bool direct_connect) | |
11fdf7f2 TL |
538 | { |
539 | int rc; | |
540 | struct spdk_nvme_ctrlr *ctrlr; | |
11fdf7f2 | 541 | |
9f95a23c TL |
542 | if (!spdk_nvme_transport_available(probe_ctx->trid.trtype)) { |
543 | SPDK_ERRLOG("NVMe trtype %u not available\n", probe_ctx->trid.trtype); | |
7c673cae FG |
544 | return -1; |
545 | } | |
546 | ||
547 | nvme_robust_mutex_lock(&g_spdk_nvme_driver->lock); | |
548 | ||
9f95a23c TL |
549 | rc = nvme_transport_ctrlr_scan(probe_ctx, direct_connect); |
550 | if (rc != 0) { | |
551 | SPDK_ERRLOG("NVMe ctrlr scan failed\n"); | |
552 | nvme_robust_mutex_unlock(&g_spdk_nvme_driver->lock); | |
553 | return -1; | |
554 | } | |
11fdf7f2 TL |
555 | |
556 | /* | |
557 | * Probe controllers on the shared_attached_ctrlrs list | |
558 | */ | |
9f95a23c | 559 | if (!spdk_process_is_primary() && (probe_ctx->trid.trtype == SPDK_NVME_TRANSPORT_PCIE)) { |
11fdf7f2 TL |
560 | TAILQ_FOREACH(ctrlr, &g_spdk_nvme_driver->shared_attached_ctrlrs, tailq) { |
561 | /* Do not attach other ctrlrs if user specify a valid trid */ | |
9f95a23c TL |
562 | if ((strlen(probe_ctx->trid.traddr) != 0) && |
563 | (spdk_nvme_transport_id_compare(&probe_ctx->trid, &ctrlr->trid))) { | |
564 | continue; | |
565 | } | |
566 | ||
567 | /* Do not attach if we failed to initialize it in this process */ | |
568 | if (spdk_nvme_ctrlr_get_current_process(ctrlr) == NULL) { | |
11fdf7f2 TL |
569 | continue; |
570 | } | |
7c673cae | 571 | |
7c673cae FG |
572 | nvme_ctrlr_proc_get_ref(ctrlr); |
573 | ||
574 | /* | |
575 | * Unlock while calling attach_cb() so the user can call other functions | |
576 | * that may take the driver lock, like nvme_detach(). | |
577 | */ | |
9f95a23c | 578 | if (probe_ctx->attach_cb) { |
11fdf7f2 | 579 | nvme_robust_mutex_unlock(&g_spdk_nvme_driver->lock); |
9f95a23c | 580 | probe_ctx->attach_cb(probe_ctx->cb_ctx, &ctrlr->trid, ctrlr, &ctrlr->opts); |
11fdf7f2 TL |
581 | nvme_robust_mutex_lock(&g_spdk_nvme_driver->lock); |
582 | } | |
7c673cae | 583 | } |
7c673cae FG |
584 | } |
585 | ||
586 | nvme_robust_mutex_unlock(&g_spdk_nvme_driver->lock); | |
7c673cae | 587 | |
9f95a23c TL |
588 | return 0; |
589 | } | |
11fdf7f2 | 590 | |
9f95a23c TL |
591 | static void |
592 | spdk_nvme_probe_ctx_init(struct spdk_nvme_probe_ctx *probe_ctx, | |
593 | const struct spdk_nvme_transport_id *trid, | |
594 | void *cb_ctx, | |
595 | spdk_nvme_probe_cb probe_cb, | |
596 | spdk_nvme_attach_cb attach_cb, | |
597 | spdk_nvme_remove_cb remove_cb) | |
598 | { | |
599 | probe_ctx->trid = *trid; | |
600 | probe_ctx->cb_ctx = cb_ctx; | |
601 | probe_ctx->probe_cb = probe_cb; | |
602 | probe_ctx->attach_cb = attach_cb; | |
603 | probe_ctx->remove_cb = remove_cb; | |
604 | TAILQ_INIT(&probe_ctx->init_ctrlrs); | |
7c673cae FG |
605 | } |
606 | ||
11fdf7f2 TL |
607 | int |
608 | spdk_nvme_probe(const struct spdk_nvme_transport_id *trid, void *cb_ctx, | |
609 | spdk_nvme_probe_cb probe_cb, spdk_nvme_attach_cb attach_cb, | |
610 | spdk_nvme_remove_cb remove_cb) | |
611 | { | |
11fdf7f2 | 612 | struct spdk_nvme_transport_id trid_pcie; |
9f95a23c | 613 | struct spdk_nvme_probe_ctx *probe_ctx; |
11fdf7f2 TL |
614 | |
615 | if (trid == NULL) { | |
616 | memset(&trid_pcie, 0, sizeof(trid_pcie)); | |
617 | trid_pcie.trtype = SPDK_NVME_TRANSPORT_PCIE; | |
618 | trid = &trid_pcie; | |
619 | } | |
620 | ||
9f95a23c TL |
621 | probe_ctx = spdk_nvme_probe_async(trid, cb_ctx, probe_cb, |
622 | attach_cb, remove_cb); | |
623 | if (!probe_ctx) { | |
624 | SPDK_ERRLOG("Create probe context failed\n"); | |
625 | return -1; | |
626 | } | |
627 | ||
628 | /* | |
629 | * Keep going even if one or more nvme_attach() calls failed, | |
630 | * but maintain the value of rc to signal errors when we return. | |
631 | */ | |
632 | return nvme_init_controllers(probe_ctx); | |
11fdf7f2 TL |
633 | } |
634 | ||
635 | static bool | |
636 | spdk_nvme_connect_probe_cb(void *cb_ctx, const struct spdk_nvme_transport_id *trid, | |
637 | struct spdk_nvme_ctrlr_opts *opts) | |
638 | { | |
9f95a23c | 639 | struct spdk_nvme_ctrlr_opts *requested_opts = cb_ctx; |
11fdf7f2 | 640 | |
9f95a23c TL |
641 | assert(requested_opts); |
642 | memcpy(opts, requested_opts, sizeof(*opts)); | |
11fdf7f2 TL |
643 | |
644 | return true; | |
645 | } | |
646 | ||
647 | struct spdk_nvme_ctrlr * | |
648 | spdk_nvme_connect(const struct spdk_nvme_transport_id *trid, | |
649 | const struct spdk_nvme_ctrlr_opts *opts, size_t opts_size) | |
650 | { | |
651 | int rc; | |
11fdf7f2 | 652 | struct spdk_nvme_ctrlr *ctrlr = NULL; |
9f95a23c | 653 | struct spdk_nvme_probe_ctx *probe_ctx; |
11fdf7f2 TL |
654 | |
655 | if (trid == NULL) { | |
656 | SPDK_ERRLOG("No transport ID specified\n"); | |
657 | return NULL; | |
658 | } | |
659 | ||
9f95a23c TL |
660 | if (opts && (opts_size != sizeof(*opts))) { |
661 | SPDK_ERRLOG("Invalid opts size\n"); | |
11fdf7f2 TL |
662 | return NULL; |
663 | } | |
664 | ||
9f95a23c TL |
665 | probe_ctx = spdk_nvme_connect_async(trid, opts, NULL); |
666 | if (!probe_ctx) { | |
667 | SPDK_ERRLOG("Create probe context failed\n"); | |
668 | return NULL; | |
11fdf7f2 TL |
669 | } |
670 | ||
9f95a23c TL |
671 | rc = nvme_init_controllers(probe_ctx); |
672 | if (rc != 0) { | |
673 | return NULL; | |
674 | } | |
675 | ||
676 | ctrlr = spdk_nvme_get_ctrlr_by_trid(trid); | |
11fdf7f2 TL |
677 | |
678 | return ctrlr; | |
679 | } | |
680 | ||
7c673cae FG |
681 | int |
682 | spdk_nvme_transport_id_parse_trtype(enum spdk_nvme_transport_type *trtype, const char *str) | |
683 | { | |
684 | if (trtype == NULL || str == NULL) { | |
685 | return -EINVAL; | |
686 | } | |
687 | ||
688 | if (strcasecmp(str, "PCIe") == 0) { | |
689 | *trtype = SPDK_NVME_TRANSPORT_PCIE; | |
690 | } else if (strcasecmp(str, "RDMA") == 0) { | |
691 | *trtype = SPDK_NVME_TRANSPORT_RDMA; | |
11fdf7f2 TL |
692 | } else if (strcasecmp(str, "FC") == 0) { |
693 | *trtype = SPDK_NVME_TRANSPORT_FC; | |
9f95a23c TL |
694 | } else if (strcasecmp(str, "TCP") == 0) { |
695 | *trtype = SPDK_NVME_TRANSPORT_TCP; | |
7c673cae FG |
696 | } else { |
697 | return -ENOENT; | |
698 | } | |
699 | return 0; | |
700 | } | |
701 | ||
11fdf7f2 TL |
702 | const char * |
703 | spdk_nvme_transport_id_trtype_str(enum spdk_nvme_transport_type trtype) | |
704 | { | |
705 | switch (trtype) { | |
706 | case SPDK_NVME_TRANSPORT_PCIE: | |
707 | return "PCIe"; | |
708 | case SPDK_NVME_TRANSPORT_RDMA: | |
709 | return "RDMA"; | |
710 | case SPDK_NVME_TRANSPORT_FC: | |
711 | return "FC"; | |
9f95a23c TL |
712 | case SPDK_NVME_TRANSPORT_TCP: |
713 | return "TCP"; | |
11fdf7f2 TL |
714 | default: |
715 | return NULL; | |
716 | } | |
717 | } | |
718 | ||
7c673cae FG |
719 | int |
720 | spdk_nvme_transport_id_parse_adrfam(enum spdk_nvmf_adrfam *adrfam, const char *str) | |
721 | { | |
722 | if (adrfam == NULL || str == NULL) { | |
723 | return -EINVAL; | |
724 | } | |
725 | ||
726 | if (strcasecmp(str, "IPv4") == 0) { | |
727 | *adrfam = SPDK_NVMF_ADRFAM_IPV4; | |
728 | } else if (strcasecmp(str, "IPv6") == 0) { | |
729 | *adrfam = SPDK_NVMF_ADRFAM_IPV6; | |
730 | } else if (strcasecmp(str, "IB") == 0) { | |
731 | *adrfam = SPDK_NVMF_ADRFAM_IB; | |
732 | } else if (strcasecmp(str, "FC") == 0) { | |
733 | *adrfam = SPDK_NVMF_ADRFAM_FC; | |
734 | } else { | |
735 | return -ENOENT; | |
736 | } | |
737 | return 0; | |
738 | } | |
739 | ||
11fdf7f2 TL |
740 | const char * |
741 | spdk_nvme_transport_id_adrfam_str(enum spdk_nvmf_adrfam adrfam) | |
742 | { | |
743 | switch (adrfam) { | |
744 | case SPDK_NVMF_ADRFAM_IPV4: | |
745 | return "IPv4"; | |
746 | case SPDK_NVMF_ADRFAM_IPV6: | |
747 | return "IPv6"; | |
748 | case SPDK_NVMF_ADRFAM_IB: | |
749 | return "IB"; | |
750 | case SPDK_NVMF_ADRFAM_FC: | |
751 | return "FC"; | |
752 | default: | |
753 | return NULL; | |
754 | } | |
755 | } | |
756 | ||
9f95a23c TL |
757 | static size_t |
758 | parse_next_key(const char **str, char *key, char *val, size_t key_buf_size, size_t val_buf_size) | |
7c673cae | 759 | { |
9f95a23c | 760 | |
11fdf7f2 | 761 | const char *sep, *sep1; |
7c673cae FG |
762 | const char *whitespace = " \t\n"; |
763 | size_t key_len, val_len; | |
9f95a23c TL |
764 | |
765 | *str += strspn(*str, whitespace); | |
766 | ||
767 | sep = strchr(*str, ':'); | |
768 | if (!sep) { | |
769 | sep = strchr(*str, '='); | |
770 | if (!sep) { | |
771 | SPDK_ERRLOG("Key without ':' or '=' separator\n"); | |
772 | return 0; | |
773 | } | |
774 | } else { | |
775 | sep1 = strchr(*str, '='); | |
776 | if ((sep1 != NULL) && (sep1 < sep)) { | |
777 | sep = sep1; | |
778 | } | |
779 | } | |
780 | ||
781 | key_len = sep - *str; | |
782 | if (key_len >= key_buf_size) { | |
783 | SPDK_ERRLOG("Key length %zu greater than maximum allowed %zu\n", | |
784 | key_len, key_buf_size - 1); | |
785 | return 0; | |
786 | } | |
787 | ||
788 | memcpy(key, *str, key_len); | |
789 | key[key_len] = '\0'; | |
790 | ||
791 | *str += key_len + 1; /* Skip key: */ | |
792 | val_len = strcspn(*str, whitespace); | |
793 | if (val_len == 0) { | |
794 | SPDK_ERRLOG("Key without value\n"); | |
795 | return 0; | |
796 | } | |
797 | ||
798 | if (val_len >= val_buf_size) { | |
799 | SPDK_ERRLOG("Value length %zu greater than maximum allowed %zu\n", | |
800 | val_len, val_buf_size - 1); | |
801 | return 0; | |
802 | } | |
803 | ||
804 | memcpy(val, *str, val_len); | |
805 | val[val_len] = '\0'; | |
806 | ||
807 | *str += val_len; | |
808 | ||
809 | return val_len; | |
810 | } | |
811 | ||
812 | int | |
813 | spdk_nvme_transport_id_parse(struct spdk_nvme_transport_id *trid, const char *str) | |
814 | { | |
815 | size_t val_len; | |
7c673cae FG |
816 | char key[32]; |
817 | char val[1024]; | |
818 | ||
819 | if (trid == NULL || str == NULL) { | |
820 | return -EINVAL; | |
821 | } | |
822 | ||
823 | while (*str != '\0') { | |
7c673cae | 824 | |
9f95a23c | 825 | val_len = parse_next_key(&str, key, val, sizeof(key), sizeof(val)); |
7c673cae | 826 | |
7c673cae | 827 | if (val_len == 0) { |
9f95a23c | 828 | SPDK_ERRLOG("Failed to parse transport ID\n"); |
7c673cae FG |
829 | return -EINVAL; |
830 | } | |
831 | ||
7c673cae FG |
832 | if (strcasecmp(key, "trtype") == 0) { |
833 | if (spdk_nvme_transport_id_parse_trtype(&trid->trtype, val) != 0) { | |
834 | SPDK_ERRLOG("Unknown trtype '%s'\n", val); | |
835 | return -EINVAL; | |
836 | } | |
837 | } else if (strcasecmp(key, "adrfam") == 0) { | |
838 | if (spdk_nvme_transport_id_parse_adrfam(&trid->adrfam, val) != 0) { | |
839 | SPDK_ERRLOG("Unknown adrfam '%s'\n", val); | |
840 | return -EINVAL; | |
841 | } | |
842 | } else if (strcasecmp(key, "traddr") == 0) { | |
843 | if (val_len > SPDK_NVMF_TRADDR_MAX_LEN) { | |
844 | SPDK_ERRLOG("traddr length %zu greater than maximum allowed %u\n", | |
845 | val_len, SPDK_NVMF_TRADDR_MAX_LEN); | |
846 | return -EINVAL; | |
847 | } | |
848 | memcpy(trid->traddr, val, val_len + 1); | |
849 | } else if (strcasecmp(key, "trsvcid") == 0) { | |
850 | if (val_len > SPDK_NVMF_TRSVCID_MAX_LEN) { | |
851 | SPDK_ERRLOG("trsvcid length %zu greater than maximum allowed %u\n", | |
852 | val_len, SPDK_NVMF_TRSVCID_MAX_LEN); | |
853 | return -EINVAL; | |
854 | } | |
855 | memcpy(trid->trsvcid, val, val_len + 1); | |
856 | } else if (strcasecmp(key, "subnqn") == 0) { | |
857 | if (val_len > SPDK_NVMF_NQN_MAX_LEN) { | |
858 | SPDK_ERRLOG("subnqn length %zu greater than maximum allowed %u\n", | |
859 | val_len, SPDK_NVMF_NQN_MAX_LEN); | |
860 | return -EINVAL; | |
861 | } | |
862 | memcpy(trid->subnqn, val, val_len + 1); | |
9f95a23c TL |
863 | } else if (strcasecmp(key, "hostaddr") == 0) { |
864 | continue; | |
865 | } else if (strcasecmp(key, "hostsvcid") == 0) { | |
866 | continue; | |
867 | } else if (strcasecmp(key, "ns") == 0) { | |
868 | /* | |
869 | * Special case. The namespace id parameter may | |
870 | * optionally be passed in the transport id string | |
871 | * for an SPDK application (e.g. nvme/perf) | |
872 | * and additionally parsed therein to limit | |
873 | * targeting a specific namespace. For this | |
874 | * scenario, just silently ignore this key | |
875 | * rather than letting it default to logging | |
876 | * it as an invalid key. | |
877 | */ | |
878 | continue; | |
879 | } else { | |
880 | SPDK_ERRLOG("Unknown transport ID key '%s'\n", key); | |
881 | } | |
882 | } | |
883 | ||
884 | return 0; | |
885 | } | |
886 | ||
887 | int | |
888 | spdk_nvme_host_id_parse(struct spdk_nvme_host_id *hostid, const char *str) | |
889 | { | |
890 | ||
891 | size_t key_size = 32; | |
892 | size_t val_size = 1024; | |
893 | size_t val_len; | |
894 | char key[key_size]; | |
895 | char val[val_size]; | |
896 | ||
897 | if (hostid == NULL || str == NULL) { | |
898 | return -EINVAL; | |
899 | } | |
900 | ||
901 | while (*str != '\0') { | |
902 | ||
903 | val_len = parse_next_key(&str, key, val, key_size, val_size); | |
904 | ||
905 | if (val_len == 0) { | |
906 | SPDK_ERRLOG("Failed to parse host ID\n"); | |
907 | return val_len; | |
908 | } | |
909 | ||
910 | /* Ignore the rest of the options from the transport ID. */ | |
911 | if (strcasecmp(key, "trtype") == 0) { | |
912 | continue; | |
913 | } else if (strcasecmp(key, "adrfam") == 0) { | |
914 | continue; | |
915 | } else if (strcasecmp(key, "traddr") == 0) { | |
916 | continue; | |
917 | } else if (strcasecmp(key, "trsvcid") == 0) { | |
918 | continue; | |
919 | } else if (strcasecmp(key, "subnqn") == 0) { | |
920 | continue; | |
921 | } else if (strcasecmp(key, "ns") == 0) { | |
922 | continue; | |
923 | } else if (strcasecmp(key, "hostaddr") == 0) { | |
924 | if (val_len > SPDK_NVMF_TRADDR_MAX_LEN) { | |
925 | SPDK_ERRLOG("hostaddr length %zu greater than maximum allowed %u\n", | |
926 | val_len, SPDK_NVMF_TRADDR_MAX_LEN); | |
927 | return -EINVAL; | |
928 | } | |
929 | memcpy(hostid->hostaddr, val, val_len + 1); | |
930 | ||
931 | } else if (strcasecmp(key, "hostsvcid") == 0) { | |
932 | if (val_len > SPDK_NVMF_TRSVCID_MAX_LEN) { | |
933 | SPDK_ERRLOG("trsvcid length %zu greater than maximum allowed %u\n", | |
934 | val_len, SPDK_NVMF_TRSVCID_MAX_LEN); | |
935 | return -EINVAL; | |
936 | } | |
937 | memcpy(hostid->hostsvcid, val, val_len + 1); | |
7c673cae FG |
938 | } else { |
939 | SPDK_ERRLOG("Unknown transport ID key '%s'\n", key); | |
940 | } | |
941 | } | |
942 | ||
943 | return 0; | |
944 | } | |
945 | ||
946 | static int | |
947 | cmp_int(int a, int b) | |
948 | { | |
949 | return a - b; | |
950 | } | |
951 | ||
952 | int | |
953 | spdk_nvme_transport_id_compare(const struct spdk_nvme_transport_id *trid1, | |
954 | const struct spdk_nvme_transport_id *trid2) | |
955 | { | |
956 | int cmp; | |
957 | ||
958 | cmp = cmp_int(trid1->trtype, trid2->trtype); | |
959 | if (cmp) { | |
960 | return cmp; | |
961 | } | |
962 | ||
11fdf7f2 TL |
963 | if (trid1->trtype == SPDK_NVME_TRANSPORT_PCIE) { |
964 | struct spdk_pci_addr pci_addr1; | |
965 | struct spdk_pci_addr pci_addr2; | |
966 | ||
967 | /* Normalize PCI addresses before comparing */ | |
968 | if (spdk_pci_addr_parse(&pci_addr1, trid1->traddr) < 0 || | |
969 | spdk_pci_addr_parse(&pci_addr2, trid2->traddr) < 0) { | |
970 | return -1; | |
971 | } | |
972 | ||
973 | /* PCIe transport ID only uses trtype and traddr */ | |
974 | return spdk_pci_addr_compare(&pci_addr1, &pci_addr2); | |
975 | } | |
976 | ||
977 | cmp = strcasecmp(trid1->traddr, trid2->traddr); | |
7c673cae FG |
978 | if (cmp) { |
979 | return cmp; | |
980 | } | |
981 | ||
11fdf7f2 | 982 | cmp = cmp_int(trid1->adrfam, trid2->adrfam); |
7c673cae FG |
983 | if (cmp) { |
984 | return cmp; | |
985 | } | |
986 | ||
987 | cmp = strcasecmp(trid1->trsvcid, trid2->trsvcid); | |
988 | if (cmp) { | |
989 | return cmp; | |
990 | } | |
991 | ||
11fdf7f2 | 992 | cmp = strcmp(trid1->subnqn, trid2->subnqn); |
7c673cae FG |
993 | if (cmp) { |
994 | return cmp; | |
995 | } | |
996 | ||
997 | return 0; | |
998 | } | |
999 | ||
9f95a23c TL |
1000 | int |
1001 | spdk_nvme_prchk_flags_parse(uint32_t *prchk_flags, const char *str) | |
1002 | { | |
1003 | size_t val_len; | |
1004 | char key[32]; | |
1005 | char val[1024]; | |
1006 | ||
1007 | if (prchk_flags == NULL || str == NULL) { | |
1008 | return -EINVAL; | |
1009 | } | |
1010 | ||
1011 | while (*str != '\0') { | |
1012 | val_len = parse_next_key(&str, key, val, sizeof(key), sizeof(val)); | |
1013 | ||
1014 | if (val_len == 0) { | |
1015 | SPDK_ERRLOG("Failed to parse prchk\n"); | |
1016 | return -EINVAL; | |
1017 | } | |
1018 | ||
1019 | if (strcasecmp(key, "prchk") == 0) { | |
1020 | if (strcasestr(val, "reftag") != NULL) { | |
1021 | *prchk_flags |= SPDK_NVME_IO_FLAGS_PRCHK_REFTAG; | |
1022 | } | |
1023 | if (strcasestr(val, "guard") != NULL) { | |
1024 | *prchk_flags |= SPDK_NVME_IO_FLAGS_PRCHK_GUARD; | |
1025 | } | |
1026 | } else { | |
1027 | SPDK_ERRLOG("Unknown key '%s'\n", key); | |
1028 | return -EINVAL; | |
1029 | } | |
1030 | } | |
1031 | ||
1032 | return 0; | |
1033 | } | |
1034 | ||
1035 | const char * | |
1036 | spdk_nvme_prchk_flags_str(uint32_t prchk_flags) | |
1037 | { | |
1038 | if (prchk_flags & SPDK_NVME_IO_FLAGS_PRCHK_REFTAG) { | |
1039 | if (prchk_flags & SPDK_NVME_IO_FLAGS_PRCHK_GUARD) { | |
1040 | return "prchk:reftag|guard"; | |
1041 | } else { | |
1042 | return "prchk:reftag"; | |
1043 | } | |
1044 | } else { | |
1045 | if (prchk_flags & SPDK_NVME_IO_FLAGS_PRCHK_GUARD) { | |
1046 | return "prchk:guard"; | |
1047 | } else { | |
1048 | return NULL; | |
1049 | } | |
1050 | } | |
1051 | } | |
1052 | ||
1053 | struct spdk_nvme_probe_ctx * | |
1054 | spdk_nvme_probe_async(const struct spdk_nvme_transport_id *trid, | |
1055 | void *cb_ctx, | |
1056 | spdk_nvme_probe_cb probe_cb, | |
1057 | spdk_nvme_attach_cb attach_cb, | |
1058 | spdk_nvme_remove_cb remove_cb) | |
1059 | { | |
1060 | int rc; | |
1061 | struct spdk_nvme_probe_ctx *probe_ctx; | |
1062 | ||
1063 | rc = nvme_driver_init(); | |
1064 | if (rc != 0) { | |
1065 | return NULL; | |
1066 | } | |
1067 | ||
1068 | probe_ctx = calloc(1, sizeof(*probe_ctx)); | |
1069 | if (!probe_ctx) { | |
1070 | return NULL; | |
1071 | } | |
1072 | ||
1073 | spdk_nvme_probe_ctx_init(probe_ctx, trid, cb_ctx, probe_cb, attach_cb, remove_cb); | |
1074 | rc = spdk_nvme_probe_internal(probe_ctx, false); | |
1075 | if (rc != 0) { | |
1076 | free(probe_ctx); | |
1077 | return NULL; | |
1078 | } | |
1079 | ||
1080 | return probe_ctx; | |
1081 | } | |
1082 | ||
1083 | int | |
1084 | spdk_nvme_probe_poll_async(struct spdk_nvme_probe_ctx *probe_ctx) | |
1085 | { | |
1086 | int rc = 0; | |
1087 | struct spdk_nvme_ctrlr *ctrlr, *ctrlr_tmp; | |
1088 | ||
1089 | if (!spdk_process_is_primary() && probe_ctx->trid.trtype == SPDK_NVME_TRANSPORT_PCIE) { | |
1090 | free(probe_ctx); | |
1091 | return 0; | |
1092 | } | |
1093 | ||
1094 | TAILQ_FOREACH_SAFE(ctrlr, &probe_ctx->init_ctrlrs, tailq, ctrlr_tmp) { | |
1095 | rc = nvme_ctrlr_poll_internal(ctrlr, probe_ctx); | |
1096 | if (rc != 0) { | |
1097 | rc = -EIO; | |
1098 | break; | |
1099 | } | |
1100 | } | |
1101 | ||
1102 | if (rc != 0 || TAILQ_EMPTY(&probe_ctx->init_ctrlrs)) { | |
1103 | nvme_robust_mutex_lock(&g_spdk_nvme_driver->lock); | |
1104 | g_spdk_nvme_driver->initialized = true; | |
1105 | nvme_robust_mutex_unlock(&g_spdk_nvme_driver->lock); | |
1106 | free(probe_ctx); | |
1107 | return rc; | |
1108 | } | |
1109 | ||
1110 | return -EAGAIN; | |
1111 | } | |
1112 | ||
1113 | struct spdk_nvme_probe_ctx * | |
1114 | spdk_nvme_connect_async(const struct spdk_nvme_transport_id *trid, | |
1115 | const struct spdk_nvme_ctrlr_opts *opts, | |
1116 | spdk_nvme_attach_cb attach_cb) | |
1117 | { | |
1118 | int rc; | |
1119 | spdk_nvme_probe_cb probe_cb = NULL; | |
1120 | struct spdk_nvme_probe_ctx *probe_ctx; | |
1121 | ||
1122 | rc = nvme_driver_init(); | |
1123 | if (rc != 0) { | |
1124 | return NULL; | |
1125 | } | |
1126 | ||
1127 | probe_ctx = calloc(1, sizeof(*probe_ctx)); | |
1128 | if (!probe_ctx) { | |
1129 | return NULL; | |
1130 | } | |
1131 | ||
1132 | if (opts) { | |
1133 | probe_cb = spdk_nvme_connect_probe_cb; | |
1134 | } | |
1135 | ||
1136 | spdk_nvme_probe_ctx_init(probe_ctx, trid, (void *)opts, probe_cb, attach_cb, NULL); | |
1137 | rc = spdk_nvme_probe_internal(probe_ctx, true); | |
1138 | if (rc != 0) { | |
1139 | free(probe_ctx); | |
1140 | return NULL; | |
1141 | } | |
1142 | ||
1143 | return probe_ctx; | |
1144 | } | |
1145 | ||
11fdf7f2 | 1146 | SPDK_LOG_REGISTER_COMPONENT("nvme", SPDK_LOG_NVME) |