Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : * Copyright (C) 2020 Intel Corporation.
3 : : * Copyright (c) 2021 Mellanox Technologies LTD.
4 : : * Copyright (c) 2024 NVIDIA CORPORATION & AFFILIATES.
5 : : * All rights reserved.
6 : : */
7 : :
8 : : #include "nvme_internal.h"
9 : :
10 : : struct spdk_nvme_poll_group *
11 : 2316 : spdk_nvme_poll_group_create(void *ctx, struct spdk_nvme_accel_fn_table *table)
12 : : {
13 : : struct spdk_nvme_poll_group *group;
14 : : int rc;
15 : :
16 : 2316 : group = calloc(1, sizeof(*group));
17 [ + + ]: 2316 : if (group == NULL) {
18 : 4 : return NULL;
19 : : }
20 : :
21 [ + - + - : 2312 : group->accel_fn_table.table_size = sizeof(struct spdk_nvme_accel_fn_table);
+ - ]
22 [ + + + - : 2312 : if (table && table->table_size != 0) {
+ - + - ]
23 [ + - + - : 2049 : group->accel_fn_table.table_size = table->table_size;
+ - + - +
- ]
24 : : #define SET_FIELD(field) \
25 : : if (offsetof(struct spdk_nvme_accel_fn_table, field) + sizeof(table->field) <= table->table_size) { \
26 : : group->accel_fn_table.field = table->field; \
27 : : } \
28 : :
29 [ + + + - : 2049 : SET_FIELD(append_crc32c);
- + + - +
- + - + -
+ - ]
30 [ + + + - : 2049 : SET_FIELD(append_copy);
- + + - +
- + - + -
+ - ]
31 [ + + + - : 2049 : SET_FIELD(finish_sequence);
- + + - +
- + - + -
+ - ]
32 [ + + + - : 2049 : SET_FIELD(reverse_sequence);
- + + - +
- + - + -
+ - ]
33 [ + + + - : 2049 : SET_FIELD(abort_sequence);
- + + - +
- + - + -
+ - ]
34 : : /* Do not remove this statement, you should always update this statement when you adding a new field,
35 : : * and do not forget to add the SET_FIELD statement for your added field. */
36 : : SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_accel_fn_table) == 56, "Incorrect size");
37 : :
38 : : #undef SET_FIELD
39 : 21 : }
40 : :
41 : : /* Make sure either all or none of the sequence manipulation callbacks are implemented */
42 [ + + + - : 4352 : if ((group->accel_fn_table.finish_sequence && group->accel_fn_table.reverse_sequence &&
+ + + - +
- + - + -
+ - - + ]
43 [ + - + + : 4361 : group->accel_fn_table.abort_sequence) !=
+ - ]
44 [ + + + + : 2565 : (group->accel_fn_table.finish_sequence || group->accel_fn_table.reverse_sequence ||
+ - - + #
# # # # #
# # ]
45 [ - + # # : 263 : group->accel_fn_table.abort_sequence)) {
# # ]
46 : 0 : SPDK_ERRLOG("Invalid accel_fn_table configuration: either all or none of the "
47 : : "sequence callbacks must be provided\n");
48 : 0 : free(group);
49 : 0 : return NULL;
50 : : }
51 : :
52 : : /* Make sure that sequence callbacks are implemented if append* callbacks are provided */
53 [ + + + + : 2313 : if ((group->accel_fn_table.append_crc32c || group->accel_fn_table.append_copy) &&
+ - - + #
# # # # #
+ - ]
54 [ + + + - : 2059 : !group->accel_fn_table.finish_sequence) {
+ - ]
55 : 0 : SPDK_ERRLOG("Invalid accel_fn_table configuration: append_crc32c and/or append_copy require sequence "
56 : : "callbacks to be provided\n");
57 : 0 : free(group);
58 : 0 : return NULL;
59 : : }
60 : :
61 : : /* If interrupt is enabled, this fd_group will be used to manage events triggerd on file
62 : : * descriptors of all the qpairs in this poll group */
63 [ + - ]: 2312 : rc = spdk_fd_group_create(&group->fgrp);
64 [ + + ]: 2312 : if (rc) {
65 : : /* Ignore this for non-Linux platforms, as fd_groups aren't supported there. */
66 : : #if defined(__linux__)
67 : 0 : SPDK_ERRLOG("Cannot create fd group for the nvme poll group\n");
68 : 0 : free(group);
69 : 0 : return NULL;
70 : : #endif
71 : 30 : }
72 : :
73 [ - + - + ]: 2312 : group->disconnect_qpair_fd = -1;
74 [ - + - + ]: 2312 : group->ctx = ctx;
75 [ - + - + : 2312 : STAILQ_INIT(&group->tgroups);
- + - + -
+ - + - +
- + ]
76 : :
77 : 2312 : return group;
78 : 32 : }
79 : :
80 : : int
81 : 0 : spdk_nvme_poll_group_get_fd(struct spdk_nvme_poll_group *group)
82 : : {
83 [ # # # # : 0 : if (!group->fgrp) {
# # ]
84 : 0 : SPDK_ERRLOG("No fd group present for the nvme poll group.\n");
85 [ # # ]: 0 : assert(false);
86 : : return -EINVAL;
87 : : }
88 : :
89 [ # # # # ]: 0 : return spdk_fd_group_get_fd(group->fgrp);
90 : : }
91 : :
92 : : struct spdk_nvme_poll_group *
93 : 0 : spdk_nvme_qpair_get_optimal_poll_group(struct spdk_nvme_qpair *qpair)
94 : : {
95 : : struct spdk_nvme_transport_poll_group *tgroup;
96 : :
97 [ # # # # ]: 0 : tgroup = nvme_transport_qpair_get_optimal_poll_group(qpair->transport, qpair);
98 : :
99 [ # # ]: 0 : if (tgroup == NULL) {
100 : 0 : return NULL;
101 : : }
102 : :
103 [ # # # # ]: 0 : return tgroup->group;
104 : 0 : }
105 : :
106 : : #ifdef __linux__
107 : : static int
108 : 0 : nvme_poll_group_read_disconnect_qpair_fd(void *arg)
109 : : {
110 : 0 : return 0;
111 : : }
112 : :
113 : : void
114 : 2433 : nvme_poll_group_write_disconnect_qpair_fd(struct spdk_nvme_poll_group *group)
115 : : {
116 : 2433 : uint64_t notify = 1;
117 : : int rc;
118 : :
119 [ + + + - : 2433 : if (!group->enable_interrupts) {
+ - - + ]
120 : 2433 : return;
121 : : }
122 : :
123 : : /* Write to the disconnect qpair fd. This will generate event on the epoll fd of poll
124 : : * group. We then check for disconnected qpairs in spdk_nvme_poll_group_wait() */
125 [ # # # # ]: 0 : rc = write(group->disconnect_qpair_fd, ¬ify, sizeof(notify));
126 [ # # ]: 0 : if (rc < 0) {
127 [ # # ]: 0 : SPDK_ERRLOG("failed to write the disconnect qpair fd: %s.\n", strerror(errno));
128 : 0 : }
129 : 1 : }
130 : :
131 : : static int
132 : 0 : nvme_poll_group_add_disconnect_qpair_fd(struct spdk_nvme_poll_group *group)
133 : : {
134 : 0 : struct spdk_event_handler_opts opts = {};
135 : : int fd;
136 : :
137 : 0 : fd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
138 [ # # ]: 0 : if (fd < 0) {
139 : 0 : return fd;
140 : : }
141 : :
142 [ # # # # : 0 : assert(group->disconnect_qpair_fd == -1);
# # # # ]
143 [ # # # # ]: 0 : group->disconnect_qpair_fd = fd;
144 : :
145 : 0 : spdk_fd_group_get_default_event_handler_opts(&opts, sizeof(opts));
146 [ # # ]: 0 : opts.fd_type = SPDK_FD_TYPE_EVENTFD;
147 : :
148 [ # # # # ]: 0 : return SPDK_FD_GROUP_ADD_EXT(group->fgrp, fd, nvme_poll_group_read_disconnect_qpair_fd,
149 : : group, &opts);
150 : 0 : }
151 : :
152 : : #else
153 : :
154 : : void
155 : 23 : nvme_poll_group_write_disconnect_qpair_fd(struct spdk_nvme_poll_group *group)
156 : : {
157 : 23 : }
158 : :
159 : : static int
160 : 0 : nvme_poll_group_add_disconnect_qpair_fd(struct spdk_nvme_poll_group *group)
161 : : {
162 : 0 : return -ENOTSUP;
163 : : }
164 : :
165 : : #endif
166 : :
167 : : int
168 : 2611 : spdk_nvme_poll_group_add(struct spdk_nvme_poll_group *group, struct spdk_nvme_qpair *qpair)
169 : : {
170 : : struct spdk_nvme_transport_poll_group *tgroup;
171 : : const struct spdk_nvme_transport *transport;
172 : : int rc;
173 : :
174 [ + + ]: 2611 : if (nvme_qpair_get_state(qpair) != NVME_QPAIR_DISCONNECTED) {
175 : 4 : return -EINVAL;
176 : : }
177 : :
178 [ + + + + : 2607 : if (!group->enable_interrupts_is_valid) {
+ - - + ]
179 [ + - + - ]: 2296 : group->enable_interrupts_is_valid = true;
180 [ + + + - : 2296 : group->enable_interrupts = qpair->ctrlr->opts.enable_interrupts;
+ - + - +
- + - + -
+ - ]
181 [ + + + + : 2296 : if (group->enable_interrupts) {
+ - + - ]
182 : 0 : rc = nvme_poll_group_add_disconnect_qpair_fd(group);
183 [ # # ]: 0 : if (rc != 0) {
184 : 0 : return rc;
185 : : }
186 : 0 : }
187 [ + + - + : 338 : } else if (qpair->ctrlr->opts.enable_interrupts != group->enable_interrupts) {
+ + # # #
# # # # #
# # # # #
# ]
188 [ + + + - : 4 : SPDK_ERRLOG("Queue pair %s interrupts cannot be added to poll group\n",
# # # # #
# # # ]
189 : : qpair->ctrlr->opts.enable_interrupts ? "without" : "with");
190 : 4 : return -EINVAL;
191 : : }
192 : :
193 [ + + + - : 2643 : STAILQ_FOREACH(tgroup, &group->tgroups, link) {
+ - + - #
# # # #
# ]
194 [ + + # # : 331 : if (tgroup->transport == qpair->transport) {
# # # # #
# ]
195 : 291 : break;
196 : : }
197 : 10 : }
198 : :
199 : : /* See if a new transport has been added (dlopen style) and we need to update the poll group */
200 [ + + ]: 2603 : if (!tgroup) {
201 : 2312 : transport = nvme_get_first_transport();
202 [ + + ]: 3725 : while (transport != NULL) {
203 [ + + + - : 3717 : if (transport == qpair->transport) {
- + ]
204 : 2304 : tgroup = nvme_transport_poll_group_create(transport);
205 [ + + ]: 2304 : if (tgroup == NULL) {
206 : 0 : return -ENOMEM;
207 : : }
208 [ - + - + ]: 2304 : tgroup->group = group;
209 [ - + - + : 2304 : STAILQ_INSERT_TAIL(&group->tgroups, tgroup, link);
- + - + -
+ - + - +
- + - + -
+ - + -
+ ]
210 : 2304 : break;
211 : : }
212 : 1413 : transport = nvme_get_next_transport(transport);
213 : : }
214 : 31 : }
215 : :
216 [ + + ]: 2603 : return tgroup ? nvme_transport_poll_group_add(tgroup, qpair) : -ENODEV;
217 : 37 : }
218 : :
219 : : int
220 : 2595 : spdk_nvme_poll_group_remove(struct spdk_nvme_poll_group *group, struct spdk_nvme_qpair *qpair)
221 : : {
222 : : struct spdk_nvme_transport_poll_group *tgroup;
223 : :
224 [ + + + - : 2627 : STAILQ_FOREACH(tgroup, &group->tgroups, link) {
+ - + - #
# # # #
# ]
225 [ + + + - : 2623 : if (tgroup->transport == qpair->transport) {
+ - + - -
+ ]
226 : 2591 : return nvme_transport_poll_group_remove(tgroup, qpair);
227 : : }
228 : 8 : }
229 : :
230 : 4 : return -ENODEV;
231 : 33 : }
232 : :
233 : : static int
234 : 0 : nvme_qpair_process_completion_wrapper(void *arg)
235 : : {
236 : 0 : struct spdk_nvme_qpair *qpair = arg;
237 : :
238 : 0 : return spdk_nvme_qpair_process_completions(qpair, 0);
239 : : }
240 : :
241 : : static int
242 : 3606 : nvme_poll_group_add_qpair_fd(struct spdk_nvme_qpair *qpair)
243 : : {
244 : : struct spdk_nvme_poll_group *group;
245 : 3606 : struct spdk_event_handler_opts opts = {
246 : : .opts_size = SPDK_SIZEOF(&opts, fd_type),
247 : : };
248 : : int fd;
249 : :
250 [ + - + - : 3606 : group = qpair->poll_group->group;
+ - + - ]
251 [ + + + - : 3606 : if (group->enable_interrupts == false) {
+ - - + ]
252 : 3606 : return 0;
253 : : }
254 : :
255 : 0 : fd = spdk_nvme_qpair_get_fd(qpair, &opts);
256 [ # # ]: 0 : if (fd < 0) {
257 : 0 : SPDK_ERRLOG("Cannot get fd for the qpair: %d\n", fd);
258 : 0 : return -EINVAL;
259 : : }
260 : :
261 [ # # # # ]: 0 : return SPDK_FD_GROUP_ADD_EXT(group->fgrp, fd, nvme_qpair_process_completion_wrapper,
262 : : qpair, &opts);
263 : 25 : }
264 : :
265 : : static void
266 : 2559 : nvme_poll_group_remove_qpair_fd(struct spdk_nvme_qpair *qpair)
267 : : {
268 : : struct spdk_nvme_poll_group *group;
269 : : int fd;
270 : :
271 [ + - + - : 2559 : group = qpair->poll_group->group;
+ - + - ]
272 [ + + + - : 2559 : if (group->enable_interrupts == false) {
+ - - + ]
273 : 2559 : return;
274 : : }
275 : :
276 : 0 : fd = spdk_nvme_qpair_get_fd(qpair, NULL);
277 [ # # ]: 0 : if (fd < 0) {
278 : 0 : SPDK_ERRLOG("Cannot get fd for the qpair: %d\n", fd);
279 [ # # ]: 0 : assert(false);
280 : : return;
281 : : }
282 : :
283 [ # # # # ]: 0 : spdk_fd_group_remove(group->fgrp, fd);
284 : 24 : }
285 : :
286 : : int
287 : 3606 : nvme_poll_group_connect_qpair(struct spdk_nvme_qpair *qpair)
288 : : {
289 : : int rc;
290 : :
291 : 3606 : rc = nvme_transport_poll_group_connect_qpair(qpair);
292 [ - + ]: 3606 : if (rc != 0) {
293 : 0 : return rc;
294 : : }
295 : :
296 : 3606 : rc = nvme_poll_group_add_qpair_fd(qpair);
297 [ + + ]: 3606 : if (rc != 0) {
298 : 0 : nvme_transport_poll_group_disconnect_qpair(qpair);
299 : 0 : return rc;
300 : : }
301 : :
302 : 3606 : return 0;
303 : 25 : }
304 : :
305 : : int
306 : 2559 : nvme_poll_group_disconnect_qpair(struct spdk_nvme_qpair *qpair)
307 : : {
308 : 2559 : nvme_poll_group_remove_qpair_fd(qpair);
309 : :
310 : 2559 : return nvme_transport_poll_group_disconnect_qpair(qpair);
311 : : }
312 : :
313 : : int
314 : 0 : spdk_nvme_poll_group_wait(struct spdk_nvme_poll_group *group,
315 : : spdk_nvme_disconnected_qpair_cb disconnected_qpair_cb)
316 : : {
317 : : struct spdk_nvme_transport_poll_group *tgroup;
318 : 0 : int num_events, timeout = -1;
319 : :
320 [ # # ]: 0 : if (disconnected_qpair_cb == NULL) {
321 : 0 : return -EINVAL;
322 : : }
323 : :
324 [ # # # # : 0 : STAILQ_FOREACH(tgroup, &group->tgroups, link) {
# # # # #
# # # #
# ]
325 : 0 : nvme_transport_poll_group_check_disconnected_qpairs(tgroup, disconnected_qpair_cb);
326 : 0 : }
327 : :
328 [ # # # # ]: 0 : num_events = spdk_fd_group_wait(group->fgrp, timeout);
329 : :
330 : 0 : return num_events;
331 : 0 : }
332 : :
333 : : int64_t
334 : 678642362 : spdk_nvme_poll_group_process_completions(struct spdk_nvme_poll_group *group,
335 : : uint32_t completions_per_qpair, spdk_nvme_disconnected_qpair_cb disconnected_qpair_cb)
336 : : {
337 : : struct spdk_nvme_transport_poll_group *tgroup;
338 : 678642362 : int64_t local_completions = 0, error_reason = 0, num_completions = 0;
339 : :
340 [ + + ]: 678642362 : if (disconnected_qpair_cb == NULL) {
341 : 0 : return -EINVAL;
342 : : }
343 : :
344 [ + + + + : 678642362 : if (spdk_unlikely(group->in_process_completions)) {
+ - - + ]
345 : 1197722 : return 0;
346 : : }
347 [ + - + - ]: 677444640 : group->in_process_completions = true;
348 : :
349 [ + + + - : 1354889255 : STAILQ_FOREACH(tgroup, &group->tgroups, link) {
+ - + + +
- + - +
- ]
350 : 695621988 : local_completions = nvme_transport_poll_group_process_completions(tgroup, completions_per_qpair,
351 : 18177373 : disconnected_qpair_cb);
352 [ + + + - ]: 677444615 : if (local_completions < 0 && error_reason == 0) {
353 : 42 : error_reason = local_completions;
354 : 0 : } else {
355 [ + - ]: 677444573 : num_completions += local_completions;
356 : : /* Just to be safe */
357 [ + + # # ]: 677444573 : assert(num_completions >= 0);
358 : : }
359 : 18177373 : }
360 [ + - + - ]: 677444640 : group->in_process_completions = false;
361 : :
362 [ + + ]: 677444640 : return error_reason ? error_reason : num_completions;
363 : 18177395 : }
364 : :
365 : : int
366 : 341709 : spdk_nvme_poll_group_all_connected(struct spdk_nvme_poll_group *group)
367 : : {
368 : : struct spdk_nvme_transport_poll_group *tgroup;
369 : : struct spdk_nvme_qpair *qpair;
370 : 341709 : int rc = 0;
371 : :
372 [ + + # # : 683418 : STAILQ_FOREACH(tgroup, &group->tgroups, link) {
# # # # #
# # # #
# ]
373 [ + + # # : 341709 : if (!STAILQ_EMPTY(&tgroup->disconnected_qpairs)) {
# # # # ]
374 : : /* Treat disconnected qpairs as highest priority for notification.
375 : : * This means we can just return immediately here.
376 : : */
377 : 0 : return -EIO;
378 : : }
379 [ + + # # : 346474 : STAILQ_FOREACH(qpair, &tgroup->connected_qpairs, poll_group_stailq) {
# # # # #
# # # #
# ]
380 [ - + ]: 346243 : if (nvme_qpair_get_state(qpair) < NVME_QPAIR_CONNECTING) {
381 : 0 : return -EIO;
382 [ + + ]: 346243 : } else if (nvme_qpair_get_state(qpair) == NVME_QPAIR_CONNECTING) {
383 : 341478 : rc = -EAGAIN;
384 : : /* Break so that we can check the remaining transport groups,
385 : : * in case any of them have a disconnected qpair.
386 : : */
387 : 341478 : break;
388 : : }
389 : 2 : }
390 : 28 : }
391 : :
392 : 341709 : return rc;
393 : 28 : }
394 : :
395 : : void *
396 : 0 : spdk_nvme_poll_group_get_ctx(struct spdk_nvme_poll_group *group)
397 : : {
398 [ # # # # ]: 0 : return group->ctx;
399 : : }
400 : :
401 : : int
402 : 2316 : spdk_nvme_poll_group_destroy(struct spdk_nvme_poll_group *group)
403 : : {
404 : : struct spdk_nvme_transport_poll_group *tgroup, *tmp_tgroup;
405 [ + - + - ]: 2316 : struct spdk_fd_group *fgrp = group->fgrp;
406 : :
407 [ + + + + : 4600 : STAILQ_FOREACH_SAFE(tgroup, &group->tgroups, link, tmp_tgroup) {
+ - + + +
- + - + -
+ + ]
408 [ + + + + : 2288 : STAILQ_REMOVE(&group->tgroups, tgroup, spdk_nvme_transport_poll_group, link);
+ - - + +
- + - + -
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
409 [ + + ]: 2288 : if (nvme_transport_poll_group_destroy(tgroup) != 0) {
410 [ # # # # : 4 : STAILQ_INSERT_TAIL(&group->tgroups, tgroup, link);
# # # # #
# # # # #
# # # # #
# # # #
# ]
411 : 4 : return -EBUSY;
412 : : }
413 : :
414 : 24 : }
415 : :
416 [ + + ]: 2312 : if (fgrp) {
417 [ + + + + : 2282 : if (group->enable_interrupts) {
+ - + - ]
418 [ # # # # ]: 0 : spdk_fd_group_remove(fgrp, group->disconnect_qpair_fd);
419 [ # # # # ]: 0 : close(group->disconnect_qpair_fd);
420 : 0 : }
421 : 2282 : spdk_fd_group_destroy(fgrp);
422 : 1 : }
423 : :
424 : 2312 : free(group);
425 : :
426 : 2312 : return 0;
427 : 32 : }
428 : :
429 : : int
430 : 16 : spdk_nvme_poll_group_get_stats(struct spdk_nvme_poll_group *group,
431 : : struct spdk_nvme_poll_group_stat **stats)
432 : : {
433 : : struct spdk_nvme_transport_poll_group *tgroup;
434 : : struct spdk_nvme_poll_group_stat *result;
435 : 16 : uint32_t transports_count = 0;
436 : : /* Not all transports used by this poll group may support statistics reporting */
437 : 16 : uint32_t reported_stats_count = 0;
438 : : int rc;
439 : :
440 [ + + # # ]: 16 : assert(group);
441 [ - + # # ]: 16 : assert(stats);
442 : :
443 : 16 : result = calloc(1, sizeof(*result));
444 [ + + ]: 16 : if (!result) {
445 : 0 : SPDK_ERRLOG("Failed to allocate memory for poll group statistics\n");
446 : 0 : return -ENOMEM;
447 : : }
448 : :
449 [ + + # # : 36 : STAILQ_FOREACH(tgroup, &group->tgroups, link) {
# # # # #
# # # #
# ]
450 : 20 : transports_count++;
451 : 3 : }
452 : :
453 [ # # # # ]: 16 : result->transport_stat = calloc(transports_count, sizeof(*result->transport_stat));
454 [ - + # # : 16 : if (!result->transport_stat) {
# # ]
455 : 0 : SPDK_ERRLOG("Failed to allocate memory for poll group statistics\n");
456 : 0 : free(result);
457 : 0 : return -ENOMEM;
458 : : }
459 : :
460 [ + + # # : 36 : STAILQ_FOREACH(tgroup, &group->tgroups, link) {
# # # # #
# # # #
# ]
461 [ # # # # : 20 : rc = nvme_transport_poll_group_get_stats(tgroup, &result->transport_stat[reported_stats_count]);
# # ]
462 [ + + ]: 20 : if (rc == 0) {
463 : 20 : reported_stats_count++;
464 : 3 : }
465 : 3 : }
466 : :
467 [ + + ]: 16 : if (reported_stats_count == 0) {
468 [ # # # # ]: 4 : free(result->transport_stat);
469 : 4 : free(result);
470 [ + + - + : 4 : SPDK_DEBUGLOG(nvme, "No transport statistics available\n");
# # ]
471 : 4 : return -ENOTSUP;
472 : : }
473 : :
474 [ # # # # ]: 12 : result->num_transports = reported_stats_count;
475 [ # # ]: 12 : *stats = result;
476 : :
477 : 12 : return 0;
478 : 2 : }
479 : :
480 : : void
481 : 12 : spdk_nvme_poll_group_free_stats(struct spdk_nvme_poll_group *group,
482 : : struct spdk_nvme_poll_group_stat *stat)
483 : : {
484 : : struct spdk_nvme_transport_poll_group *tgroup;
485 : : uint32_t i;
486 : 12 : uint32_t freed_stats __attribute__((unused)) = 0;
487 : :
488 [ + + # # ]: 12 : assert(group);
489 [ + + # # ]: 12 : assert(stat);
490 : :
491 [ + + # # : 32 : for (i = 0; i < stat->num_transports; i++) {
# # ]
492 [ + + # # : 20 : STAILQ_FOREACH(tgroup, &group->tgroups, link) {
# # # # #
# # # #
# ]
493 [ + + # # : 20 : if (nvme_transport_get_trtype(tgroup->transport) == stat->transport_stat[i]->trtype) {
# # # # #
# # # # #
# # # # ]
494 [ # # # # : 20 : nvme_transport_poll_group_free_stats(tgroup, stat->transport_stat[i]);
# # # # ]
495 : 20 : freed_stats++;
496 : 20 : break;
497 : : }
498 : 0 : }
499 : 3 : }
500 : :
501 [ + + # # : 12 : assert(freed_stats == stat->num_transports);
# # # # ]
502 : :
503 [ # # # # ]: 12 : free(stat->transport_stat);
504 : 12 : free(stat);
505 : 12 : }
|