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 : 1995 : spdk_nvme_poll_group_create(void *ctx, struct spdk_nvme_accel_fn_table *table)
12 : : {
13 : : struct spdk_nvme_poll_group *group;
14 : :
15 : 1995 : group = calloc(1, sizeof(*group));
16 [ + + ]: 1995 : if (group == NULL) {
17 : 4 : return NULL;
18 : : }
19 : :
20 [ + - + - : 1991 : group->accel_fn_table.table_size = sizeof(struct spdk_nvme_accel_fn_table);
+ - ]
21 [ + + + - : 1991 : if (table && table->table_size != 0) {
+ - - + ]
22 [ + - + - : 1779 : group->accel_fn_table.table_size = table->table_size;
+ - + - +
- ]
23 : : #define SET_FIELD(field) \
24 : : if (offsetof(struct spdk_nvme_accel_fn_table, field) + sizeof(table->field) <= table->table_size) { \
25 : : group->accel_fn_table.field = table->field; \
26 : : } \
27 : :
28 [ + + + - : 1779 : SET_FIELD(append_crc32c);
- + + - +
- + - + -
+ - ]
29 [ + + + - : 1779 : SET_FIELD(append_copy);
- + + - +
- + - + -
+ - ]
30 [ + + + - : 1779 : SET_FIELD(finish_sequence);
- + + - +
- + - + -
+ - ]
31 [ + + + - : 1779 : SET_FIELD(reverse_sequence);
- + + - +
- + - + -
+ - ]
32 [ + + + - : 1779 : SET_FIELD(abort_sequence);
- + + - +
- + - + -
+ - ]
33 : : /* Do not remove this statement, you should always update this statement when you adding a new field,
34 : : * and do not forget to add the SET_FIELD statement for your added field. */
35 : : SPDK_STATIC_ASSERT(sizeof(struct spdk_nvme_accel_fn_table) == 56, "Incorrect size");
36 : :
37 : : #undef SET_FIELD
38 : 21 : }
39 : :
40 : : /* Make sure either all or none of the sequence manipulation callbacks are implemented */
41 [ + + + - : 3760 : if ((group->accel_fn_table.finish_sequence && group->accel_fn_table.reverse_sequence &&
+ + + - +
- + - + -
- + - + ]
42 [ + - + + : 3770 : group->accel_fn_table.abort_sequence) !=
+ - ]
43 [ + + + + : 2194 : (group->accel_fn_table.finish_sequence || group->accel_fn_table.reverse_sequence ||
+ - - + #
# # # # #
# # ]
44 [ - + # # : 212 : group->accel_fn_table.abort_sequence)) {
# # ]
45 : 0 : SPDK_ERRLOG("Invalid accel_fn_table configuration: either all or none of the "
46 : : "sequence callbacks must be provided\n");
47 : 0 : free(group);
48 : 0 : return NULL;
49 : : }
50 : :
51 : : /* Make sure that sequence callbacks are implemented if append* callbacks are provided */
52 [ + + + + : 1992 : if ((group->accel_fn_table.append_crc32c || group->accel_fn_table.append_copy) &&
+ - - + #
# # # # #
+ - ]
53 [ + + + - : 1788 : !group->accel_fn_table.finish_sequence) {
+ - ]
54 : 0 : SPDK_ERRLOG("Invalid accel_fn_table configuration: append_crc32c and/or append_copy require sequence "
55 : : "callbacks to be provided\n");
56 : 0 : free(group);
57 : 0 : return NULL;
58 : : }
59 : :
60 [ + - + - ]: 1991 : group->ctx = ctx;
61 [ + - + - : 1991 : STAILQ_INIT(&group->tgroups);
+ - + - +
- + - + -
+ - ]
62 : :
63 : 1991 : return group;
64 : 31 : }
65 : :
66 : : struct spdk_nvme_poll_group *
67 : 0 : spdk_nvme_qpair_get_optimal_poll_group(struct spdk_nvme_qpair *qpair)
68 : : {
69 : : struct spdk_nvme_transport_poll_group *tgroup;
70 : :
71 [ # # # # ]: 0 : tgroup = nvme_transport_qpair_get_optimal_poll_group(qpair->transport, qpair);
72 : :
73 [ # # ]: 0 : if (tgroup == NULL) {
74 : 0 : return NULL;
75 : : }
76 : :
77 [ # # # # ]: 0 : return tgroup->group;
78 : 0 : }
79 : :
80 : : int
81 : 2233 : spdk_nvme_poll_group_add(struct spdk_nvme_poll_group *group, struct spdk_nvme_qpair *qpair)
82 : : {
83 : : struct spdk_nvme_transport_poll_group *tgroup;
84 : : const struct spdk_nvme_transport *transport;
85 : :
86 [ + + ]: 2233 : if (nvme_qpair_get_state(qpair) != NVME_QPAIR_DISCONNECTED) {
87 : 4 : return -EINVAL;
88 : : }
89 : :
90 [ + + + - : 2269 : STAILQ_FOREACH(tgroup, &group->tgroups, link) {
+ - + - #
# # # #
# ]
91 [ + + # # : 278 : if (tgroup->transport == qpair->transport) {
# # # # #
# ]
92 : 238 : break;
93 : : }
94 : 10 : }
95 : :
96 : : /* See if a new transport has been added (dlopen style) and we need to update the poll group */
97 [ + + ]: 2229 : if (!tgroup) {
98 : 1991 : transport = nvme_get_first_transport();
99 [ + + ]: 3055 : while (transport != NULL) {
100 [ + + + - : 3047 : if (transport == qpair->transport) {
- + ]
101 : 1983 : tgroup = nvme_transport_poll_group_create(transport);
102 [ + + ]: 1983 : if (tgroup == NULL) {
103 : 0 : return -ENOMEM;
104 : : }
105 [ + - + - ]: 1983 : tgroup->group = group;
106 [ + - + - : 1983 : STAILQ_INSERT_TAIL(&group->tgroups, tgroup, link);
+ - + - +
- + - + -
+ - + - +
- + - +
- ]
107 : 1983 : break;
108 : : }
109 : 1064 : transport = nvme_get_next_transport(transport);
110 : : }
111 : 30 : }
112 : :
113 [ + + ]: 2229 : return tgroup ? nvme_transport_poll_group_add(tgroup, qpair) : -ENODEV;
114 : 34 : }
115 : :
116 : : int
117 : 2221 : spdk_nvme_poll_group_remove(struct spdk_nvme_poll_group *group, struct spdk_nvme_qpair *qpair)
118 : : {
119 : : struct spdk_nvme_transport_poll_group *tgroup;
120 : :
121 [ + + + - : 2253 : STAILQ_FOREACH(tgroup, &group->tgroups, link) {
+ - + - #
# # # #
# ]
122 [ + + + - : 2249 : if (tgroup->transport == qpair->transport) {
+ - + - -
+ ]
123 : 2217 : return nvme_transport_poll_group_remove(tgroup, qpair);
124 : : }
125 : 8 : }
126 : :
127 : 4 : return -ENODEV;
128 : 31 : }
129 : :
130 : : int
131 : 2830 : nvme_poll_group_connect_qpair(struct spdk_nvme_qpair *qpair)
132 : : {
133 : 2830 : return nvme_transport_poll_group_connect_qpair(qpair);
134 : : }
135 : :
136 : : int
137 : 2193 : nvme_poll_group_disconnect_qpair(struct spdk_nvme_qpair *qpair)
138 : : {
139 : 2193 : return nvme_transport_poll_group_disconnect_qpair(qpair);
140 : : }
141 : :
142 : : int64_t
143 : 1204476807 : spdk_nvme_poll_group_process_completions(struct spdk_nvme_poll_group *group,
144 : : uint32_t completions_per_qpair, spdk_nvme_disconnected_qpair_cb disconnected_qpair_cb)
145 : : {
146 : : struct spdk_nvme_transport_poll_group *tgroup;
147 : 1204476807 : int64_t local_completions = 0, error_reason = 0, num_completions = 0;
148 : :
149 [ + + ]: 1204476807 : if (disconnected_qpair_cb == NULL) {
150 : 0 : return -EINVAL;
151 : : }
152 : :
153 [ + + + + : 1204476807 : if (spdk_unlikely(group->in_process_completions)) {
+ - - + ]
154 : 1261242 : return 0;
155 : : }
156 [ + - + - ]: 1203215565 : group->in_process_completions = true;
157 : :
158 [ + + + - : 2406431134 : STAILQ_FOREACH(tgroup, &group->tgroups, link) {
+ - + + +
- + - +
- ]
159 : 1213190135 : local_completions = nvme_transport_poll_group_process_completions(tgroup, completions_per_qpair,
160 : 9974566 : disconnected_qpair_cb);
161 [ + + + - ]: 1203215569 : if (local_completions < 0 && error_reason == 0) {
162 : 31 : error_reason = local_completions;
163 : 0 : } else {
164 [ + - ]: 1203215538 : num_completions += local_completions;
165 : : /* Just to be safe */
166 [ + + # # ]: 1203215538 : assert(num_completions >= 0);
167 : : }
168 : 9974566 : }
169 [ + - + - ]: 1203215565 : group->in_process_completions = false;
170 : :
171 [ + + ]: 1203215565 : return error_reason ? error_reason : num_completions;
172 : 9974559 : }
173 : :
174 : : int
175 : 297095 : spdk_nvme_poll_group_all_connected(struct spdk_nvme_poll_group *group)
176 : : {
177 : : struct spdk_nvme_transport_poll_group *tgroup;
178 : : struct spdk_nvme_qpair *qpair;
179 : 297095 : int rc = 0;
180 : :
181 [ + + # # : 594190 : STAILQ_FOREACH(tgroup, &group->tgroups, link) {
# # # # #
# # # #
# ]
182 [ + + # # : 297095 : if (!STAILQ_EMPTY(&tgroup->disconnected_qpairs)) {
# # # # ]
183 : : /* Treat disconnected qpairs as highest priority for notification.
184 : : * This means we can just return immediately here.
185 : : */
186 : 0 : return -EIO;
187 : : }
188 [ + + # # : 297710 : STAILQ_FOREACH(qpair, &tgroup->connected_qpairs, poll_group_stailq) {
# # # # #
# # # #
# ]
189 [ - + ]: 297526 : if (nvme_qpair_get_state(qpair) < NVME_QPAIR_CONNECTING) {
190 : 0 : return -EIO;
191 [ + + ]: 297526 : } else if (nvme_qpair_get_state(qpair) == NVME_QPAIR_CONNECTING) {
192 : 296911 : rc = -EAGAIN;
193 : : /* Break so that we can check the remaining transport groups,
194 : : * in case any of them have a disconnected qpair.
195 : : */
196 : 296911 : break;
197 : : }
198 : 2 : }
199 : 14 : }
200 : :
201 : 297095 : return rc;
202 : 14 : }
203 : :
204 : : void *
205 : 0 : spdk_nvme_poll_group_get_ctx(struct spdk_nvme_poll_group *group)
206 : : {
207 [ # # # # ]: 0 : return group->ctx;
208 : : }
209 : :
210 : : int
211 : 1995 : spdk_nvme_poll_group_destroy(struct spdk_nvme_poll_group *group)
212 : : {
213 : : struct spdk_nvme_transport_poll_group *tgroup, *tmp_tgroup;
214 : :
215 [ + + + + : 3962 : STAILQ_FOREACH_SAFE(tgroup, &group->tgroups, link, tmp_tgroup) {
+ - + + +
- + - + -
+ + ]
216 [ + + + + : 1971 : STAILQ_REMOVE(&group->tgroups, tgroup, spdk_nvme_transport_poll_group, link);
+ - - + +
- + - + -
+ - + - +
- + - + -
+ - - + +
- + - + -
+ - + - #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # #
# # # # ]
217 [ + + ]: 1971 : if (nvme_transport_poll_group_destroy(tgroup) != 0) {
218 [ # # # # : 4 : STAILQ_INSERT_TAIL(&group->tgroups, tgroup, link);
# # # # #
# # # # #
# # # # #
# # # #
# ]
219 : 4 : return -EBUSY;
220 : : }
221 : :
222 : 24 : }
223 : :
224 : 1991 : free(group);
225 : :
226 : 1991 : return 0;
227 : 31 : }
228 : :
229 : : int
230 : 14 : spdk_nvme_poll_group_get_stats(struct spdk_nvme_poll_group *group,
231 : : struct spdk_nvme_poll_group_stat **stats)
232 : : {
233 : : struct spdk_nvme_transport_poll_group *tgroup;
234 : : struct spdk_nvme_poll_group_stat *result;
235 : 14 : uint32_t transports_count = 0;
236 : : /* Not all transports used by this poll group may support statistics reporting */
237 : 14 : uint32_t reported_stats_count = 0;
238 : : int rc;
239 : :
240 [ + + # # ]: 14 : assert(group);
241 [ - + # # ]: 14 : assert(stats);
242 : :
243 : 14 : result = calloc(1, sizeof(*result));
244 [ + + ]: 14 : if (!result) {
245 : 0 : SPDK_ERRLOG("Failed to allocate memory for poll group statistics\n");
246 : 0 : return -ENOMEM;
247 : : }
248 : :
249 [ + + # # : 32 : STAILQ_FOREACH(tgroup, &group->tgroups, link) {
# # # # #
# # # #
# ]
250 : 18 : transports_count++;
251 : 3 : }
252 : :
253 [ # # # # ]: 14 : result->transport_stat = calloc(transports_count, sizeof(*result->transport_stat));
254 [ - + # # : 14 : if (!result->transport_stat) {
# # ]
255 : 0 : SPDK_ERRLOG("Failed to allocate memory for poll group statistics\n");
256 : 0 : free(result);
257 : 0 : return -ENOMEM;
258 : : }
259 : :
260 [ + + # # : 32 : STAILQ_FOREACH(tgroup, &group->tgroups, link) {
# # # # #
# # # #
# ]
261 [ # # # # : 18 : rc = nvme_transport_poll_group_get_stats(tgroup, &result->transport_stat[reported_stats_count]);
# # ]
262 [ + + ]: 18 : if (rc == 0) {
263 : 18 : reported_stats_count++;
264 : 3 : }
265 : 3 : }
266 : :
267 [ + + ]: 14 : if (reported_stats_count == 0) {
268 [ # # # # ]: 4 : free(result->transport_stat);
269 : 4 : free(result);
270 [ + + - + : 4 : SPDK_DEBUGLOG(nvme, "No transport statistics available\n");
# # ]
271 : 4 : return -ENOTSUP;
272 : : }
273 : :
274 [ # # # # ]: 10 : result->num_transports = reported_stats_count;
275 [ # # ]: 10 : *stats = result;
276 : :
277 : 10 : return 0;
278 : 2 : }
279 : :
280 : : void
281 : 10 : spdk_nvme_poll_group_free_stats(struct spdk_nvme_poll_group *group,
282 : : struct spdk_nvme_poll_group_stat *stat)
283 : : {
284 : : struct spdk_nvme_transport_poll_group *tgroup;
285 : : uint32_t i;
286 : 10 : uint32_t freed_stats __attribute__((unused)) = 0;
287 : :
288 [ + + # # ]: 10 : assert(group);
289 [ + + # # ]: 10 : assert(stat);
290 : :
291 [ + + # # : 28 : for (i = 0; i < stat->num_transports; i++) {
# # ]
292 [ + + # # : 18 : STAILQ_FOREACH(tgroup, &group->tgroups, link) {
# # # # #
# # # #
# ]
293 [ + + # # : 18 : if (nvme_transport_get_trtype(tgroup->transport) == stat->transport_stat[i]->trtype) {
# # # # #
# # # # #
# # # # ]
294 [ # # # # : 18 : nvme_transport_poll_group_free_stats(tgroup, stat->transport_stat[i]);
# # # # ]
295 : 18 : freed_stats++;
296 : 18 : break;
297 : : }
298 : 0 : }
299 : 3 : }
300 : :
301 [ + + # # : 10 : assert(freed_stats == stat->num_transports);
# # # # ]
302 : :
303 [ # # # # ]: 10 : free(stat->transport_stat);
304 : 10 : free(stat);
305 : 10 : }
|