Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2016 Intel Corporation. All rights reserved.
3 : : * Copyright (c) 2018-2019, 2021 Mellanox Technologies LTD. All rights reserved.
4 : : * Copyright (c) 2023 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
5 : : */
6 : :
7 : : #include "spdk/stdinc.h"
8 : :
9 : : #include "nvmf_internal.h"
10 : : #include "transport.h"
11 : :
12 : : #include "spdk/config.h"
13 : : #include "spdk/log.h"
14 : : #include "spdk/nvmf.h"
15 : : #include "spdk/nvmf_transport.h"
16 : : #include "spdk/queue.h"
17 : : #include "spdk/util.h"
18 : : #include "spdk_internal/usdt.h"
19 : :
20 : : #define NVMF_TRANSPORT_DEFAULT_ASSOCIATION_TIMEOUT_IN_MS 120000
21 : :
22 : : struct nvmf_transport_ops_list_element {
23 : : struct spdk_nvmf_transport_ops ops;
24 : : TAILQ_ENTRY(nvmf_transport_ops_list_element) link;
25 : : };
26 : :
27 : : TAILQ_HEAD(nvmf_transport_ops_list, nvmf_transport_ops_list_element)
28 : : g_spdk_nvmf_transport_ops = TAILQ_HEAD_INITIALIZER(g_spdk_nvmf_transport_ops);
29 : :
30 : : static inline const struct spdk_nvmf_transport_ops *
31 : 2399 : nvmf_get_transport_ops(const char *transport_name)
32 : : {
33 : : struct nvmf_transport_ops_list_element *ops;
34 [ + + ]: 3671 : TAILQ_FOREACH(ops, &g_spdk_nvmf_transport_ops, link) {
35 [ + + - + : 1823 : if (strcasecmp(transport_name, ops->ops.name) == 0) {
+ + ]
36 : 551 : return &ops->ops;
37 : : }
38 : : }
39 : 1848 : return NULL;
40 : : }
41 : :
42 : : void
43 : 1842 : spdk_nvmf_transport_register(const struct spdk_nvmf_transport_ops *ops)
44 : : {
45 : : struct nvmf_transport_ops_list_element *new_ops;
46 : :
47 [ - + ]: 1842 : if (nvmf_get_transport_ops(ops->name) != NULL) {
48 : 0 : SPDK_ERRLOG("Double registering nvmf transport type %s.\n", ops->name);
49 : 0 : assert(false);
50 : : return;
51 : : }
52 : :
53 : 1842 : new_ops = calloc(1, sizeof(*new_ops));
54 [ - + ]: 1842 : if (new_ops == NULL) {
55 : 0 : SPDK_ERRLOG("Unable to allocate memory to register new transport type %s.\n", ops->name);
56 : 0 : assert(false);
57 : : return;
58 : : }
59 : :
60 : 1842 : new_ops->ops = *ops;
61 : :
62 : 1842 : TAILQ_INSERT_TAIL(&g_spdk_nvmf_transport_ops, new_ops, link);
63 : : }
64 : :
65 : : const struct spdk_nvmf_transport_opts *
66 : 166 : spdk_nvmf_get_transport_opts(struct spdk_nvmf_transport *transport)
67 : : {
68 : 166 : return &transport->opts;
69 : : }
70 : :
71 : : void
72 : 166 : nvmf_transport_dump_opts(struct spdk_nvmf_transport *transport, struct spdk_json_write_ctx *w,
73 : : bool named)
74 : : {
75 : 166 : const struct spdk_nvmf_transport_opts *opts = spdk_nvmf_get_transport_opts(transport);
76 : :
77 [ + + ]: 166 : named ? spdk_json_write_named_object_begin(w, "params") : spdk_json_write_object_begin(w);
78 : :
79 : 166 : spdk_json_write_named_string(w, "trtype", spdk_nvmf_get_transport_name(transport));
80 : 166 : spdk_json_write_named_uint32(w, "max_queue_depth", opts->max_queue_depth);
81 : 166 : spdk_json_write_named_uint32(w, "max_io_qpairs_per_ctrlr", opts->max_qpairs_per_ctrlr - 1);
82 : 166 : spdk_json_write_named_uint32(w, "in_capsule_data_size", opts->in_capsule_data_size);
83 : 166 : spdk_json_write_named_uint32(w, "max_io_size", opts->max_io_size);
84 : 166 : spdk_json_write_named_uint32(w, "io_unit_size", opts->io_unit_size);
85 : 166 : spdk_json_write_named_uint32(w, "max_aq_depth", opts->max_aq_depth);
86 : 166 : spdk_json_write_named_uint32(w, "num_shared_buffers", opts->num_shared_buffers);
87 : 166 : spdk_json_write_named_uint32(w, "buf_cache_size", opts->buf_cache_size);
88 [ - + ]: 166 : spdk_json_write_named_bool(w, "dif_insert_or_strip", opts->dif_insert_or_strip);
89 [ - + ]: 166 : spdk_json_write_named_bool(w, "zcopy", opts->zcopy);
90 : :
91 [ + + ]: 166 : if (transport->ops->dump_opts) {
92 : 165 : transport->ops->dump_opts(transport, w);
93 : : }
94 : :
95 : 166 : spdk_json_write_named_uint32(w, "abort_timeout_sec", opts->abort_timeout_sec);
96 : 166 : spdk_json_write_named_uint32(w, "ack_timeout", opts->ack_timeout);
97 : 166 : spdk_json_write_named_uint32(w, "data_wr_pool_size", opts->data_wr_pool_size);
98 : 166 : spdk_json_write_object_end(w);
99 : 166 : }
100 : :
101 : : void
102 : 1539 : nvmf_transport_listen_dump_trid(const struct spdk_nvme_transport_id *trid,
103 : : struct spdk_json_write_ctx *w)
104 : : {
105 : 1539 : const char *adrfam = spdk_nvme_transport_id_adrfam_str(trid->adrfam);
106 : :
107 : 1539 : spdk_json_write_named_string(w, "trtype", trid->trstring);
108 [ + - ]: 1539 : spdk_json_write_named_string(w, "adrfam", adrfam ? adrfam : "unknown");
109 : 1539 : spdk_json_write_named_string(w, "traddr", trid->traddr);
110 : 1539 : spdk_json_write_named_string(w, "trsvcid", trid->trsvcid);
111 : 1539 : }
112 : :
113 : : spdk_nvme_transport_type_t
114 : 0 : spdk_nvmf_get_transport_type(struct spdk_nvmf_transport *transport)
115 : : {
116 : 0 : return transport->ops->type;
117 : : }
118 : :
119 : : const char *
120 : 198 : spdk_nvmf_get_transport_name(struct spdk_nvmf_transport *transport)
121 : : {
122 : 198 : return transport->ops->name;
123 : : }
124 : :
125 : : static void
126 : 536 : nvmf_transport_opts_copy(struct spdk_nvmf_transport_opts *opts,
127 : : struct spdk_nvmf_transport_opts *opts_src,
128 : : size_t opts_size)
129 : : {
130 [ - + ]: 536 : assert(opts);
131 [ - + ]: 536 : assert(opts_src);
132 : :
133 : 536 : opts->opts_size = opts_size;
134 : :
135 : : #define SET_FIELD(field) \
136 : : if (offsetof(struct spdk_nvmf_transport_opts, field) + sizeof(opts->field) <= opts_size) { \
137 : : opts->field = opts_src->field; \
138 : : } \
139 : :
140 [ + - ]: 536 : SET_FIELD(max_queue_depth);
141 [ + - ]: 536 : SET_FIELD(max_qpairs_per_ctrlr);
142 [ + - ]: 536 : SET_FIELD(in_capsule_data_size);
143 [ + - ]: 536 : SET_FIELD(max_io_size);
144 [ + - ]: 536 : SET_FIELD(io_unit_size);
145 [ + - ]: 536 : SET_FIELD(max_aq_depth);
146 [ + - ]: 536 : SET_FIELD(buf_cache_size);
147 [ + - ]: 536 : SET_FIELD(num_shared_buffers);
148 [ + - - + ]: 536 : SET_FIELD(dif_insert_or_strip);
149 [ + - ]: 536 : SET_FIELD(abort_timeout_sec);
150 [ + - ]: 536 : SET_FIELD(association_timeout);
151 [ + - ]: 536 : SET_FIELD(transport_specific);
152 [ + - ]: 536 : SET_FIELD(acceptor_poll_rate);
153 [ + - - + ]: 536 : SET_FIELD(zcopy);
154 [ + - ]: 536 : SET_FIELD(ack_timeout);
155 [ + - ]: 536 : SET_FIELD(data_wr_pool_size);
156 : :
157 : : /* Do not remove this statement, you should always update this statement when you adding a new field,
158 : : * and do not forget to add the SET_FIELD statement for your added field. */
159 : : SPDK_STATIC_ASSERT(sizeof(struct spdk_nvmf_transport_opts) == 72, "Incorrect size");
160 : :
161 : : #undef SET_FIELD
162 : : #undef FILED_CHECK
163 : 536 : }
164 : :
165 : : struct nvmf_transport_create_ctx {
166 : : const struct spdk_nvmf_transport_ops *ops;
167 : : struct spdk_nvmf_transport_opts opts;
168 : : void *cb_arg;
169 : : spdk_nvmf_transport_create_done_cb cb_fn;
170 : : };
171 : :
172 : : static bool
173 : 10876674 : nvmf_transport_use_iobuf(struct spdk_nvmf_transport *transport)
174 : : {
175 [ + + + + ]: 10876674 : return transport->opts.num_shared_buffers || transport->opts.buf_cache_size;
176 : : }
177 : :
178 : : static void
179 : 268 : nvmf_transport_create_async_done(void *cb_arg, struct spdk_nvmf_transport *transport)
180 : : {
181 : 268 : struct nvmf_transport_create_ctx *ctx = cb_arg;
182 : : int chars_written;
183 : :
184 [ - + ]: 268 : if (!transport) {
185 : 0 : SPDK_ERRLOG("Failed to create transport.\n");
186 : 0 : goto err;
187 : : }
188 : :
189 [ - + ]: 268 : pthread_mutex_init(&transport->mutex, NULL);
190 : 268 : TAILQ_INIT(&transport->listeners);
191 : 268 : transport->ops = ctx->ops;
192 : 268 : transport->opts = ctx->opts;
193 [ - + ]: 268 : chars_written = snprintf(transport->iobuf_name, MAX_MEMPOOL_NAME_LENGTH, "%s_%s", "nvmf",
194 : 268 : transport->ops->name);
195 [ - + ]: 268 : if (chars_written < 0) {
196 : 0 : SPDK_ERRLOG("Unable to generate transport data buffer pool name.\n");
197 : 0 : goto err;
198 : : }
199 : :
200 [ + + ]: 268 : if (nvmf_transport_use_iobuf(transport)) {
201 : 258 : spdk_iobuf_register_module(transport->iobuf_name);
202 : : }
203 : :
204 : 268 : ctx->cb_fn(ctx->cb_arg, transport);
205 : 268 : free(ctx);
206 : 268 : return;
207 : :
208 : 0 : err:
209 [ # # ]: 0 : if (transport) {
210 : 0 : transport->ops->destroy(transport, NULL, NULL);
211 : : }
212 : :
213 : 0 : ctx->cb_fn(ctx->cb_arg, NULL);
214 : 0 : free(ctx);
215 : : }
216 : :
217 : : static void
218 : 259 : _nvmf_transport_create_done(void *ctx)
219 : : {
220 : 259 : struct nvmf_transport_create_ctx *_ctx = (struct nvmf_transport_create_ctx *)ctx;
221 : :
222 : 259 : nvmf_transport_create_async_done(_ctx, _ctx->ops->create(&_ctx->opts));
223 : 259 : }
224 : :
225 : : static int
226 : 280 : nvmf_transport_create(const char *transport_name, struct spdk_nvmf_transport_opts *opts,
227 : : spdk_nvmf_transport_create_done_cb cb_fn, void *cb_arg, bool sync)
228 : : {
229 : : struct nvmf_transport_create_ctx *ctx;
230 : 280 : struct spdk_iobuf_opts opts_iobuf = {};
231 : : int rc;
232 : : uint64_t count;
233 : :
234 : 280 : ctx = calloc(1, sizeof(*ctx));
235 [ - + ]: 280 : if (!ctx) {
236 : 0 : return -ENOMEM;
237 : : }
238 : :
239 [ - + ]: 280 : if (!opts) {
240 : 0 : SPDK_ERRLOG("opts should not be NULL\n");
241 : 0 : goto err;
242 : : }
243 : :
244 [ - + ]: 280 : if (!opts->opts_size) {
245 : 0 : SPDK_ERRLOG("The opts_size in opts structure should not be zero\n");
246 : 0 : goto err;
247 : : }
248 : :
249 : 280 : ctx->ops = nvmf_get_transport_ops(transport_name);
250 [ + + ]: 280 : if (!ctx->ops) {
251 : 3 : SPDK_ERRLOG("Transport type '%s' unavailable.\n", transport_name);
252 : 3 : goto err;
253 : : }
254 : :
255 : 277 : nvmf_transport_opts_copy(&ctx->opts, opts, opts->opts_size);
256 [ + + + - ]: 277 : if (ctx->opts.max_io_size != 0 && (!spdk_u32_is_pow2(ctx->opts.max_io_size) ||
257 [ + + ]: 274 : ctx->opts.max_io_size < 8192)) {
258 : 3 : SPDK_ERRLOG("max_io_size %u must be a power of 2 and be greater than or equal 8KB\n",
259 : : ctx->opts.max_io_size);
260 : 3 : goto err;
261 : : }
262 : :
263 [ + + ]: 274 : if (ctx->opts.max_aq_depth < SPDK_NVMF_MIN_ADMIN_MAX_SQ_SIZE) {
264 : 3 : SPDK_ERRLOG("max_aq_depth %u is less than minimum defined by NVMf spec, use min value\n",
265 : : ctx->opts.max_aq_depth);
266 : 3 : ctx->opts.max_aq_depth = SPDK_NVMF_MIN_ADMIN_MAX_SQ_SIZE;
267 : : }
268 : :
269 : 274 : spdk_iobuf_get_opts(&opts_iobuf, sizeof(opts_iobuf));
270 [ + + ]: 274 : if (ctx->opts.io_unit_size == 0) {
271 : 3 : SPDK_ERRLOG("io_unit_size cannot be 0\n");
272 : 3 : goto err;
273 : : }
274 [ + + ]: 271 : if (ctx->opts.io_unit_size > opts_iobuf.large_bufsize) {
275 : 3 : SPDK_ERRLOG("io_unit_size %u is larger than iobuf pool large buffer size %d\n",
276 : : ctx->opts.io_unit_size, opts_iobuf.large_bufsize);
277 : 3 : goto err;
278 : : }
279 : :
280 [ + + ]: 268 : if (ctx->opts.io_unit_size <= opts_iobuf.small_bufsize) {
281 : : /* We'll be using the small buffer pool only */
282 : 144 : count = opts_iobuf.small_pool_count;
283 : : } else {
284 : 124 : count = spdk_min(opts_iobuf.small_pool_count, opts_iobuf.large_pool_count);
285 : : }
286 : :
287 [ - + ]: 268 : if (ctx->opts.num_shared_buffers > count) {
288 : 0 : SPDK_WARNLOG("The num_shared_buffers value (%u) is larger than the available iobuf"
289 : : " pool size (%lu). Please increase the iobuf pool sizes.\n",
290 : : ctx->opts.num_shared_buffers, count);
291 : : }
292 : :
293 : 268 : ctx->cb_fn = cb_fn;
294 : 268 : ctx->cb_arg = cb_arg;
295 : :
296 : : /* Prioritize sync create operation. */
297 [ + + ]: 268 : if (ctx->ops->create) {
298 [ + + ]: 259 : if (sync) {
299 : 3 : _nvmf_transport_create_done(ctx);
300 : 3 : return 0;
301 : : }
302 : :
303 : 256 : rc = spdk_thread_send_msg(spdk_get_thread(), _nvmf_transport_create_done, ctx);
304 [ - + ]: 256 : if (rc) {
305 : 0 : goto err;
306 : : }
307 : :
308 : 256 : return 0;
309 : : }
310 : :
311 [ - + ]: 9 : assert(ctx->ops->create_async);
312 : 9 : rc = ctx->ops->create_async(&ctx->opts, nvmf_transport_create_async_done, ctx);
313 [ - + ]: 9 : if (rc) {
314 : 0 : SPDK_ERRLOG("Unable to create new transport of type %s\n", transport_name);
315 : 0 : goto err;
316 : : }
317 : :
318 : 9 : return 0;
319 : 12 : err:
320 : 12 : free(ctx);
321 : 12 : return -1;
322 : : }
323 : :
324 : : int
325 : 277 : spdk_nvmf_transport_create_async(const char *transport_name, struct spdk_nvmf_transport_opts *opts,
326 : : spdk_nvmf_transport_create_done_cb cb_fn, void *cb_arg)
327 : : {
328 : 277 : return nvmf_transport_create(transport_name, opts, cb_fn, cb_arg, false);
329 : : }
330 : :
331 : : static void
332 : 3 : nvmf_transport_create_sync_done(void *cb_arg, struct spdk_nvmf_transport *transport)
333 : : {
334 : 3 : struct spdk_nvmf_transport **_transport = cb_arg;
335 : :
336 : 3 : *_transport = transport;
337 : 3 : }
338 : :
339 : : struct spdk_nvmf_transport *
340 : 3 : spdk_nvmf_transport_create(const char *transport_name, struct spdk_nvmf_transport_opts *opts)
341 : : {
342 : 3 : struct spdk_nvmf_transport *transport = NULL;
343 : :
344 : : /* Current implementation supports synchronous version of create operation only. */
345 [ + - + - ]: 3 : assert(nvmf_get_transport_ops(transport_name) && nvmf_get_transport_ops(transport_name)->create);
346 : :
347 : 3 : nvmf_transport_create(transport_name, opts, nvmf_transport_create_sync_done, &transport, true);
348 : 3 : return transport;
349 : : }
350 : :
351 : : struct spdk_nvmf_transport *
352 : 6377 : spdk_nvmf_transport_get_first(struct spdk_nvmf_tgt *tgt)
353 : : {
354 : 6377 : return TAILQ_FIRST(&tgt->transports);
355 : : }
356 : :
357 : : struct spdk_nvmf_transport *
358 : 6268 : spdk_nvmf_transport_get_next(struct spdk_nvmf_transport *transport)
359 : : {
360 : 6268 : return TAILQ_NEXT(transport, link);
361 : : }
362 : :
363 : : int
364 : 265 : spdk_nvmf_transport_destroy(struct spdk_nvmf_transport *transport,
365 : : spdk_nvmf_transport_destroy_done_cb cb_fn, void *cb_arg)
366 : : {
367 : : struct spdk_nvmf_listener *listener, *listener_tmp;
368 : :
369 [ - + ]: 265 : TAILQ_FOREACH_SAFE(listener, &transport->listeners, link, listener_tmp) {
370 [ # # ]: 0 : TAILQ_REMOVE(&transport->listeners, listener, link);
371 : 0 : transport->ops->stop_listen(transport, &listener->trid);
372 : 0 : free(listener);
373 : : }
374 : :
375 [ + + ]: 265 : if (nvmf_transport_use_iobuf(transport)) {
376 : 258 : spdk_iobuf_unregister_module(transport->iobuf_name);
377 : : }
378 : :
379 [ - + ]: 265 : pthread_mutex_destroy(&transport->mutex);
380 : 265 : return transport->ops->destroy(transport, cb_fn, cb_arg);
381 : : }
382 : :
383 : : struct spdk_nvmf_listener *
384 : 1476 : nvmf_transport_find_listener(struct spdk_nvmf_transport *transport,
385 : : const struct spdk_nvme_transport_id *trid)
386 : : {
387 : : struct spdk_nvmf_listener *listener;
388 : :
389 [ + + ]: 1741 : TAILQ_FOREACH(listener, &transport->listeners, link) {
390 [ + + ]: 1379 : if (spdk_nvme_transport_id_compare(&listener->trid, trid) == 0) {
391 : 1114 : return listener;
392 : : }
393 : : }
394 : :
395 : 362 : return NULL;
396 : : }
397 : :
398 : : int
399 : 490 : spdk_nvmf_transport_listen(struct spdk_nvmf_transport *transport,
400 : : const struct spdk_nvme_transport_id *trid, struct spdk_nvmf_listen_opts *opts)
401 : : {
402 : : struct spdk_nvmf_listener *listener;
403 : : int rc;
404 : :
405 : 490 : listener = nvmf_transport_find_listener(transport, trid);
406 [ + + ]: 490 : if (!listener) {
407 : 353 : listener = calloc(1, sizeof(*listener));
408 [ - + ]: 353 : if (!listener) {
409 : 0 : return -ENOMEM;
410 : : }
411 : :
412 : 353 : listener->ref = 1;
413 : 353 : listener->trid = *trid;
414 : 353 : listener->sock_impl = opts->sock_impl;
415 : 353 : TAILQ_INSERT_TAIL(&transport->listeners, listener, link);
416 [ - + ]: 353 : pthread_mutex_lock(&transport->mutex);
417 : 353 : rc = transport->ops->listen(transport, &listener->trid, opts);
418 [ - + ]: 353 : pthread_mutex_unlock(&transport->mutex);
419 [ + + ]: 353 : if (rc != 0) {
420 [ - + ]: 3 : TAILQ_REMOVE(&transport->listeners, listener, link);
421 : 3 : free(listener);
422 : : }
423 : 353 : return rc;
424 : : }
425 : :
426 [ - + - - : 137 : if (opts->sock_impl && strncmp(opts->sock_impl, listener->sock_impl, strlen(listener->sock_impl))) {
- - - - -
- ]
427 : 0 : SPDK_ERRLOG("opts->sock_impl: '%s' doesn't match listener->sock_impl: '%s'\n", opts->sock_impl,
428 : : listener->sock_impl);
429 : 0 : return -EINVAL;
430 : : }
431 : :
432 : 137 : ++listener->ref;
433 : :
434 : 137 : return 0;
435 : : }
436 : :
437 : : int
438 : 490 : spdk_nvmf_transport_stop_listen(struct spdk_nvmf_transport *transport,
439 : : const struct spdk_nvme_transport_id *trid)
440 : : {
441 : : struct spdk_nvmf_listener *listener;
442 : :
443 : 490 : listener = nvmf_transport_find_listener(transport, trid);
444 [ + + ]: 490 : if (!listener) {
445 : 3 : return -ENOENT;
446 : : }
447 : :
448 [ + + ]: 487 : if (--listener->ref == 0) {
449 [ + + ]: 350 : TAILQ_REMOVE(&transport->listeners, listener, link);
450 [ - + ]: 350 : pthread_mutex_lock(&transport->mutex);
451 : 350 : transport->ops->stop_listen(transport, trid);
452 [ - + ]: 350 : pthread_mutex_unlock(&transport->mutex);
453 : 350 : free(listener);
454 : : }
455 : :
456 : 487 : return 0;
457 : : }
458 : :
459 : : struct nvmf_stop_listen_ctx {
460 : : struct spdk_nvmf_transport *transport;
461 : : struct spdk_nvme_transport_id trid;
462 : : struct spdk_nvmf_subsystem *subsystem;
463 : : spdk_nvmf_tgt_subsystem_listen_done_fn cb_fn;
464 : : void *cb_arg;
465 : : };
466 : :
467 : : static void
468 : 40 : nvmf_stop_listen_fini(struct spdk_io_channel_iter *i, int status)
469 : : {
470 : : struct nvmf_stop_listen_ctx *ctx;
471 : : struct spdk_nvmf_transport *transport;
472 : 40 : int rc = status;
473 : :
474 : 40 : ctx = spdk_io_channel_iter_get_ctx(i);
475 : 40 : transport = ctx->transport;
476 [ - + ]: 40 : assert(transport != NULL);
477 : :
478 : 40 : rc = spdk_nvmf_transport_stop_listen(transport, &ctx->trid);
479 [ - + ]: 40 : if (rc) {
480 : 0 : SPDK_ERRLOG("Failed to stop listening on address '%s'\n", ctx->trid.traddr);
481 : : }
482 : :
483 [ + - ]: 40 : if (ctx->cb_fn) {
484 : 40 : ctx->cb_fn(ctx->cb_arg, rc);
485 : : }
486 : 40 : free(ctx);
487 : 40 : }
488 : :
489 : : static void nvmf_stop_listen_disconnect_qpairs(struct spdk_io_channel_iter *i);
490 : :
491 : : static void
492 : 38 : nvmf_stop_listen_disconnect_qpairs_msg(void *ctx)
493 : : {
494 : 38 : nvmf_stop_listen_disconnect_qpairs((struct spdk_io_channel_iter *)ctx);
495 : 38 : }
496 : :
497 : : static void
498 : 133 : nvmf_stop_listen_disconnect_qpairs(struct spdk_io_channel_iter *i)
499 : : {
500 : : struct nvmf_stop_listen_ctx *ctx;
501 : : struct spdk_nvmf_poll_group *group;
502 : : struct spdk_io_channel *ch;
503 : : struct spdk_nvmf_qpair *qpair, *tmp_qpair;
504 : 19 : struct spdk_nvme_transport_id tmp_trid;
505 : 133 : bool qpair_found = false;
506 : :
507 : 133 : ctx = spdk_io_channel_iter_get_ctx(i);
508 : 133 : ch = spdk_io_channel_iter_get_channel(i);
509 : 133 : group = spdk_io_channel_get_ctx(ch);
510 : :
511 [ + + ]: 221 : TAILQ_FOREACH_SAFE(qpair, &group->qpairs, link, tmp_qpair) {
512 [ - + ]: 88 : if (spdk_nvmf_qpair_get_listen_trid(qpair, &tmp_trid)) {
513 : 0 : continue;
514 : : }
515 : :
516 : : /* Skip qpairs that don't match the listen trid and subsystem pointer. If
517 : : * the ctx->subsystem is NULL, it means disconnect all qpairs that match
518 : : * the listen trid. */
519 [ + + ]: 88 : if (!spdk_nvme_transport_id_compare(&ctx->trid, &tmp_trid)) {
520 [ + - ]: 52 : if (ctx->subsystem == NULL ||
521 [ + - + - ]: 52 : (qpair->ctrlr != NULL && ctx->subsystem == qpair->ctrlr->subsys)) {
522 : 52 : spdk_nvmf_qpair_disconnect(qpair);
523 : 52 : qpair_found = true;
524 : : }
525 : : }
526 : : }
527 [ + + ]: 133 : if (qpair_found) {
528 : 38 : spdk_thread_send_msg(spdk_get_thread(), nvmf_stop_listen_disconnect_qpairs_msg, i);
529 : 38 : return;
530 : : }
531 : :
532 : 95 : spdk_for_each_channel_continue(i, 0);
533 : : }
534 : :
535 : : int
536 : 40 : spdk_nvmf_transport_stop_listen_async(struct spdk_nvmf_transport *transport,
537 : : const struct spdk_nvme_transport_id *trid,
538 : : struct spdk_nvmf_subsystem *subsystem,
539 : : spdk_nvmf_tgt_subsystem_listen_done_fn cb_fn,
540 : : void *cb_arg)
541 : : {
542 : : struct nvmf_stop_listen_ctx *ctx;
543 : :
544 [ - + ]: 40 : if (trid->subnqn[0] != '\0') {
545 : 0 : SPDK_ERRLOG("subnqn should be empty, use subsystem pointer instead\n");
546 : 0 : return -EINVAL;
547 : : }
548 : :
549 : 40 : ctx = calloc(1, sizeof(struct nvmf_stop_listen_ctx));
550 [ - + ]: 40 : if (ctx == NULL) {
551 : 0 : return -ENOMEM;
552 : : }
553 : :
554 : 40 : ctx->trid = *trid;
555 : 40 : ctx->subsystem = subsystem;
556 : 40 : ctx->transport = transport;
557 : 40 : ctx->cb_fn = cb_fn;
558 : 40 : ctx->cb_arg = cb_arg;
559 : :
560 : 40 : spdk_for_each_channel(transport->tgt, nvmf_stop_listen_disconnect_qpairs, ctx,
561 : : nvmf_stop_listen_fini);
562 : :
563 : 40 : return 0;
564 : : }
565 : :
566 : : void
567 : 589 : nvmf_transport_listener_discover(struct spdk_nvmf_transport *transport,
568 : : struct spdk_nvme_transport_id *trid,
569 : : struct spdk_nvmf_discovery_log_page_entry *entry)
570 : : {
571 : 589 : transport->ops->listener_discover(transport, trid, entry);
572 : 589 : }
573 : :
574 : : struct spdk_nvmf_transport_poll_group *
575 : 579 : nvmf_transport_poll_group_create(struct spdk_nvmf_transport *transport,
576 : : struct spdk_nvmf_poll_group *group)
577 : : {
578 : : struct spdk_nvmf_transport_poll_group *tgroup;
579 : 579 : struct spdk_iobuf_opts opts_iobuf = {};
580 : : uint32_t buf_cache_size, small_cache_size, large_cache_size;
581 : : int rc;
582 : :
583 [ - + ]: 579 : pthread_mutex_lock(&transport->mutex);
584 : 579 : tgroup = transport->ops->poll_group_create(transport, group);
585 [ - + ]: 579 : pthread_mutex_unlock(&transport->mutex);
586 [ - + ]: 579 : if (!tgroup) {
587 : 0 : return NULL;
588 : : }
589 : 579 : tgroup->transport = transport;
590 : :
591 : 579 : STAILQ_INIT(&tgroup->pending_buf_queue);
592 : :
593 [ + + ]: 579 : if (!nvmf_transport_use_iobuf(transport)) {
594 : : /* We aren't going to allocate any shared buffers or cache, so just return now. */
595 : 21 : return tgroup;
596 : : }
597 : :
598 : 558 : buf_cache_size = transport->opts.buf_cache_size;
599 : :
600 : : /* buf_cache_size of UINT32_MAX means the value should be calculated dynamically
601 : : * based on the number of buffers in the shared pool and the number of poll groups
602 : : * that are sharing them. We allocate 75% of the pool for the cache, and then
603 : : * divide that by number of poll groups to determine the buf_cache_size for this
604 : : * poll group.
605 : : */
606 [ + + ]: 558 : if (buf_cache_size == UINT32_MAX) {
607 : 552 : uint32_t num_shared_buffers = transport->opts.num_shared_buffers;
608 : :
609 : : /* Theoretically the nvmf library can dynamically add poll groups to
610 : : * the target, after transports have already been created. We aren't
611 : : * going to try to really handle this case efficiently, just do enough
612 : : * here to ensure we don't divide-by-zero.
613 : : */
614 [ + - ]: 552 : uint16_t num_poll_groups = group->tgt->num_poll_groups ? : spdk_env_get_core_count();
615 : :
616 [ - + ]: 552 : buf_cache_size = (num_shared_buffers * 3 / 4) / num_poll_groups;
617 : : }
618 : :
619 : 558 : spdk_iobuf_get_opts(&opts_iobuf, sizeof(opts_iobuf));
620 : 558 : small_cache_size = buf_cache_size;
621 [ + + ]: 558 : if (transport->opts.io_unit_size <= opts_iobuf.small_bufsize) {
622 : 432 : large_cache_size = 0;
623 : : } else {
624 : 126 : large_cache_size = buf_cache_size;
625 : : }
626 : :
627 : 558 : tgroup->buf_cache = calloc(1, sizeof(*tgroup->buf_cache));
628 [ - + ]: 558 : if (!tgroup->buf_cache) {
629 : 0 : SPDK_ERRLOG("Unable to allocate an iobuf channel in the poll group.\n");
630 : 0 : goto err;
631 : : }
632 : :
633 : 558 : rc = spdk_iobuf_channel_init(tgroup->buf_cache, transport->iobuf_name, small_cache_size,
634 : : large_cache_size);
635 [ - + ]: 558 : if (rc != 0) {
636 : 0 : SPDK_ERRLOG("Unable to reserve the full number of buffers for the pg buffer cache.\n");
637 : 0 : rc = spdk_iobuf_channel_init(tgroup->buf_cache, transport->iobuf_name, 0, 0);
638 [ # # ]: 0 : if (rc != 0) {
639 : 0 : SPDK_ERRLOG("Unable to create an iobuf channel in the poll group.\n");
640 : 0 : goto err;
641 : : }
642 : : }
643 : :
644 : 558 : return tgroup;
645 : 0 : err:
646 : 0 : transport->ops->poll_group_destroy(tgroup);
647 : 0 : return NULL;
648 : : }
649 : :
650 : : struct spdk_nvmf_transport_poll_group *
651 : 8147 : nvmf_transport_get_optimal_poll_group(struct spdk_nvmf_transport *transport,
652 : : struct spdk_nvmf_qpair *qpair)
653 : : {
654 : : struct spdk_nvmf_transport_poll_group *tgroup;
655 : :
656 [ + - ]: 8147 : if (transport->ops->get_optimal_poll_group) {
657 [ - + ]: 8147 : pthread_mutex_lock(&transport->mutex);
658 : 8147 : tgroup = transport->ops->get_optimal_poll_group(qpair);
659 [ - + ]: 8147 : pthread_mutex_unlock(&transport->mutex);
660 : :
661 : 8147 : return tgroup;
662 : : } else {
663 : 0 : return NULL;
664 : : }
665 : : }
666 : :
667 : : void
668 : 579 : nvmf_transport_poll_group_destroy(struct spdk_nvmf_transport_poll_group *group)
669 : : {
670 : : struct spdk_nvmf_transport *transport;
671 : 579 : struct spdk_iobuf_channel *ch = NULL;
672 : :
673 : 579 : transport = group->transport;
674 : :
675 [ - + ]: 579 : if (!STAILQ_EMPTY(&group->pending_buf_queue)) {
676 : 0 : SPDK_ERRLOG("Pending I/O list wasn't empty on poll group destruction\n");
677 : : }
678 : :
679 [ + + ]: 579 : if (nvmf_transport_use_iobuf(transport)) {
680 : : /* The call to poll_group_destroy both frees the group memory, but also
681 : : * releases any remaining buffers. Cache channel pointer so we can still
682 : : * release the resources after the group has been freed. */
683 : 558 : ch = group->buf_cache;
684 : : }
685 : :
686 [ - + ]: 579 : pthread_mutex_lock(&transport->mutex);
687 : 579 : transport->ops->poll_group_destroy(group);
688 [ - + ]: 579 : pthread_mutex_unlock(&transport->mutex);
689 : :
690 [ + + ]: 579 : if (nvmf_transport_use_iobuf(transport)) {
691 : 558 : spdk_iobuf_channel_fini(ch);
692 : 558 : free(ch);
693 : : }
694 : 579 : }
695 : :
696 : : int
697 : 8147 : nvmf_transport_poll_group_add(struct spdk_nvmf_transport_poll_group *group,
698 : : struct spdk_nvmf_qpair *qpair)
699 : : {
700 [ + - ]: 8147 : if (qpair->transport) {
701 [ - + ]: 8147 : assert(qpair->transport == group->transport);
702 [ - + ]: 8147 : if (qpair->transport != group->transport) {
703 : 0 : return -1;
704 : : }
705 : : } else {
706 : 0 : qpair->transport = group->transport;
707 : : }
708 : :
709 : 2339 : SPDK_DTRACE_PROBE3(nvmf_transport_poll_group_add, qpair, qpair->qid,
710 : : spdk_thread_get_id(group->group->thread));
711 : :
712 : 8147 : return group->transport->ops->poll_group_add(group, qpair);
713 : : }
714 : :
715 : : int
716 : 8147 : nvmf_transport_poll_group_remove(struct spdk_nvmf_transport_poll_group *group,
717 : : struct spdk_nvmf_qpair *qpair)
718 : : {
719 : 8147 : int rc = ENOTSUP;
720 : :
721 : 2339 : SPDK_DTRACE_PROBE3(nvmf_transport_poll_group_remove, qpair, qpair->qid,
722 : : spdk_thread_get_id(group->group->thread));
723 : :
724 [ - + ]: 8147 : assert(qpair->transport == group->transport);
725 [ + - ]: 8147 : if (group->transport->ops->poll_group_remove) {
726 : 8147 : rc = group->transport->ops->poll_group_remove(group, qpair);
727 : : }
728 : :
729 : 8147 : return rc;
730 : : }
731 : :
732 : : int
733 : 1040553047 : nvmf_transport_poll_group_poll(struct spdk_nvmf_transport_poll_group *group)
734 : : {
735 : 1040553047 : return group->transport->ops->poll_group_poll(group);
736 : : }
737 : :
738 : : int
739 : 5109 : nvmf_transport_req_free(struct spdk_nvmf_request *req)
740 : : {
741 : 5109 : return req->qpair->transport->ops->req_free(req);
742 : : }
743 : :
744 : : int
745 : 20764962 : nvmf_transport_req_complete(struct spdk_nvmf_request *req)
746 : : {
747 : 20764962 : return req->qpair->transport->ops->req_complete(req);
748 : : }
749 : :
750 : : void
751 : 8147 : nvmf_transport_qpair_fini(struct spdk_nvmf_qpair *qpair,
752 : : spdk_nvmf_transport_qpair_fini_cb cb_fn,
753 : : void *cb_arg)
754 : : {
755 : 2339 : SPDK_DTRACE_PROBE1(nvmf_transport_qpair_fini, qpair);
756 : :
757 : 8147 : qpair->transport->ops->qpair_fini(qpair, cb_fn, cb_arg);
758 : 8147 : }
759 : :
760 : : int
761 : 296 : nvmf_transport_qpair_get_peer_trid(struct spdk_nvmf_qpair *qpair,
762 : : struct spdk_nvme_transport_id *trid)
763 : : {
764 : 296 : return qpair->transport->ops->qpair_get_peer_trid(qpair, trid);
765 : : }
766 : :
767 : : int
768 : 0 : nvmf_transport_qpair_get_local_trid(struct spdk_nvmf_qpair *qpair,
769 : : struct spdk_nvme_transport_id *trid)
770 : : {
771 : 0 : return qpair->transport->ops->qpair_get_local_trid(qpair, trid);
772 : : }
773 : :
774 : : int
775 : 10382 : nvmf_transport_qpair_get_listen_trid(struct spdk_nvmf_qpair *qpair,
776 : : struct spdk_nvme_transport_id *trid)
777 : : {
778 : 10382 : return qpair->transport->ops->qpair_get_listen_trid(qpair, trid);
779 : : }
780 : :
781 : : void
782 : 37302 : nvmf_transport_qpair_abort_request(struct spdk_nvmf_qpair *qpair,
783 : : struct spdk_nvmf_request *req)
784 : : {
785 [ + - ]: 37302 : if (qpair->transport->ops->qpair_abort_request) {
786 : 37302 : qpair->transport->ops->qpair_abort_request(qpair, req);
787 : : }
788 : 37302 : }
789 : :
790 : : bool
791 : 268 : spdk_nvmf_transport_opts_init(const char *transport_name,
792 : : struct spdk_nvmf_transport_opts *opts, size_t opts_size)
793 : : {
794 : : const struct spdk_nvmf_transport_ops *ops;
795 : 268 : struct spdk_nvmf_transport_opts opts_local = {};
796 : :
797 : 268 : ops = nvmf_get_transport_ops(transport_name);
798 [ + + ]: 268 : if (!ops) {
799 : 3 : SPDK_ERRLOG("Transport type %s unavailable.\n", transport_name);
800 : 3 : return false;
801 : : }
802 : :
803 [ + + ]: 265 : if (!opts) {
804 : 3 : SPDK_ERRLOG("opts should not be NULL\n");
805 : 3 : return false;
806 : : }
807 : :
808 [ + + ]: 262 : if (!opts_size) {
809 : 3 : SPDK_ERRLOG("opts_size inside opts should not be zero value\n");
810 : 3 : return false;
811 : : }
812 : :
813 : 259 : opts_local.association_timeout = NVMF_TRANSPORT_DEFAULT_ASSOCIATION_TIMEOUT_IN_MS;
814 : 259 : opts_local.acceptor_poll_rate = SPDK_NVMF_DEFAULT_ACCEPT_POLL_RATE_US;
815 : 259 : opts_local.disable_command_passthru = false;
816 : 259 : ops->opts_init(&opts_local);
817 : :
818 : 259 : nvmf_transport_opts_copy(opts, &opts_local, opts_size);
819 : :
820 : 259 : return true;
821 : : }
822 : :
823 : : void
824 : 10874359 : spdk_nvmf_request_free_buffers(struct spdk_nvmf_request *req,
825 : : struct spdk_nvmf_transport_poll_group *group,
826 : : struct spdk_nvmf_transport *transport)
827 : : {
828 : : uint32_t i;
829 : :
830 [ + + ]: 28570390 : for (i = 0; i < req->iovcnt; i++) {
831 : 17696031 : spdk_iobuf_put(group->buf_cache, req->iov[i].iov_base, req->iov[i].iov_len);
832 : 17696031 : req->iov[i].iov_base = NULL;
833 : 17696031 : req->iov[i].iov_len = 0;
834 : : }
835 : 10874359 : req->iovcnt = 0;
836 : 10874359 : req->data_from_pool = false;
837 : 10874359 : }
838 : :
839 : : typedef int (*set_buffer_callback)(struct spdk_nvmf_request *req, void *buf,
840 : : uint32_t length, uint32_t io_unit_size);
841 : : static int
842 : 17696238 : nvmf_request_set_buffer(struct spdk_nvmf_request *req, void *buf, uint32_t length,
843 : : uint32_t io_unit_size)
844 : : {
845 : 17696238 : req->iov[req->iovcnt].iov_base = buf;
846 : 17696238 : req->iov[req->iovcnt].iov_len = spdk_min(length, io_unit_size);
847 : 17696238 : length -= req->iov[req->iovcnt].iov_len;
848 : 17696238 : req->iovcnt++;
849 : :
850 : 17696238 : return length;
851 : : }
852 : :
853 : : static int
854 : 10874416 : nvmf_request_get_buffers(struct spdk_nvmf_request *req,
855 : : struct spdk_nvmf_transport_poll_group *group,
856 : : struct spdk_nvmf_transport *transport,
857 : : uint32_t length, uint32_t io_unit_size,
858 : : set_buffer_callback cb_func)
859 : : {
860 : : uint32_t num_buffers;
861 : 10874416 : uint32_t i = 0;
862 : : void *buffer;
863 : :
864 : : /* If the number of buffers is too large, then we know the I/O is larger than allowed.
865 : : * Fail it.
866 : : */
867 [ - + ]: 10874416 : num_buffers = SPDK_CEIL_DIV(length, io_unit_size);
868 [ - + ]: 10874416 : if (spdk_unlikely(num_buffers > NVMF_REQ_MAX_BUFFERS)) {
869 : 0 : return -EINVAL;
870 : : }
871 : :
872 [ + + ]: 28570672 : while (i < num_buffers) {
873 : 18351452 : buffer = spdk_iobuf_get(group->buf_cache, spdk_min(io_unit_size, length), NULL, NULL);
874 [ + + ]: 18351452 : if (spdk_unlikely(buffer == NULL)) {
875 : 655196 : return -ENOMEM;
876 : : }
877 : 17696256 : length = cb_func(req, buffer, length, io_unit_size);
878 : 17696256 : i++;
879 : : }
880 : :
881 [ - + ]: 10219220 : assert(length == 0);
882 : :
883 : 10219220 : return 0;
884 : : }
885 : :
886 : : int
887 : 10874404 : spdk_nvmf_request_get_buffers(struct spdk_nvmf_request *req,
888 : : struct spdk_nvmf_transport_poll_group *group,
889 : : struct spdk_nvmf_transport *transport,
890 : : uint32_t length)
891 : : {
892 : : int rc;
893 : :
894 [ - + ]: 10874404 : assert(nvmf_transport_use_iobuf(transport));
895 : :
896 : 10874404 : req->iovcnt = 0;
897 : 10874404 : rc = nvmf_request_get_buffers(req, group, transport, length,
898 : : transport->opts.io_unit_size,
899 : : nvmf_request_set_buffer);
900 [ + + ]: 10874404 : if (spdk_likely(rc == 0)) {
901 : 10219208 : req->data_from_pool = true;
902 [ + - ]: 655196 : } else if (rc == -ENOMEM) {
903 : 655196 : spdk_nvmf_request_free_buffers(req, group, transport);
904 : : }
905 : :
906 : 10874404 : return rc;
907 : : }
908 : :
909 : : static int
910 : 18 : nvmf_request_set_stripped_buffer(struct spdk_nvmf_request *req, void *buf, uint32_t length,
911 : : uint32_t io_unit_size)
912 : : {
913 : 18 : struct spdk_nvmf_stripped_data *data = req->stripped_data;
914 : :
915 : 18 : data->iov[data->iovcnt].iov_base = buf;
916 : 18 : data->iov[data->iovcnt].iov_len = spdk_min(length, io_unit_size);
917 : 18 : length -= data->iov[data->iovcnt].iov_len;
918 : 18 : data->iovcnt++;
919 : :
920 : 18 : return length;
921 : : }
922 : :
923 : : void
924 : 0 : nvmf_request_free_stripped_buffers(struct spdk_nvmf_request *req,
925 : : struct spdk_nvmf_transport_poll_group *group,
926 : : struct spdk_nvmf_transport *transport)
927 : : {
928 : 0 : struct spdk_nvmf_stripped_data *data = req->stripped_data;
929 : : uint32_t i;
930 : :
931 [ # # ]: 0 : for (i = 0; i < data->iovcnt; i++) {
932 : 0 : spdk_iobuf_put(group->buf_cache, data->iov[i].iov_base, data->iov[i].iov_len);
933 : : }
934 : 0 : free(data);
935 : 0 : req->stripped_data = NULL;
936 : 0 : }
937 : :
938 : : int
939 : 24 : nvmf_request_get_stripped_buffers(struct spdk_nvmf_request *req,
940 : : struct spdk_nvmf_transport_poll_group *group,
941 : : struct spdk_nvmf_transport *transport,
942 : : uint32_t length)
943 : : {
944 : 24 : uint32_t block_size = req->dif.dif_ctx.block_size;
945 : 24 : uint32_t data_block_size = block_size - req->dif.dif_ctx.md_size;
946 [ - + ]: 24 : uint32_t io_unit_size = transport->opts.io_unit_size / block_size * data_block_size;
947 : : struct spdk_nvmf_stripped_data *data;
948 : : uint32_t i;
949 : : int rc;
950 : :
951 : : /* Data blocks must be block aligned */
952 [ + + ]: 42 : for (i = 0; i < req->iovcnt; i++) {
953 [ + + + + ]: 30 : if (req->iov[i].iov_len % block_size) {
954 : 12 : return -EINVAL;
955 : : }
956 : : }
957 : :
958 : 12 : data = calloc(1, sizeof(*data));
959 [ - + ]: 12 : if (data == NULL) {
960 : 0 : SPDK_ERRLOG("Unable to allocate memory for stripped_data.\n");
961 : 0 : return -ENOMEM;
962 : : }
963 : 12 : req->stripped_data = data;
964 : 12 : req->stripped_data->iovcnt = 0;
965 : :
966 : 12 : rc = nvmf_request_get_buffers(req, group, transport, length, io_unit_size,
967 : : nvmf_request_set_stripped_buffer);
968 [ - + ]: 12 : if (rc == -ENOMEM) {
969 : 0 : nvmf_request_free_stripped_buffers(req, group, transport);
970 : 0 : return rc;
971 : : }
972 : 12 : return rc;
973 : : }
|