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