Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2016 Intel Corporation.
3 : : * All rights reserved.
4 : : * Copyright (c) 2021 Mellanox Technologies LTD. All rights reserved.
5 : : * Copyright (c) 2021, 2022 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
6 : : */
7 : :
8 : : /*
9 : : * NVMe transport abstraction
10 : : */
11 : :
12 : : #include "nvme_internal.h"
13 : : #include "spdk/queue.h"
14 : :
15 : : #define SPDK_MAX_NUM_OF_TRANSPORTS 16
16 : :
17 : : struct spdk_nvme_transport {
18 : : struct spdk_nvme_transport_ops ops;
19 : : TAILQ_ENTRY(spdk_nvme_transport) link;
20 : : };
21 : :
22 : : TAILQ_HEAD(nvme_transport_list, spdk_nvme_transport) g_spdk_nvme_transports =
23 : : TAILQ_HEAD_INITIALIZER(g_spdk_nvme_transports);
24 : :
25 : : struct spdk_nvme_transport g_spdk_transports[SPDK_MAX_NUM_OF_TRANSPORTS] = {};
26 : : int g_current_transport_index = 0;
27 : :
28 : : struct spdk_nvme_transport_opts g_spdk_nvme_transport_opts = {
29 : : .rdma_srq_size = 0,
30 : : .rdma_max_cq_size = 0,
31 : : };
32 : :
33 : : const struct spdk_nvme_transport *
34 : 1908 : nvme_get_first_transport(void)
35 : : {
36 : 1908 : return TAILQ_FIRST(&g_spdk_nvme_transports);
37 : : }
38 : :
39 : : const struct spdk_nvme_transport *
40 : 1028 : nvme_get_next_transport(const struct spdk_nvme_transport *transport)
41 : : {
42 [ # # # # : 1028 : return TAILQ_NEXT(transport, link);
# # ]
43 : : }
44 : :
45 : : /*
46 : : * Unfortunately, due to NVMe PCIe multiprocess support, we cannot store the
47 : : * transport object in either the controller struct or the admin qpair. This means
48 : : * that a lot of admin related transport calls will have to call nvme_get_transport
49 : : * in order to know which functions to call.
50 : : * In the I/O path, we have the ability to store the transport struct in the I/O
51 : : * qpairs to avoid taking a performance hit.
52 : : */
53 : : const struct spdk_nvme_transport *
54 : 68571201 : nvme_get_transport(const char *transport_name)
55 : : {
56 : : struct spdk_nvme_transport *registered_transport;
57 : :
58 [ + + - + : 87936241 : TAILQ_FOREACH(registered_transport, &g_spdk_nvme_transports, link) {
- + - + ]
59 [ + + + + : 87926486 : if (strcasecmp(transport_name, registered_transport->ops.name) == 0) {
+ + + - +
+ ]
60 : 68561446 : return registered_transport;
61 : : }
62 : 904766 : }
63 : :
64 : 9755 : return NULL;
65 : 1568978 : }
66 : :
67 : : bool
68 : 0 : spdk_nvme_transport_available(enum spdk_nvme_transport_type trtype)
69 : : {
70 : 0 : return nvme_get_transport(spdk_nvme_transport_id_trtype_str(trtype)) == NULL ? false : true;
71 : : }
72 : :
73 : : bool
74 : 70765 : spdk_nvme_transport_available_by_name(const char *transport_name)
75 : : {
76 : 70765 : return nvme_get_transport(transport_name) == NULL ? false : true;
77 : : }
78 : :
79 : : void
80 : 9752 : spdk_nvme_transport_register(const struct spdk_nvme_transport_ops *ops)
81 : : {
82 : : struct spdk_nvme_transport *new_transport;
83 : :
84 [ + + + - ]: 9752 : if (nvme_get_transport(ops->name)) {
85 [ # # ]: 0 : SPDK_ERRLOG("Double registering NVMe transport %s is prohibited.\n", ops->name);
86 [ # # ]: 0 : assert(false);
87 : : }
88 : :
89 [ + + ]: 9752 : if (g_current_transport_index == SPDK_MAX_NUM_OF_TRANSPORTS) {
90 : 0 : SPDK_ERRLOG("Unable to register new NVMe transport.\n");
91 [ # # ]: 0 : assert(false);
92 : : return;
93 : : }
94 [ + - + - : 9752 : new_transport = &g_spdk_transports[g_current_transport_index++];
+ - ]
95 : :
96 [ + - ]: 9752 : new_transport->ops = *ops;
97 [ + - + - : 9752 : TAILQ_INSERT_TAIL(&g_spdk_nvme_transports, new_transport, link);
+ - + - +
- + - + -
+ - + - +
- + - +
- ]
98 : 224 : }
99 : :
100 : 2776 : struct spdk_nvme_ctrlr *nvme_transport_ctrlr_construct(const struct spdk_nvme_transport_id *trid,
101 : : const struct spdk_nvme_ctrlr_opts *opts,
102 : : void *devhandle)
103 : : {
104 [ + - ]: 2776 : const struct spdk_nvme_transport *transport = nvme_get_transport(trid->trstring);
105 : : struct spdk_nvme_ctrlr *ctrlr;
106 : :
107 [ + + ]: 2776 : if (transport == NULL) {
108 [ # # ]: 0 : SPDK_ERRLOG("Transport %s doesn't exist.", trid->trstring);
109 : 0 : return NULL;
110 : : }
111 : :
112 [ + - + - : 2776 : ctrlr = transport->ops.ctrlr_construct(trid, opts, devhandle);
+ - - + +
- ]
113 : :
114 : 2776 : return ctrlr;
115 : 1052 : }
116 : :
117 : : int
118 : 70660 : nvme_transport_ctrlr_scan(struct spdk_nvme_probe_ctx *probe_ctx,
119 : : bool direct_connect)
120 : : {
121 [ + - + - ]: 70660 : const struct spdk_nvme_transport *transport = nvme_get_transport(probe_ctx->trid.trstring);
122 : :
123 [ + + ]: 70660 : if (transport == NULL) {
124 [ # # # # ]: 0 : SPDK_ERRLOG("Transport %s doesn't exist.", probe_ctx->trid.trstring);
125 : 0 : return -ENOENT;
126 : : }
127 : :
128 [ + - + - : 70660 : return transport->ops.ctrlr_scan(probe_ctx, direct_connect);
+ - - + +
- + - ]
129 : 1052 : }
130 : :
131 : : int
132 : 2761 : nvme_transport_ctrlr_destruct(struct spdk_nvme_ctrlr *ctrlr)
133 : : {
134 [ + - + - ]: 2761 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
135 : :
136 [ + + # # ]: 2761 : assert(transport != NULL);
137 [ + - + - : 2761 : return transport->ops.ctrlr_destruct(ctrlr);
+ - - + +
- ]
138 : : }
139 : :
140 : : int
141 : 2886 : nvme_transport_ctrlr_enable(struct spdk_nvme_ctrlr *ctrlr)
142 : : {
143 [ + - + - ]: 2886 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
144 : :
145 [ + + # # ]: 2886 : assert(transport != NULL);
146 [ + - + - : 2886 : return transport->ops.ctrlr_enable(ctrlr);
+ - - + +
- ]
147 : : }
148 : :
149 : : int
150 : 2743 : nvme_transport_ctrlr_ready(struct spdk_nvme_ctrlr *ctrlr)
151 : : {
152 [ + - + - ]: 2743 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
153 : :
154 [ + + # # ]: 2743 : assert(transport != NULL);
155 [ + + + - : 2743 : if (transport->ops.ctrlr_ready) {
+ - - + ]
156 [ # # # # : 0 : return transport->ops.ctrlr_ready(ctrlr);
# # # # #
# ]
157 : : }
158 : :
159 : 2743 : return 0;
160 : 1052 : }
161 : :
162 : : int
163 : 0 : nvme_transport_ctrlr_set_reg_4(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint32_t value)
164 : : {
165 [ # # # # ]: 0 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
166 : :
167 [ # # # # ]: 0 : assert(transport != NULL);
168 [ # # # # : 0 : return transport->ops.ctrlr_set_reg_4(ctrlr, offset, value);
# # # # #
# ]
169 : : }
170 : :
171 : : int
172 : 0 : nvme_transport_ctrlr_set_reg_8(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint64_t value)
173 : : {
174 [ # # # # ]: 0 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
175 : :
176 [ # # # # ]: 0 : assert(transport != NULL);
177 [ # # # # : 0 : return transport->ops.ctrlr_set_reg_8(ctrlr, offset, value);
# # # # #
# ]
178 : : }
179 : :
180 : : int
181 : 20892 : nvme_transport_ctrlr_get_reg_4(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint32_t *value)
182 : : {
183 [ + - + - ]: 20892 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
184 : :
185 [ + + # # ]: 20892 : assert(transport != NULL);
186 [ + - + - : 20892 : return transport->ops.ctrlr_get_reg_4(ctrlr, offset, value);
+ - - + +
- ]
187 : : }
188 : :
189 : : int
190 : 692 : nvme_transport_ctrlr_get_reg_8(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint64_t *value)
191 : : {
192 [ + - + - ]: 692 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
193 : :
194 [ + + # # ]: 692 : assert(transport != NULL);
195 [ + - + - : 692 : return transport->ops.ctrlr_get_reg_8(ctrlr, offset, value);
+ - - + +
- ]
196 : : }
197 : :
198 : : static int
199 : 5241364 : nvme_queue_register_operation_completion(struct spdk_nvme_ctrlr *ctrlr, uint64_t value,
200 : : spdk_nvme_reg_cb cb_fn, void *cb_ctx)
201 : : {
202 : : struct nvme_register_completion *ctx;
203 : :
204 : 5241364 : ctx = spdk_zmalloc(sizeof(*ctx), 0, NULL, SPDK_ENV_SOCKET_ID_ANY, SPDK_MALLOC_SHARE);
205 [ + + ]: 5241364 : if (ctx == NULL) {
206 : 0 : return -ENOMEM;
207 : : }
208 : :
209 [ + - + - : 5241364 : ctx->cpl.status.sct = SPDK_NVME_SCT_GENERIC;
+ - + - ]
210 [ + - + - : 5241364 : ctx->cpl.status.sc = SPDK_NVME_SC_SUCCESS;
+ - + - ]
211 [ + - + - ]: 5241364 : ctx->cb_fn = cb_fn;
212 [ + - + - ]: 5241364 : ctx->cb_ctx = cb_ctx;
213 [ + - + - ]: 5241364 : ctx->value = value;
214 [ + - + - ]: 5241364 : ctx->pid = getpid();
215 : :
216 : 5241364 : nvme_ctrlr_lock(ctrlr);
217 [ + - + - : 5241364 : STAILQ_INSERT_TAIL(&ctrlr->register_operations, ctx, stailq);
+ - + - +
- + - + -
+ - + - +
- + - +
- ]
218 : 5241364 : nvme_ctrlr_unlock(ctrlr);
219 : :
220 : 5241364 : return 0;
221 : 386116 : }
222 : :
223 : : int
224 : 6190 : nvme_transport_ctrlr_set_reg_4_async(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint32_t value,
225 : : spdk_nvme_reg_cb cb_fn, void *cb_arg)
226 : : {
227 [ + - + - ]: 6190 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
228 : : int rc;
229 : :
230 [ + + # # ]: 6190 : assert(transport != NULL);
231 [ + + + - : 6190 : if (transport->ops.ctrlr_set_reg_4_async == NULL) {
+ - + + ]
232 [ + - + - : 2054 : rc = transport->ops.ctrlr_set_reg_4(ctrlr, offset, value);
+ - - + +
- ]
233 [ - + ]: 2054 : if (rc != 0) {
234 : 0 : return rc;
235 : : }
236 : :
237 : 2054 : return nvme_queue_register_operation_completion(ctrlr, value, cb_fn, cb_arg);
238 : : }
239 : :
240 [ + - + - : 4136 : return transport->ops.ctrlr_set_reg_4_async(ctrlr, offset, value, cb_fn, cb_arg);
+ - - + +
- ]
241 : 2104 : }
242 : :
243 : : int
244 : 0 : nvme_transport_ctrlr_set_reg_8_async(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset, uint64_t value,
245 : : spdk_nvme_reg_cb cb_fn, void *cb_arg)
246 : :
247 : : {
248 [ # # # # ]: 0 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
249 : : int rc;
250 : :
251 [ # # # # ]: 0 : assert(transport != NULL);
252 [ # # # # : 0 : if (transport->ops.ctrlr_set_reg_8_async == NULL) {
# # # # ]
253 [ # # # # : 0 : rc = transport->ops.ctrlr_set_reg_8(ctrlr, offset, value);
# # # # #
# ]
254 [ # # ]: 0 : if (rc != 0) {
255 : 0 : return rc;
256 : : }
257 : :
258 : 0 : return nvme_queue_register_operation_completion(ctrlr, value, cb_fn, cb_arg);
259 : : }
260 : :
261 [ # # # # : 0 : return transport->ops.ctrlr_set_reg_8_async(ctrlr, offset, value, cb_fn, cb_arg);
# # # # #
# ]
262 : 0 : }
263 : :
264 : : int
265 : 5301409 : nvme_transport_ctrlr_get_reg_4_async(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset,
266 : : spdk_nvme_reg_cb cb_fn, void *cb_arg)
267 : : {
268 [ + - + - ]: 5301409 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
269 : 932450 : uint32_t value;
270 : : int rc;
271 : :
272 [ + + # # ]: 5301409 : assert(transport != NULL);
273 [ + + + - : 5301409 : if (transport->ops.ctrlr_get_reg_4_async == NULL) {
+ - + + ]
274 [ + - + - : 5238530 : rc = transport->ops.ctrlr_get_reg_4(ctrlr, offset, &value);
+ - - + +
- ]
275 [ - + ]: 5238530 : if (rc != 0) {
276 : 0 : return rc;
277 : : }
278 : :
279 : 5238530 : return nvme_queue_register_operation_completion(ctrlr, value, cb_fn, cb_arg);
280 : : }
281 : :
282 [ + - + - : 62879 : return transport->ops.ctrlr_get_reg_4_async(ctrlr, offset, cb_fn, cb_arg);
+ - - + +
- ]
283 : 392347 : }
284 : :
285 : : int
286 : 2886 : nvme_transport_ctrlr_get_reg_8_async(struct spdk_nvme_ctrlr *ctrlr, uint32_t offset,
287 : : spdk_nvme_reg_cb cb_fn, void *cb_arg)
288 : : {
289 [ + - + - ]: 2886 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
290 : 398 : uint64_t value;
291 : : int rc;
292 : :
293 [ + + # # ]: 2886 : assert(transport != NULL);
294 [ + + + - : 2886 : if (transport->ops.ctrlr_get_reg_8_async == NULL) {
+ - + + ]
295 [ + - + - : 780 : rc = transport->ops.ctrlr_get_reg_8(ctrlr, offset, &value);
+ - - + +
- ]
296 [ - + ]: 780 : if (rc != 0) {
297 : 0 : return rc;
298 : : }
299 : :
300 : 780 : return nvme_queue_register_operation_completion(ctrlr, value, cb_fn, cb_arg);
301 : : }
302 : :
303 [ + - + - : 2106 : return transport->ops.ctrlr_get_reg_8_async(ctrlr, offset, cb_fn, cb_arg);
+ - - + +
- ]
304 : 1052 : }
305 : :
306 : : uint32_t
307 : 2886 : nvme_transport_ctrlr_get_max_xfer_size(struct spdk_nvme_ctrlr *ctrlr)
308 : : {
309 [ + - + - ]: 2886 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
310 : :
311 [ + + # # ]: 2886 : assert(transport != NULL);
312 [ + - + - : 2886 : return transport->ops.ctrlr_get_max_xfer_size(ctrlr);
+ - - + +
- ]
313 : : }
314 : :
315 : : uint16_t
316 : 2753 : nvme_transport_ctrlr_get_max_sges(struct spdk_nvme_ctrlr *ctrlr)
317 : : {
318 [ + - + - ]: 2753 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
319 : :
320 [ + + # # ]: 2753 : assert(transport != NULL);
321 [ + - + - : 2753 : return transport->ops.ctrlr_get_max_sges(ctrlr);
+ - - + +
- ]
322 : : }
323 : :
324 : : int
325 : 0 : nvme_transport_ctrlr_reserve_cmb(struct spdk_nvme_ctrlr *ctrlr)
326 : : {
327 [ # # # # ]: 0 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
328 : :
329 [ # # # # ]: 0 : assert(transport != NULL);
330 [ # # # # : 0 : if (transport->ops.ctrlr_reserve_cmb != NULL) {
# # # # ]
331 [ # # # # : 0 : return transport->ops.ctrlr_reserve_cmb(ctrlr);
# # # # #
# ]
332 : : }
333 : :
334 : 0 : return -ENOTSUP;
335 : 0 : }
336 : :
337 : : void *
338 : 16 : nvme_transport_ctrlr_map_cmb(struct spdk_nvme_ctrlr *ctrlr, size_t *size)
339 : : {
340 [ # # # # ]: 16 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
341 : :
342 [ - + # # ]: 16 : assert(transport != NULL);
343 [ + + # # : 16 : if (transport->ops.ctrlr_map_cmb != NULL) {
# # # # ]
344 [ # # # # : 14 : return transport->ops.ctrlr_map_cmb(ctrlr, size);
# # # # #
# ]
345 : : }
346 : :
347 : 2 : return NULL;
348 : 0 : }
349 : :
350 : : int
351 : 2 : nvme_transport_ctrlr_unmap_cmb(struct spdk_nvme_ctrlr *ctrlr)
352 : : {
353 [ # # # # ]: 2 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
354 : :
355 [ - + # # ]: 2 : assert(transport != NULL);
356 [ + - # # : 2 : if (transport->ops.ctrlr_unmap_cmb != NULL) {
# # # # ]
357 [ # # # # : 2 : return transport->ops.ctrlr_unmap_cmb(ctrlr);
# # # # #
# ]
358 : : }
359 : :
360 : 0 : return 0;
361 : 0 : }
362 : :
363 : : int
364 : 44 : nvme_transport_ctrlr_enable_pmr(struct spdk_nvme_ctrlr *ctrlr)
365 : : {
366 [ # # # # ]: 44 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
367 : :
368 [ - + # # ]: 44 : assert(transport != NULL);
369 [ + - # # : 44 : if (transport->ops.ctrlr_enable_pmr != NULL) {
# # # # ]
370 [ # # # # : 44 : return transport->ops.ctrlr_enable_pmr(ctrlr);
# # # # #
# ]
371 : : }
372 : :
373 : 0 : return -ENOSYS;
374 : 0 : }
375 : :
376 : : int
377 : 44 : nvme_transport_ctrlr_disable_pmr(struct spdk_nvme_ctrlr *ctrlr)
378 : : {
379 [ # # # # ]: 44 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
380 : :
381 [ - + # # ]: 44 : assert(transport != NULL);
382 [ + - # # : 44 : if (transport->ops.ctrlr_disable_pmr != NULL) {
# # # # ]
383 [ # # # # : 44 : return transport->ops.ctrlr_disable_pmr(ctrlr);
# # # # #
# ]
384 : : }
385 : :
386 : 0 : return -ENOSYS;
387 : 0 : }
388 : :
389 : : void *
390 : 44 : nvme_transport_ctrlr_map_pmr(struct spdk_nvme_ctrlr *ctrlr, size_t *size)
391 : : {
392 [ # # # # ]: 44 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
393 : :
394 [ - + # # ]: 44 : assert(transport != NULL);
395 [ + - # # : 44 : if (transport->ops.ctrlr_map_pmr != NULL) {
# # # # ]
396 [ # # # # : 44 : return transport->ops.ctrlr_map_pmr(ctrlr, size);
# # # # #
# ]
397 : : }
398 : :
399 : 0 : return NULL;
400 : 0 : }
401 : :
402 : : int
403 : 44 : nvme_transport_ctrlr_unmap_pmr(struct spdk_nvme_ctrlr *ctrlr)
404 : : {
405 [ # # # # ]: 44 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
406 : :
407 [ - + # # ]: 44 : assert(transport != NULL);
408 [ + - # # : 44 : if (transport->ops.ctrlr_unmap_pmr != NULL) {
# # # # ]
409 [ # # # # : 44 : return transport->ops.ctrlr_unmap_pmr(ctrlr);
# # # # #
# ]
410 : : }
411 : :
412 : 0 : return -ENOSYS;
413 : 0 : }
414 : :
415 : : struct spdk_nvme_qpair *
416 : 5773 : nvme_transport_ctrlr_create_io_qpair(struct spdk_nvme_ctrlr *ctrlr, uint16_t qid,
417 : : const struct spdk_nvme_io_qpair_opts *opts)
418 : : {
419 : : struct spdk_nvme_qpair *qpair;
420 [ + - + - ]: 5773 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
421 : :
422 [ + + # # ]: 5773 : assert(transport != NULL);
423 [ + - + - : 5773 : qpair = transport->ops.ctrlr_create_io_qpair(ctrlr, qid, opts);
+ - - + +
- ]
424 [ + - + + ]: 5773 : if (qpair != NULL && !nvme_qpair_is_admin_queue(qpair)) {
425 [ + - + - ]: 5773 : qpair->transport = transport;
426 : 1052 : }
427 : :
428 : 5773 : return qpair;
429 : : }
430 : :
431 : : void
432 : 5773 : nvme_transport_ctrlr_delete_io_qpair(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair)
433 : : {
434 [ + - + - ]: 5773 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
435 : : int rc;
436 : :
437 [ + + # # ]: 5773 : assert(transport != NULL);
438 : :
439 : : /* Do not rely on qpair->transport. For multi-process cases, a foreign process may delete
440 : : * the IO qpair, in which case the transport object would be invalid (each process has their
441 : : * own unique transport objects since they contain function pointers). So we look up the
442 : : * transport object in the delete_io_qpair case.
443 : : */
444 [ + - + - : 5773 : rc = transport->ops.ctrlr_delete_io_qpair(ctrlr, qpair);
+ - - + +
- ]
445 [ + + ]: 5773 : if (rc != 0) {
446 [ # # # # ]: 0 : SPDK_ERRLOG("transport %s returned non-zero for ctrlr_delete_io_qpair op\n",
447 : : transport->ops.name);
448 [ # # ]: 0 : assert(false);
449 : : }
450 : 5773 : }
451 : :
452 : : static void
453 : 12290 : nvme_transport_connect_qpair_fail(struct spdk_nvme_qpair *qpair, void *unused)
454 : : {
455 [ # # # # ]: 12290 : struct spdk_nvme_ctrlr *ctrlr = qpair->ctrlr;
456 : :
457 : : /* If the qpair was unable to reconnect, restore the original failure reason */
458 [ # # # # ]: 12290 : qpair->transport_failure_reason = qpair->last_transport_failure_reason;
459 : 12290 : nvme_transport_ctrlr_disconnect_qpair(ctrlr, qpair);
460 : 12290 : }
461 : :
462 : : int
463 : 21000 : nvme_transport_ctrlr_connect_qpair(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair)
464 : : {
465 [ + - + - ]: 21000 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
466 : : int rc;
467 : :
468 [ + + # # ]: 21000 : assert(transport != NULL);
469 [ + + + + : 21000 : if (!nvme_qpair_is_admin_queue(qpair) && qpair->transport == NULL) {
+ - + - ]
470 [ # # # # ]: 0 : qpair->transport = transport;
471 : 0 : }
472 : :
473 [ + - + - ]: 21000 : qpair->last_transport_failure_reason = qpair->transport_failure_reason;
474 [ + - ]: 21000 : qpair->transport_failure_reason = SPDK_NVME_QPAIR_FAILURE_NONE;
475 : :
476 : 21000 : nvme_qpair_set_state(qpair, NVME_QPAIR_CONNECTING);
477 [ + - + - : 21000 : rc = transport->ops.ctrlr_connect_qpair(ctrlr, qpair);
+ - - + +
- ]
478 [ + + ]: 21000 : if (rc != 0) {
479 : 11502 : goto err;
480 : : }
481 : :
482 [ + + + - : 9498 : if (qpair->poll_group) {
+ + ]
483 : 2117 : rc = nvme_poll_group_connect_qpair(qpair);
484 [ - + ]: 2117 : if (rc) {
485 : 0 : goto err;
486 : : }
487 : 1 : }
488 : :
489 [ + + + + ]: 9498 : if (!qpair->async) {
490 : : /* Busy wait until the qpair exits the connecting state */
491 [ + + ]: 13316678 : while (nvme_qpair_get_state(qpair) == NVME_QPAIR_CONNECTING) {
492 [ + + + - : 13312211 : if (qpair->poll_group && spdk_nvme_ctrlr_is_fabrics(ctrlr)) {
- + # # ]
493 : 0 : rc = spdk_nvme_poll_group_process_completions(
494 [ # # # # : 0 : qpair->poll_group->group, 0,
# # # # ]
495 : : nvme_transport_connect_qpair_fail);
496 : 0 : } else {
497 : 13312211 : rc = spdk_nvme_qpair_process_completions(qpair, 0);
498 : : }
499 : :
500 [ + + ]: 13312211 : if (rc < 0) {
501 : 788 : goto err;
502 : : }
503 : : }
504 : 1060 : }
505 : :
506 : 8710 : return 0;
507 : 12290 : err:
508 : 12290 : nvme_transport_connect_qpair_fail(qpair, NULL);
509 [ + + ]: 12290 : if (nvme_qpair_get_state(qpair) == NVME_QPAIR_DISCONNECTING) {
510 [ - + # # : 409 : assert(qpair->async == true);
# # ]
511 : : /* Let the caller to poll the qpair until it is actually disconnected. */
512 : 409 : return 0;
513 : : }
514 : :
515 : 11881 : return rc;
516 : 2104 : }
517 : :
518 : : void
519 : 43135 : nvme_transport_ctrlr_disconnect_qpair(struct spdk_nvme_ctrlr *ctrlr, struct spdk_nvme_qpair *qpair)
520 : : {
521 [ + - + - ]: 43135 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
522 : :
523 [ + + + + ]: 65603 : if (nvme_qpair_get_state(qpair) == NVME_QPAIR_DISCONNECTING ||
524 : 24573 : nvme_qpair_get_state(qpair) == NVME_QPAIR_DISCONNECTED) {
525 : 22132 : return;
526 : : }
527 : :
528 : 21003 : nvme_qpair_set_state(qpair, NVME_QPAIR_DISCONNECTING);
529 [ + + # # ]: 21003 : assert(transport != NULL);
530 : :
531 [ + + + - : 21003 : if (qpair->poll_group && (qpair->active_proc == nvme_ctrlr_get_current_process(ctrlr))) {
+ + + - +
- - + ]
532 : 2117 : nvme_poll_group_disconnect_qpair(qpair);
533 : 1 : }
534 : :
535 [ + - + - : 21003 : transport->ops.ctrlr_disconnect_qpair(ctrlr, qpair);
+ - - + +
- ]
536 : 2105 : }
537 : :
538 : : void
539 : 19194 : nvme_transport_ctrlr_disconnect_qpair_done(struct spdk_nvme_qpair *qpair)
540 : : {
541 [ + + + - : 20802 : if (qpair->active_proc == nvme_ctrlr_get_current_process(qpair->ctrlr) ||
+ - + - +
+ + - ]
542 : 1617 : nvme_qpair_is_admin_queue(qpair)) {
543 : 19194 : nvme_qpair_abort_all_queued_reqs(qpair);
544 : 1061 : }
545 : 19194 : nvme_qpair_set_state(qpair, NVME_QPAIR_DISCONNECTED);
546 : 19194 : }
547 : :
548 : : int
549 : 8255 : nvme_transport_ctrlr_get_memory_domains(const struct spdk_nvme_ctrlr *ctrlr,
550 : : struct spdk_memory_domain **domains, int array_size)
551 : : {
552 [ + - + - ]: 8255 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
553 : :
554 [ + + # # ]: 8255 : assert(transport != NULL);
555 [ + + + - : 8255 : if (transport->ops.ctrlr_get_memory_domains) {
+ - - + ]
556 [ # # # # : 438 : return transport->ops.ctrlr_get_memory_domains(ctrlr, domains, array_size);
# # # # #
# ]
557 : : }
558 : :
559 : 7817 : return 0;
560 : 4 : }
561 : :
562 : : void
563 : 728 : nvme_transport_qpair_abort_reqs(struct spdk_nvme_qpair *qpair)
564 : : {
565 : : const struct spdk_nvme_transport *transport;
566 : :
567 [ + + ]: 728 : if (spdk_likely(!nvme_qpair_is_admin_queue(qpair))) {
568 [ # # # # : 41 : qpair->transport->ops.qpair_abort_reqs(qpair, qpair->abort_dnr);
# # # # #
# # # # #
# # ]
569 : 0 : } else {
570 [ + - + - : 687 : transport = nvme_get_transport(qpair->ctrlr->trid.trstring);
+ - + - ]
571 [ + + # # ]: 687 : assert(transport != NULL);
572 [ + - + - : 687 : transport->ops.qpair_abort_reqs(qpair, qpair->abort_dnr);
+ - - + +
- + - ]
573 : : }
574 : 728 : }
575 : :
576 : : int
577 : 2886 : nvme_transport_qpair_reset(struct spdk_nvme_qpair *qpair)
578 : : {
579 : : const struct spdk_nvme_transport *transport;
580 : :
581 [ - + ]: 2886 : if (spdk_likely(!nvme_qpair_is_admin_queue(qpair))) {
582 [ # # # # : 0 : return qpair->transport->ops.qpair_reset(qpair);
# # # # #
# # # #
# ]
583 : : }
584 : :
585 [ + - + - : 2886 : transport = nvme_get_transport(qpair->ctrlr->trid.trstring);
+ - + - ]
586 [ + + # # ]: 2886 : assert(transport != NULL);
587 [ + - + - : 2886 : return transport->ops.qpair_reset(qpair);
+ - - + +
- ]
588 : 1052 : }
589 : :
590 : : int
591 : 68451440 : nvme_transport_qpair_submit_request(struct spdk_nvme_qpair *qpair, struct nvme_request *req)
592 : : {
593 : : const struct spdk_nvme_transport *transport;
594 : :
595 [ + + ]: 68451440 : if (spdk_likely(!nvme_qpair_is_admin_queue(qpair))) {
596 [ + - + - : 66867886 : return qpair->transport->ops.qpair_submit_request(qpair, req);
+ - + - +
- - + +
- ]
597 : : }
598 : :
599 [ + - + - : 1583554 : transport = nvme_get_transport(qpair->ctrlr->trid.trstring);
+ - + - ]
600 [ + + # # ]: 1583554 : assert(transport != NULL);
601 [ + - + - : 1583554 : return transport->ops.qpair_submit_request(qpair, req);
+ - - + +
- ]
602 : 180192 : }
603 : :
604 : : int32_t
605 : 1060870664 : nvme_transport_qpair_process_completions(struct spdk_nvme_qpair *qpair, uint32_t max_completions)
606 : : {
607 : : const struct spdk_nvme_transport *transport;
608 : :
609 [ + + ]: 1060870664 : if (spdk_likely(!nvme_qpair_is_admin_queue(qpair))) {
610 [ + - + - : 999474102 : return qpair->transport->ops.qpair_process_completions(qpair, max_completions);
+ - + - +
- - + +
- ]
611 : : }
612 : :
613 [ + - + - : 61396562 : transport = nvme_get_transport(qpair->ctrlr->trid.trstring);
+ - + - ]
614 [ + + # # ]: 61396562 : assert(transport != NULL);
615 [ + - + - : 61396562 : return transport->ops.qpair_process_completions(qpair, max_completions);
+ - - + +
- ]
616 : 1969467 : }
617 : :
618 : : int
619 : 460521 : nvme_transport_qpair_iterate_requests(struct spdk_nvme_qpair *qpair,
620 : : int (*iter_fn)(struct nvme_request *req, void *arg),
621 : : void *arg)
622 : : {
623 : : const struct spdk_nvme_transport *transport;
624 : :
625 [ + - ]: 460521 : if (spdk_likely(!nvme_qpair_is_admin_queue(qpair))) {
626 [ # # # # : 460521 : return qpair->transport->ops.qpair_iterate_requests(qpair, iter_fn, arg);
# # # # #
# # # #
# ]
627 : : }
628 : :
629 [ # # # # : 0 : transport = nvme_get_transport(qpair->ctrlr->trid.trstring);
# # # # ]
630 [ # # # # ]: 0 : assert(transport != NULL);
631 [ # # # # : 0 : return transport->ops.qpair_iterate_requests(qpair, iter_fn, arg);
# # # # #
# ]
632 : 0 : }
633 : :
634 : : void
635 : 3315 : nvme_transport_admin_qpair_abort_aers(struct spdk_nvme_qpair *qpair)
636 : : {
637 [ + - + - : 3315 : const struct spdk_nvme_transport *transport = nvme_get_transport(qpair->ctrlr->trid.trstring);
+ - + - ]
638 : :
639 [ + + # # ]: 3315 : assert(transport != NULL);
640 [ + - + - : 3315 : transport->ops.admin_qpair_abort_aers(qpair);
+ - - + +
- ]
641 : 3315 : }
642 : :
643 : : struct spdk_nvme_transport_poll_group *
644 : 1908 : nvme_transport_poll_group_create(const struct spdk_nvme_transport *transport)
645 : : {
646 : 1908 : struct spdk_nvme_transport_poll_group *group = NULL;
647 : :
648 [ + - + - : 1908 : group = transport->ops.poll_group_create();
+ - - + +
- ]
649 [ + + ]: 1908 : if (group) {
650 [ + - + - ]: 1908 : group->transport = transport;
651 [ + - + - : 1908 : STAILQ_INIT(&group->connected_qpairs);
+ - + - +
- + - + -
+ - ]
652 [ + - + - : 1908 : STAILQ_INIT(&group->disconnected_qpairs);
+ - + - +
- + - + -
+ - ]
653 [ + - + - ]: 1908 : group->num_connected_qpairs = 0;
654 : 1 : }
655 : :
656 : 1908 : return group;
657 : : }
658 : :
659 : : struct spdk_nvme_transport_poll_group *
660 : 0 : nvme_transport_qpair_get_optimal_poll_group(const struct spdk_nvme_transport *transport,
661 : : struct spdk_nvme_qpair *qpair)
662 : : {
663 [ # # # # : 0 : if (transport->ops.qpair_get_optimal_poll_group) {
# # # # ]
664 [ # # # # : 0 : return transport->ops.qpair_get_optimal_poll_group(qpair);
# # # # #
# ]
665 : : } else {
666 : 0 : return NULL;
667 : : }
668 : 0 : }
669 : :
670 : : int
671 : 2120 : nvme_transport_poll_group_add(struct spdk_nvme_transport_poll_group *tgroup,
672 : : struct spdk_nvme_qpair *qpair)
673 : : {
674 : : int rc;
675 : :
676 [ + - + - : 2120 : rc = tgroup->transport->ops.poll_group_add(tgroup, qpair);
+ - + - +
- - + +
- ]
677 [ + + ]: 2120 : if (rc == 0) {
678 [ + - + - ]: 2120 : qpair->poll_group = tgroup;
679 [ + + # # ]: 2120 : assert(nvme_qpair_get_state(qpair) < NVME_QPAIR_CONNECTED);
680 [ + - + - : 2120 : qpair->poll_group_tailq_head = &tgroup->disconnected_qpairs;
+ - ]
681 [ + - + - : 2120 : STAILQ_INSERT_TAIL(&tgroup->disconnected_qpairs, qpair, poll_group_stailq);
+ - + - +
- + - + -
+ - + - +
- + - +
- ]
682 : 1 : }
683 : :
684 : 2120 : return rc;
685 : : }
686 : :
687 : : int
688 : 2126 : nvme_transport_poll_group_remove(struct spdk_nvme_transport_poll_group *tgroup,
689 : : struct spdk_nvme_qpair *qpair)
690 : : {
691 : : int rc __attribute__((unused));
692 : :
693 [ + + + - : 2126 : if (qpair->poll_group_tailq_head == &tgroup->connected_qpairs) {
+ - + - ]
694 : 3 : return -EINVAL;
695 [ + + + - : 2123 : } else if (qpair->poll_group_tailq_head != &tgroup->disconnected_qpairs) {
+ - - + ]
696 : 3 : return -ENOENT;
697 : : }
698 : :
699 [ + - + - : 2120 : rc = tgroup->transport->ops.poll_group_remove(tgroup, qpair);
+ - + - +
- - + +
- ]
700 [ + + # # ]: 2120 : assert(rc == 0);
701 : :
702 [ + + + + : 2126 : STAILQ_REMOVE(&tgroup->disconnected_qpairs, qpair, spdk_nvme_qpair, poll_group_stailq);
+ + + + +
- + - + -
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
703 : :
704 [ + - + - ]: 2120 : qpair->poll_group = NULL;
705 [ + - + - ]: 2120 : qpair->poll_group_tailq_head = NULL;
706 : :
707 : 2120 : return 0;
708 : 1 : }
709 : :
710 : : int64_t
711 : 1217135692 : nvme_transport_poll_group_process_completions(struct spdk_nvme_transport_poll_group *tgroup,
712 : : uint32_t completions_per_qpair, spdk_nvme_disconnected_qpair_cb disconnected_qpair_cb)
713 : : {
714 [ + - + - : 1217135692 : return tgroup->transport->ops.poll_group_process_completions(tgroup, completions_per_qpair,
+ - + - +
- - + +
- ]
715 : 71 : disconnected_qpair_cb);
716 : : }
717 : :
718 : : int
719 : 1908 : nvme_transport_poll_group_destroy(struct spdk_nvme_transport_poll_group *tgroup)
720 : : {
721 [ + - + - : 1908 : return tgroup->transport->ops.poll_group_destroy(tgroup);
+ - + - +
- - + +
- ]
722 : : }
723 : :
724 : : int
725 : 2126 : nvme_transport_poll_group_disconnect_qpair(struct spdk_nvme_qpair *qpair)
726 : : {
727 : : struct spdk_nvme_transport_poll_group *tgroup;
728 : : int rc __attribute__((unused));
729 : :
730 [ + - + - ]: 2126 : tgroup = qpair->poll_group;
731 : :
732 [ + + + - : 2126 : if (qpair->poll_group_tailq_head == &tgroup->disconnected_qpairs) {
+ - + - ]
733 : 3 : return 0;
734 : : }
735 : :
736 [ + + + - : 2123 : if (qpair->poll_group_tailq_head == &tgroup->connected_qpairs) {
+ - - + ]
737 [ + - + - : 2120 : rc = tgroup->transport->ops.poll_group_disconnect_qpair(qpair);
+ - + - +
- - + +
- ]
738 [ + + # # ]: 2120 : assert(rc == 0);
739 : :
740 [ + - + - : 2120 : qpair->poll_group_tailq_head = &tgroup->disconnected_qpairs;
+ - ]
741 [ + + + + : 2141 : STAILQ_REMOVE(&tgroup->connected_qpairs, qpair, spdk_nvme_qpair, poll_group_stailq);
+ + + + +
- + - + -
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
742 [ + + + - : 2120 : assert(tgroup->num_connected_qpairs > 0);
+ - # # ]
743 [ + - ]: 2120 : tgroup->num_connected_qpairs--;
744 [ + - + - : 2120 : STAILQ_INSERT_TAIL(&tgroup->disconnected_qpairs, qpair, poll_group_stailq);
+ - + - +
- + - + -
+ - + - +
- + - +
- ]
745 : :
746 : 2120 : return 0;
747 : : }
748 : :
749 : 3 : return -EINVAL;
750 : 1 : }
751 : :
752 : : int
753 : 2723 : nvme_transport_poll_group_connect_qpair(struct spdk_nvme_qpair *qpair)
754 : : {
755 : : struct spdk_nvme_transport_poll_group *tgroup;
756 : : int rc;
757 : :
758 [ + - + - ]: 2723 : tgroup = qpair->poll_group;
759 : :
760 [ + + + - : 2723 : if (qpair->poll_group_tailq_head == &tgroup->connected_qpairs) {
+ - + - ]
761 : 600 : return 0;
762 : : }
763 : :
764 [ + + + - : 2123 : if (qpair->poll_group_tailq_head == &tgroup->disconnected_qpairs) {
+ - - + ]
765 [ + - + - : 2120 : rc = tgroup->transport->ops.poll_group_connect_qpair(qpair);
+ - + - +
- - + +
- ]
766 [ + + ]: 2120 : if (rc == 0) {
767 [ + - + - : 2120 : qpair->poll_group_tailq_head = &tgroup->connected_qpairs;
+ - ]
768 [ + + + - : 2120 : STAILQ_REMOVE(&tgroup->disconnected_qpairs, qpair, spdk_nvme_qpair, poll_group_stailq);
+ + + + +
- + - + -
+ - + - +
- + - + -
+ - - + -
+ - + - +
- + - + #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
769 [ + - + - : 2120 : STAILQ_INSERT_TAIL(&tgroup->connected_qpairs, qpair, poll_group_stailq);
+ - + - +
- + - + -
+ - + - +
- + - +
- ]
770 [ + - ]: 2120 : tgroup->num_connected_qpairs++;
771 : 1 : }
772 : :
773 [ + + ]: 2120 : return rc == -EINPROGRESS ? 0 : rc;
774 : : }
775 : :
776 : :
777 : 3 : return -EINVAL;
778 : 1 : }
779 : :
780 : : int
781 : 10 : nvme_transport_poll_group_get_stats(struct spdk_nvme_transport_poll_group *tgroup,
782 : : struct spdk_nvme_transport_poll_group_stat **stats)
783 : : {
784 [ + - # # : 10 : if (tgroup->transport->ops.poll_group_get_stats) {
# # # # #
# # # ]
785 [ # # # # : 10 : return tgroup->transport->ops.poll_group_get_stats(tgroup, stats);
# # # # #
# # # #
# ]
786 : : }
787 : 0 : return -ENOTSUP;
788 : 0 : }
789 : :
790 : : void
791 : 10 : nvme_transport_poll_group_free_stats(struct spdk_nvme_transport_poll_group *tgroup,
792 : : struct spdk_nvme_transport_poll_group_stat *stats)
793 : : {
794 [ + - # # : 10 : if (tgroup->transport->ops.poll_group_free_stats) {
# # # # #
# # # ]
795 [ # # # # : 10 : tgroup->transport->ops.poll_group_free_stats(tgroup, stats);
# # # # #
# # # #
# ]
796 : 0 : }
797 : 10 : }
798 : :
799 : : spdk_nvme_transport_type_t
800 : 10 : nvme_transport_get_trtype(const struct spdk_nvme_transport *transport)
801 : : {
802 [ # # # # : 10 : return transport->ops.type;
# # ]
803 : : }
804 : :
805 : : void
806 : 0 : spdk_nvme_transport_get_opts(struct spdk_nvme_transport_opts *opts, size_t opts_size)
807 : : {
808 [ # # ]: 0 : if (opts == NULL) {
809 : 0 : SPDK_ERRLOG("opts should not be NULL.\n");
810 : 0 : return;
811 : : }
812 : :
813 [ # # ]: 0 : if (opts_size == 0) {
814 : 0 : SPDK_ERRLOG("opts_size should not be zero.\n");
815 : 0 : return;
816 : : }
817 : :
818 [ # # # # ]: 0 : opts->opts_size = opts_size;
819 : :
820 : : #define SET_FIELD(field) \
821 : : if (offsetof(struct spdk_nvme_transport_opts, field) + sizeof(opts->field) <= opts_size) { \
822 : : opts->field = g_spdk_nvme_transport_opts.field; \
823 : : } \
824 : :
825 [ # # # # : 0 : SET_FIELD(rdma_srq_size);
# # ]
826 [ # # # # : 0 : SET_FIELD(rdma_max_cq_size);
# # # # ]
827 : :
828 : : /* Do not remove this statement, you should always update this statement when you adding a new field,
829 : : * and do not forget to add the SET_FIELD statement for your added field. */
830 : : SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_transport_opts) == 24, "Incorrect size");
831 : :
832 : : #undef SET_FIELD
833 : 0 : }
834 : :
835 : : int
836 : 0 : spdk_nvme_transport_set_opts(const struct spdk_nvme_transport_opts *opts, size_t opts_size)
837 : : {
838 [ # # ]: 0 : if (opts == NULL) {
839 : 0 : SPDK_ERRLOG("opts should not be NULL.\n");
840 : 0 : return -EINVAL;
841 : : }
842 : :
843 [ # # ]: 0 : if (opts_size == 0) {
844 : 0 : SPDK_ERRLOG("opts_size should not be zero.\n");
845 : 0 : return -EINVAL;
846 : : }
847 : :
848 : : #define SET_FIELD(field) \
849 : : if (offsetof(struct spdk_nvme_transport_opts, field) + sizeof(opts->field) <= opts->opts_size) { \
850 : : g_spdk_nvme_transport_opts.field = opts->field; \
851 : : } \
852 : :
853 [ # # # # : 0 : SET_FIELD(rdma_srq_size);
# # # # #
# ]
854 [ # # # # : 0 : SET_FIELD(rdma_max_cq_size);
# # # # #
# # # ]
855 : :
856 [ # # # # : 0 : g_spdk_nvme_transport_opts.opts_size = opts->opts_size;
# # ]
857 : :
858 : : #undef SET_FIELD
859 : :
860 : 0 : return 0;
861 : 0 : }
862 : :
863 : : volatile struct spdk_nvme_registers *
864 : 10 : spdk_nvme_ctrlr_get_registers(struct spdk_nvme_ctrlr *ctrlr)
865 : : {
866 [ # # # # ]: 10 : const struct spdk_nvme_transport *transport = nvme_get_transport(ctrlr->trid.trstring);
867 : :
868 [ - + ]: 10 : if (transport == NULL) {
869 : : /* Transport does not exist. */
870 : 0 : return NULL;
871 : : }
872 : :
873 [ + - # # : 10 : if (transport->ops.ctrlr_get_registers) {
# # # # ]
874 [ # # # # : 10 : return transport->ops.ctrlr_get_registers(ctrlr);
# # # # #
# ]
875 : : }
876 : :
877 : 0 : return NULL;
878 : 0 : }
|