Branch data Line data Source code
1 : : /* SPDX-License-Identifier: BSD-3-Clause
2 : : *
3 : : * Copyright(c) 2022 Xilinx, Inc.
4 : : */
5 : :
6 : : #include <stdbool.h>
7 : : #include <stdint.h>
8 : :
9 : : #include <rte_common.h>
10 : : #include <rte_flow.h>
11 : : #include <rte_tailq.h>
12 : :
13 : : #include "efx.h"
14 : :
15 : : #include "sfc.h"
16 : : #include "sfc_debug.h"
17 : : #include "sfc_flow_rss.h"
18 : : #include "sfc_log.h"
19 : : #include "sfc_rx.h"
20 : :
21 : : int
22 : 0 : sfc_flow_rss_attach(struct sfc_adapter *sa)
23 : : {
24 : 0 : const efx_nic_cfg_t *encp = efx_nic_cfg_get(sa->nic);
25 : : struct sfc_flow_rss *flow_rss = &sa->flow_rss;
26 : : int rc;
27 : :
28 : 0 : sfc_log_init(sa, "entry");
29 : :
30 : 0 : flow_rss->qid_span_max = encp->enc_rx_scale_indirection_max_nqueues;
31 : 0 : flow_rss->nb_tbl_entries_min = encp->enc_rx_scale_tbl_min_nentries;
32 : 0 : flow_rss->nb_tbl_entries_max = encp->enc_rx_scale_tbl_max_nentries;
33 : :
34 : 0 : sfc_log_init(sa, "allocate the bounce buffer for indirection entries");
35 : 0 : flow_rss->bounce_tbl = rte_calloc("sfc_flow_rss_bounce_tbl",
36 : 0 : flow_rss->nb_tbl_entries_max,
37 : : sizeof(*flow_rss->bounce_tbl), 0);
38 [ # # ]: 0 : if (flow_rss->bounce_tbl == NULL) {
39 : : rc = ENOMEM;
40 : 0 : goto fail;
41 : : }
42 : :
43 : 0 : TAILQ_INIT(&flow_rss->ctx_list);
44 : :
45 : 0 : sfc_log_init(sa, "done");
46 : :
47 : 0 : return 0;
48 : :
49 : : fail:
50 : 0 : sfc_log_init(sa, "failed %d", rc);
51 : :
52 : 0 : return rc;
53 : : }
54 : :
55 : : void
56 : 0 : sfc_flow_rss_detach(struct sfc_adapter *sa)
57 : : {
58 : : struct sfc_flow_rss *flow_rss = &sa->flow_rss;
59 : :
60 : 0 : sfc_log_init(sa, "entry");
61 : :
62 : 0 : sfc_log_init(sa, "free the bounce buffer for indirection entries");
63 : 0 : rte_free(flow_rss->bounce_tbl);
64 : :
65 : 0 : sfc_log_init(sa, "done");
66 : 0 : }
67 : :
68 : : int
69 [ # # ]: 0 : sfc_flow_rss_parse_conf(struct sfc_adapter *sa,
70 : : const struct rte_flow_action_rss *in,
71 : : struct sfc_flow_rss_conf *out, uint16_t *sw_qid_minp)
72 : : {
73 : : struct sfc_adapter_shared * const sas = sfc_sa2shared(sa);
74 : : const struct sfc_flow_rss *flow_rss = &sa->flow_rss;
75 : : const struct sfc_rss *ethdev_rss = &sas->rss;
76 : : uint16_t sw_qid_min;
77 : : uint16_t sw_qid_max;
78 : : const uint8_t *key;
79 : : unsigned int i;
80 : : int rc;
81 : :
82 [ # # ]: 0 : if (in->level) {
83 : : /*
84 : : * The caller demands that RSS hash be computed
85 : : * within the given encapsulation frame / level.
86 : : * Per flow control for that is not implemented.
87 : : */
88 : 0 : sfc_err(sa, "flow-rss: parse: 'level' must be 0");
89 : 0 : return EINVAL;
90 : : }
91 : :
92 [ # # ]: 0 : if (in->types != 0) {
93 : 0 : rc = sfc_rx_hf_rte_to_efx(sa, in->types,
94 : : &out->efx_hash_types);
95 [ # # ]: 0 : if (rc != 0) {
96 : 0 : sfc_err(sa, "flow-rss: parse: failed to process 'types'");
97 : 0 : return rc;
98 : : }
99 : : } else {
100 : 0 : sfc_dbg(sa, "flow-rss: parse: 'types' is 0; proceeding with ethdev setting");
101 : 0 : out->efx_hash_types = ethdev_rss->hash_types;
102 : : }
103 : :
104 [ # # ]: 0 : if (in->key_len != 0) {
105 [ # # ]: 0 : if (in->key_len != sizeof(out->key)) {
106 : 0 : sfc_err(sa, "flow-rss: parse: 'key_len' must be either %zu or 0",
107 : : sizeof(out->key));
108 : 0 : return EINVAL;
109 : : }
110 : :
111 [ # # ]: 0 : if (in->key == NULL) {
112 : 0 : sfc_err(sa, "flow-rss: parse: 'key' is NULL");
113 : 0 : return EINVAL;
114 : : }
115 : :
116 : : key = in->key;
117 : : } else {
118 : 0 : sfc_dbg(sa, "flow-rss: parse: 'key_len' is 0; proceeding with ethdev key");
119 : 0 : key = ethdev_rss->key;
120 : : }
121 : :
122 [ # # ]: 0 : rte_memcpy(out->key, key, sizeof(out->key));
123 : :
124 [ # # # ]: 0 : switch (in->func) {
125 : : case RTE_ETH_HASH_FUNCTION_DEFAULT:
126 : : /*
127 : : * DEFAULT means that conformance to a specific
128 : : * hash algorithm is a don't care to the caller.
129 : : * The driver can pick the one it deems optimal.
130 : : */
131 : : break;
132 : 0 : case RTE_ETH_HASH_FUNCTION_TOEPLITZ:
133 [ # # ]: 0 : if (ethdev_rss->hash_alg != EFX_RX_HASHALG_TOEPLITZ) {
134 : 0 : sfc_err(sa, "flow-rss: parse: 'func' TOEPLITZ is unavailable; use DEFAULT");
135 : 0 : return EINVAL;
136 : : }
137 : : break;
138 : 0 : default:
139 : 0 : sfc_err(sa, "flow-rss: parse: 'func' #%d is unsupported", in->func);
140 : 0 : return EINVAL;
141 : : }
142 : :
143 : 0 : out->rte_hash_function = in->func;
144 : :
145 [ # # ]: 0 : if (in->queue_num == 0) {
146 : 0 : sfc_err(sa, "flow-rss: parse: 'queue_num' is 0; MIN=1");
147 : 0 : return EINVAL;
148 : : }
149 : :
150 [ # # ]: 0 : if (in->queue_num > flow_rss->nb_tbl_entries_max) {
151 : 0 : sfc_err(sa, "flow-rss: parse: 'queue_num' is too large; MAX=%u",
152 : : flow_rss->nb_tbl_entries_max);
153 : 0 : return EINVAL;
154 : : }
155 : :
156 [ # # ]: 0 : if (in->queue == NULL) {
157 : 0 : sfc_err(sa, "flow-rss: parse: 'queue' is NULL");
158 : 0 : return EINVAL;
159 : : }
160 : :
161 : 0 : sw_qid_min = sas->ethdev_rxq_count - 1;
162 : : sw_qid_max = 0;
163 : :
164 : 0 : out->nb_qid_offsets = 0;
165 : :
166 [ # # ]: 0 : for (i = 0; i < in->queue_num; ++i) {
167 : 0 : uint16_t sw_qid = in->queue[i];
168 : :
169 [ # # ]: 0 : if (sw_qid >= sas->ethdev_rxq_count) {
170 : 0 : sfc_err(sa, "flow-rss: parse: queue=%u does not exist",
171 : : sw_qid);
172 : 0 : return EINVAL;
173 : : }
174 : :
175 : : if (sw_qid < sw_qid_min)
176 : : sw_qid_min = sw_qid;
177 : :
178 : : if (sw_qid > sw_qid_max)
179 : : sw_qid_max = sw_qid;
180 : :
181 [ # # ]: 0 : if (sw_qid != in->queue[0] + i)
182 : 0 : out->nb_qid_offsets = in->queue_num;
183 : : }
184 : :
185 : 0 : out->qid_span = sw_qid_max - sw_qid_min + 1;
186 : :
187 [ # # ]: 0 : if (out->qid_span > flow_rss->qid_span_max) {
188 : 0 : sfc_err(sa, "flow-rss: parse: queue ID span %u is too large; MAX=%u",
189 : : out->qid_span, flow_rss->qid_span_max);
190 : 0 : return EINVAL;
191 : : }
192 : :
193 [ # # ]: 0 : if (sw_qid_minp != NULL)
194 : 0 : *sw_qid_minp = sw_qid_min;
195 : :
196 : : return 0;
197 : : }
198 : :
199 : : struct sfc_flow_rss_ctx *
200 : 0 : sfc_flow_rss_ctx_reuse(struct sfc_adapter *sa,
201 : : const struct sfc_flow_rss_conf *conf,
202 : : uint16_t sw_qid_min, const uint16_t *sw_qids)
203 : : {
204 : : struct sfc_flow_rss *flow_rss = &sa->flow_rss;
205 : : static struct sfc_flow_rss_ctx *ctx;
206 : :
207 : : SFC_ASSERT(sfc_adapter_is_locked(sa));
208 : :
209 [ # # ]: 0 : TAILQ_FOREACH(ctx, &flow_rss->ctx_list, entries) {
210 [ # # ]: 0 : if (memcmp(&ctx->conf, conf, sizeof(*conf)) != 0)
211 : 0 : continue;
212 : :
213 [ # # ]: 0 : if (conf->nb_qid_offsets != 0) {
214 : : bool match_confirmed = true;
215 : : unsigned int i;
216 : :
217 [ # # ]: 0 : for (i = 0; i < conf->nb_qid_offsets; ++i) {
218 : 0 : uint16_t qid_offset = sw_qids[i] - sw_qid_min;
219 : :
220 [ # # ]: 0 : if (ctx->qid_offsets[i] != qid_offset) {
221 : : match_confirmed = false;
222 : : break;
223 : : }
224 : : }
225 : :
226 [ # # ]: 0 : if (!match_confirmed)
227 : 0 : continue;
228 : : }
229 : :
230 : 0 : sfc_dbg(sa, "flow-rss: reusing ctx=%p", ctx);
231 : 0 : ++(ctx->refcnt);
232 : 0 : return ctx;
233 : : }
234 : :
235 : : return NULL;
236 : : }
237 : :
238 : : int
239 : 0 : sfc_flow_rss_ctx_add(struct sfc_adapter *sa,
240 : : const struct sfc_flow_rss_conf *conf, uint16_t sw_qid_min,
241 : : const uint16_t *sw_qids, struct sfc_flow_rss_ctx **ctxp)
242 : : {
243 : : struct sfc_flow_rss *flow_rss = &sa->flow_rss;
244 : : struct sfc_flow_rss_ctx *ctx;
245 : :
246 : : SFC_ASSERT(sfc_adapter_is_locked(sa));
247 : :
248 : 0 : ctx = rte_zmalloc("sfc_flow_rss_ctx", sizeof(*ctx), 0);
249 [ # # ]: 0 : if (ctx == NULL)
250 : : return ENOMEM;
251 : :
252 [ # # ]: 0 : if (conf->nb_qid_offsets != 0) {
253 : : unsigned int i;
254 : :
255 : 0 : ctx->qid_offsets = rte_calloc("sfc_flow_rss_ctx_qid_offsets",
256 : : conf->nb_qid_offsets,
257 : : sizeof(*ctx->qid_offsets), 0);
258 [ # # ]: 0 : if (ctx->qid_offsets == NULL) {
259 : 0 : rte_free(ctx);
260 : 0 : return ENOMEM;
261 : : }
262 : :
263 [ # # ]: 0 : for (i = 0; i < conf->nb_qid_offsets; ++i)
264 : 0 : ctx->qid_offsets[i] = sw_qids[i] - sw_qid_min;
265 : : }
266 : :
267 : 0 : ctx->conf = *conf;
268 : 0 : ctx->refcnt = 1;
269 : :
270 : 0 : TAILQ_INSERT_TAIL(&flow_rss->ctx_list, ctx, entries);
271 : :
272 : 0 : *ctxp = ctx;
273 : :
274 : 0 : sfc_dbg(sa, "flow-rss: added ctx=%p", ctx);
275 : :
276 : 0 : return 0;
277 : : }
278 : :
279 : : void
280 : 0 : sfc_flow_rss_ctx_del(struct sfc_adapter *sa, struct sfc_flow_rss_ctx *ctx)
281 : : {
282 : : struct sfc_flow_rss *flow_rss = &sa->flow_rss;
283 : :
284 [ # # ]: 0 : if (ctx == NULL)
285 : : return;
286 : :
287 : : SFC_ASSERT(sfc_adapter_is_locked(sa));
288 : :
289 [ # # ]: 0 : if (ctx->dummy)
290 : : return;
291 : :
292 : : SFC_ASSERT(ctx->refcnt != 0);
293 : :
294 : 0 : --(ctx->refcnt);
295 : :
296 [ # # ]: 0 : if (ctx->refcnt != 0)
297 : : return;
298 : :
299 [ # # ]: 0 : if (ctx->nic_handle_refcnt != 0) {
300 : 0 : sfc_err(sa, "flow-rss: deleting ctx=%p abandons its NIC resource: handle=0x%08x, refcnt=%u",
301 : : ctx, ctx->nic_handle, ctx->nic_handle_refcnt);
302 : : }
303 : :
304 [ # # ]: 0 : TAILQ_REMOVE(&flow_rss->ctx_list, ctx, entries);
305 : 0 : rte_free(ctx->qid_offsets);
306 : 0 : sfc_dbg(sa, "flow-rss: deleted ctx=%p", ctx);
307 : :
308 : 0 : rte_free(ctx);
309 : : }
310 : :
311 : : static int
312 : 0 : sfc_flow_rss_ctx_program_tbl(struct sfc_adapter *sa,
313 : : unsigned int nb_tbl_entries,
314 : : const struct sfc_flow_rss_ctx *ctx)
315 : : {
316 : : const struct sfc_flow_rss_conf *conf = &ctx->conf;
317 : 0 : unsigned int *tbl = sa->flow_rss.bounce_tbl;
318 : : unsigned int i;
319 : :
320 : : SFC_ASSERT(sfc_adapter_is_locked(sa));
321 : :
322 [ # # ]: 0 : if (nb_tbl_entries == 0)
323 : : return 0;
324 : :
325 [ # # ]: 0 : if (conf->nb_qid_offsets != 0) {
326 : : SFC_ASSERT(ctx->qid_offsets != NULL);
327 : :
328 [ # # ]: 0 : for (i = 0; i < nb_tbl_entries; ++i)
329 : 0 : tbl[i] = ctx->qid_offsets[i % conf->nb_qid_offsets];
330 : : } else {
331 [ # # ]: 0 : for (i = 0; i < nb_tbl_entries; ++i)
332 : 0 : tbl[i] = i % conf->qid_span;
333 : : }
334 : :
335 : 0 : return efx_rx_scale_tbl_set(sa->nic, ctx->nic_handle,
336 : : tbl, nb_tbl_entries);
337 : : }
338 : :
339 : : int
340 : 0 : sfc_flow_rss_ctx_program(struct sfc_adapter *sa, struct sfc_flow_rss_ctx *ctx)
341 : : {
342 : : efx_rx_scale_context_type_t ctx_type = EFX_RX_SCALE_EXCLUSIVE;
343 : : struct sfc_adapter_shared * const sas = sfc_sa2shared(sa);
344 : 0 : const efx_nic_cfg_t *encp = efx_nic_cfg_get(sa->nic);
345 : : const struct sfc_flow_rss *flow_rss = &sa->flow_rss;
346 : : struct sfc_rss *ethdev_rss = &sas->rss;
347 : : struct sfc_flow_rss_conf *conf;
348 : : bool allocation_done = B_FALSE;
349 : : unsigned int nb_qid_offsets;
350 : : unsigned int nb_tbl_entries;
351 : : int rc;
352 : :
353 [ # # ]: 0 : if (ctx == NULL)
354 : : return 0;
355 : :
356 : : conf = &ctx->conf;
357 : :
358 : : SFC_ASSERT(sfc_adapter_is_locked(sa));
359 : :
360 [ # # ]: 0 : if (conf->nb_qid_offsets != 0)
361 : : nb_qid_offsets = conf->nb_qid_offsets;
362 : : else
363 : 0 : nb_qid_offsets = conf->qid_span;
364 : :
365 [ # # # # ]: 0 : if (!RTE_IS_POWER_OF_2(nb_qid_offsets)) {
366 : : /*
367 : : * Most likely, it pays to enlarge the indirection
368 : : * table to facilitate better distribution quality.
369 : : */
370 : 0 : nb_qid_offsets = flow_rss->nb_tbl_entries_max;
371 : : }
372 : :
373 : 0 : nb_tbl_entries = RTE_MAX(flow_rss->nb_tbl_entries_min, nb_qid_offsets);
374 : :
375 [ # # # # ]: 0 : if (conf->rte_hash_function == RTE_ETH_HASH_FUNCTION_DEFAULT &&
376 : 0 : conf->nb_qid_offsets == 0 &&
377 [ # # ]: 0 : conf->qid_span <= encp->enc_rx_scale_even_spread_max_nqueues) {
378 : : /*
379 : : * Conformance to a specific hash algorithm is a don't care to
380 : : * the user. The queue array is contiguous and ascending. That
381 : : * means that the even spread context may be requested here in
382 : : * order to avoid wasting precious indirection table resources.
383 : : */
384 : : ctx_type = EFX_RX_SCALE_EVEN_SPREAD;
385 : : nb_tbl_entries = 0;
386 : : }
387 : :
388 [ # # ]: 0 : if (ctx->nic_handle_refcnt == 0) {
389 : 0 : rc = efx_rx_scale_context_alloc_v2(sa->nic, ctx_type,
390 : : conf->qid_span,
391 : : nb_tbl_entries,
392 : : &ctx->nic_handle);
393 [ # # ]: 0 : if (rc != 0) {
394 : 0 : sfc_err(sa, "flow-rss: failed to allocate NIC resource for ctx=%p: type=%d, qid_span=%u, nb_tbl_entries=%u; rc=%d",
395 : : ctx, ctx_type, conf->qid_span, nb_tbl_entries, rc);
396 : 0 : goto fail;
397 : : }
398 : :
399 : 0 : sfc_dbg(sa, "flow-rss: allocated NIC resource for ctx=%p: type=%d, qid_span=%u, nb_tbl_entries=%u; handle=0x%08x",
400 : : ctx, ctx_type, conf->qid_span, nb_tbl_entries,
401 : : ctx->nic_handle);
402 : :
403 : 0 : ++(ctx->nic_handle_refcnt);
404 : : allocation_done = B_TRUE;
405 : : } else {
406 : 0 : ++(ctx->nic_handle_refcnt);
407 : 0 : return 0;
408 : : }
409 : :
410 : 0 : rc = efx_rx_scale_mode_set(sa->nic, ctx->nic_handle,
411 : : ethdev_rss->hash_alg,
412 [ # # ]: 0 : (ctx->dummy) ? ethdev_rss->hash_types :
413 : : conf->efx_hash_types,
414 : : B_TRUE);
415 [ # # ]: 0 : if (rc != 0) {
416 [ # # ]: 0 : sfc_err(sa, "flow-rss: failed to configure hash for ctx=%p: efx_hash_alg=%d, efx_hash_types=0x%08x; rc=%d",
417 : : ctx, ethdev_rss->hash_alg,
418 : : (ctx->dummy) ? ethdev_rss->hash_types :
419 : : conf->efx_hash_types,
420 : : rc);
421 : 0 : goto fail;
422 : : }
423 : :
424 : 0 : rc = efx_rx_scale_key_set(sa->nic, ctx->nic_handle,
425 [ # # ]: 0 : (ctx->dummy) ? ethdev_rss->key : conf->key,
426 : : RTE_DIM(conf->key));
427 [ # # ]: 0 : if (rc != 0) {
428 : 0 : sfc_err(sa, "flow-rss: failed to set key for ctx=%p; rc=%d",
429 : : ctx, rc);
430 : 0 : goto fail;
431 : : }
432 : :
433 : 0 : rc = sfc_flow_rss_ctx_program_tbl(sa, nb_tbl_entries, ctx);
434 [ # # ]: 0 : if (rc != 0) {
435 : 0 : sfc_err(sa, "flow-rss: failed to program table for ctx=%p: nb_tbl_entries=%u; rc=%d",
436 : : ctx, nb_tbl_entries, rc);
437 : 0 : goto fail;
438 : : }
439 : :
440 : : return 0;
441 : :
442 : : fail:
443 : : if (allocation_done)
444 : 0 : sfc_flow_rss_ctx_terminate(sa, ctx);
445 : :
446 : : return rc;
447 : : }
448 : :
449 : : void
450 : 0 : sfc_flow_rss_ctx_terminate(struct sfc_adapter *sa, struct sfc_flow_rss_ctx *ctx)
451 : : {
452 [ # # ]: 0 : if (ctx == NULL)
453 : : return;
454 : :
455 : : SFC_ASSERT(sfc_adapter_is_locked(sa));
456 : :
457 : : SFC_ASSERT(ctx->nic_handle_refcnt != 0);
458 : 0 : --(ctx->nic_handle_refcnt);
459 : :
460 [ # # ]: 0 : if (ctx->nic_handle_refcnt == 0) {
461 : : int rc;
462 : :
463 : 0 : rc = efx_rx_scale_context_free(sa->nic, ctx->nic_handle);
464 [ # # ]: 0 : if (rc != 0) {
465 : 0 : sfc_err(sa, "flow-rss: failed to release NIC resource for ctx=%p: handle=0x%08x; rc=%d",
466 : : ctx, ctx->nic_handle, rc);
467 : :
468 : 0 : sfc_warn(sa, "flow-rss: proceeding despite the prior error");
469 : : }
470 : :
471 : 0 : sfc_dbg(sa, "flow-rss: released NIC resource for ctx=%p; rc=%d",
472 : : ctx, rc);
473 : : }
474 : : }
|