xref: /dpdk/drivers/net/sfc/sfc_flow_rss.c (revision 757b0b6f207c072a550f43836856235aa41553ad)
16da67e70SIvan Malov /* SPDX-License-Identifier: BSD-3-Clause
26da67e70SIvan Malov  *
36da67e70SIvan Malov  * Copyright(c) 2022 Xilinx, Inc.
46da67e70SIvan Malov  */
56da67e70SIvan Malov 
66da67e70SIvan Malov #include <stdbool.h>
76da67e70SIvan Malov #include <stdint.h>
86da67e70SIvan Malov 
96da67e70SIvan Malov #include <rte_common.h>
106da67e70SIvan Malov #include <rte_flow.h>
116da67e70SIvan Malov #include <rte_tailq.h>
126da67e70SIvan Malov 
136da67e70SIvan Malov #include "efx.h"
146da67e70SIvan Malov 
156da67e70SIvan Malov #include "sfc.h"
166da67e70SIvan Malov #include "sfc_debug.h"
176da67e70SIvan Malov #include "sfc_flow_rss.h"
186da67e70SIvan Malov #include "sfc_log.h"
196da67e70SIvan Malov #include "sfc_rx.h"
206da67e70SIvan Malov 
216da67e70SIvan Malov int
226da67e70SIvan Malov sfc_flow_rss_attach(struct sfc_adapter *sa)
236da67e70SIvan Malov {
24f6d23053SIvan Malov 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(sa->nic);
256da67e70SIvan Malov 	struct sfc_flow_rss *flow_rss = &sa->flow_rss;
26be5cbe47SIvan Malov 	int rc;
276da67e70SIvan Malov 
286da67e70SIvan Malov 	sfc_log_init(sa, "entry");
296da67e70SIvan Malov 
30f6d23053SIvan Malov 	flow_rss->qid_span_max = encp->enc_rx_scale_indirection_max_nqueues;
31be5cbe47SIvan Malov 	flow_rss->nb_tbl_entries_min = encp->enc_rx_scale_tbl_min_nentries;
32be5cbe47SIvan Malov 	flow_rss->nb_tbl_entries_max = encp->enc_rx_scale_tbl_max_nentries;
33be5cbe47SIvan Malov 
34be5cbe47SIvan Malov 	sfc_log_init(sa, "allocate the bounce buffer for indirection entries");
35be5cbe47SIvan Malov 	flow_rss->bounce_tbl = rte_calloc("sfc_flow_rss_bounce_tbl",
36be5cbe47SIvan Malov 					  flow_rss->nb_tbl_entries_max,
37be5cbe47SIvan Malov 					  sizeof(*flow_rss->bounce_tbl), 0);
38be5cbe47SIvan Malov 	if (flow_rss->bounce_tbl == NULL) {
39be5cbe47SIvan Malov 		rc = ENOMEM;
40be5cbe47SIvan Malov 		goto fail;
41be5cbe47SIvan Malov 	}
42f6d23053SIvan Malov 
436da67e70SIvan Malov 	TAILQ_INIT(&flow_rss->ctx_list);
446da67e70SIvan Malov 
456da67e70SIvan Malov 	sfc_log_init(sa, "done");
466da67e70SIvan Malov 
476da67e70SIvan Malov 	return 0;
48be5cbe47SIvan Malov 
49be5cbe47SIvan Malov fail:
50be5cbe47SIvan Malov 	sfc_log_init(sa, "failed %d", rc);
51be5cbe47SIvan Malov 
52be5cbe47SIvan Malov 	return rc;
536da67e70SIvan Malov }
546da67e70SIvan Malov 
556da67e70SIvan Malov void
566da67e70SIvan Malov sfc_flow_rss_detach(struct sfc_adapter *sa)
576da67e70SIvan Malov {
58be5cbe47SIvan Malov 	struct sfc_flow_rss *flow_rss = &sa->flow_rss;
59be5cbe47SIvan Malov 
606da67e70SIvan Malov 	sfc_log_init(sa, "entry");
616da67e70SIvan Malov 
62be5cbe47SIvan Malov 	sfc_log_init(sa, "free the bounce buffer for indirection entries");
63be5cbe47SIvan Malov 	rte_free(flow_rss->bounce_tbl);
64be5cbe47SIvan Malov 
656da67e70SIvan Malov 	sfc_log_init(sa, "done");
666da67e70SIvan Malov }
676da67e70SIvan Malov 
686da67e70SIvan Malov int
696da67e70SIvan Malov sfc_flow_rss_parse_conf(struct sfc_adapter *sa,
706da67e70SIvan Malov 			const struct rte_flow_action_rss *in,
716da67e70SIvan Malov 			struct sfc_flow_rss_conf *out, uint16_t *sw_qid_minp)
726da67e70SIvan Malov {
736da67e70SIvan Malov 	struct sfc_adapter_shared * const sas = sfc_sa2shared(sa);
74f6d23053SIvan Malov 	const struct sfc_flow_rss *flow_rss = &sa->flow_rss;
756da67e70SIvan Malov 	const struct sfc_rss *ethdev_rss = &sas->rss;
766da67e70SIvan Malov 	uint16_t sw_qid_min;
776da67e70SIvan Malov 	uint16_t sw_qid_max;
786da67e70SIvan Malov 	const uint8_t *key;
796da67e70SIvan Malov 	unsigned int i;
806da67e70SIvan Malov 	int rc;
816da67e70SIvan Malov 
826da67e70SIvan Malov 	if (in->level) {
836da67e70SIvan Malov 		/*
846da67e70SIvan Malov 		 * The caller demands that RSS hash be computed
856da67e70SIvan Malov 		 * within the given encapsulation frame / level.
866da67e70SIvan Malov 		 * Per flow control for that is not implemented.
876da67e70SIvan Malov 		 */
886da67e70SIvan Malov 		sfc_err(sa, "flow-rss: parse: 'level' must be 0");
896da67e70SIvan Malov 		return EINVAL;
906da67e70SIvan Malov 	}
916da67e70SIvan Malov 
926da67e70SIvan Malov 	if (in->types != 0) {
936da67e70SIvan Malov 		rc = sfc_rx_hf_rte_to_efx(sa, in->types,
946da67e70SIvan Malov 					  &out->efx_hash_types);
956da67e70SIvan Malov 		if (rc != 0) {
966da67e70SIvan Malov 			sfc_err(sa, "flow-rss: parse: failed to process 'types'");
976da67e70SIvan Malov 			return rc;
986da67e70SIvan Malov 		}
996da67e70SIvan Malov 	} else {
1006da67e70SIvan Malov 		sfc_dbg(sa, "flow-rss: parse: 'types' is 0; proceeding with ethdev setting");
1016da67e70SIvan Malov 		out->efx_hash_types = ethdev_rss->hash_types;
1026da67e70SIvan Malov 	}
1036da67e70SIvan Malov 
1046da67e70SIvan Malov 	if (in->key_len != 0) {
1056da67e70SIvan Malov 		if (in->key_len != sizeof(out->key)) {
1066da67e70SIvan Malov 			sfc_err(sa, "flow-rss: parse: 'key_len' must be either %zu or 0",
1076da67e70SIvan Malov 				sizeof(out->key));
1086da67e70SIvan Malov 			return EINVAL;
1096da67e70SIvan Malov 		}
1106da67e70SIvan Malov 
1116da67e70SIvan Malov 		if (in->key == NULL) {
1126da67e70SIvan Malov 			sfc_err(sa, "flow-rss: parse: 'key' is NULL");
1136da67e70SIvan Malov 			return EINVAL;
1146da67e70SIvan Malov 		}
1156da67e70SIvan Malov 
1166da67e70SIvan Malov 		key = in->key;
1176da67e70SIvan Malov 	} else {
1186da67e70SIvan Malov 		sfc_dbg(sa, "flow-rss: parse: 'key_len' is 0; proceeding with ethdev key");
1196da67e70SIvan Malov 		key = ethdev_rss->key;
1206da67e70SIvan Malov 	}
1216da67e70SIvan Malov 
1226da67e70SIvan Malov 	rte_memcpy(out->key, key, sizeof(out->key));
1236da67e70SIvan Malov 
1246da67e70SIvan Malov 	switch (in->func) {
1256da67e70SIvan Malov 	case RTE_ETH_HASH_FUNCTION_DEFAULT:
1266da67e70SIvan Malov 		/*
1276da67e70SIvan Malov 		 * DEFAULT means that conformance to a specific
1286da67e70SIvan Malov 		 * hash algorithm is a don't care to the caller.
1296da67e70SIvan Malov 		 * The driver can pick the one it deems optimal.
1306da67e70SIvan Malov 		 */
1316da67e70SIvan Malov 		break;
1326da67e70SIvan Malov 	case RTE_ETH_HASH_FUNCTION_TOEPLITZ:
1336da67e70SIvan Malov 		if (ethdev_rss->hash_alg != EFX_RX_HASHALG_TOEPLITZ) {
1346da67e70SIvan Malov 			sfc_err(sa, "flow-rss: parse: 'func' TOEPLITZ is unavailable; use DEFAULT");
1356da67e70SIvan Malov 			return EINVAL;
1366da67e70SIvan Malov 		}
1376da67e70SIvan Malov 		break;
1386da67e70SIvan Malov 	default:
1396da67e70SIvan Malov 		sfc_err(sa, "flow-rss: parse: 'func' #%d is unsupported", in->func);
1406da67e70SIvan Malov 		return EINVAL;
1416da67e70SIvan Malov 	}
1426da67e70SIvan Malov 
14368cde2a3SIvan Malov 	out->rte_hash_function = in->func;
14468cde2a3SIvan Malov 
1456da67e70SIvan Malov 	if (in->queue_num == 0) {
1466da67e70SIvan Malov 		sfc_err(sa, "flow-rss: parse: 'queue_num' is 0; MIN=1");
1476da67e70SIvan Malov 		return EINVAL;
1486da67e70SIvan Malov 	}
1496da67e70SIvan Malov 
150be5cbe47SIvan Malov 	if (in->queue_num > flow_rss->nb_tbl_entries_max) {
1516da67e70SIvan Malov 		sfc_err(sa, "flow-rss: parse: 'queue_num' is too large; MAX=%u",
152be5cbe47SIvan Malov 			flow_rss->nb_tbl_entries_max);
1536da67e70SIvan Malov 		return EINVAL;
1546da67e70SIvan Malov 	}
1556da67e70SIvan Malov 
1566da67e70SIvan Malov 	if (in->queue == NULL) {
1576da67e70SIvan Malov 		sfc_err(sa, "flow-rss: parse: 'queue' is NULL");
1586da67e70SIvan Malov 		return EINVAL;
1596da67e70SIvan Malov 	}
1606da67e70SIvan Malov 
1616da67e70SIvan Malov 	sw_qid_min = sas->ethdev_rxq_count - 1;
1626da67e70SIvan Malov 	sw_qid_max = 0;
1636da67e70SIvan Malov 
1646da67e70SIvan Malov 	out->nb_qid_offsets = 0;
1656da67e70SIvan Malov 
1666da67e70SIvan Malov 	for (i = 0; i < in->queue_num; ++i) {
1676da67e70SIvan Malov 		uint16_t sw_qid = in->queue[i];
1686da67e70SIvan Malov 
1696da67e70SIvan Malov 		if (sw_qid >= sas->ethdev_rxq_count) {
1706da67e70SIvan Malov 			sfc_err(sa, "flow-rss: parse: queue=%u does not exist",
1716da67e70SIvan Malov 				sw_qid);
1726da67e70SIvan Malov 			return EINVAL;
1736da67e70SIvan Malov 		}
1746da67e70SIvan Malov 
1756da67e70SIvan Malov 		if (sw_qid < sw_qid_min)
1766da67e70SIvan Malov 			sw_qid_min = sw_qid;
1776da67e70SIvan Malov 
1786da67e70SIvan Malov 		if (sw_qid > sw_qid_max)
1796da67e70SIvan Malov 			sw_qid_max = sw_qid;
1806da67e70SIvan Malov 
1816da67e70SIvan Malov 		if (sw_qid != in->queue[0] + i)
1826da67e70SIvan Malov 			out->nb_qid_offsets = in->queue_num;
1836da67e70SIvan Malov 	}
1846da67e70SIvan Malov 
1856da67e70SIvan Malov 	out->qid_span = sw_qid_max - sw_qid_min + 1;
1866da67e70SIvan Malov 
187f6d23053SIvan Malov 	if (out->qid_span > flow_rss->qid_span_max) {
1886da67e70SIvan Malov 		sfc_err(sa, "flow-rss: parse: queue ID span %u is too large; MAX=%u",
189f6d23053SIvan Malov 			out->qid_span, flow_rss->qid_span_max);
1906da67e70SIvan Malov 		return EINVAL;
1916da67e70SIvan Malov 	}
1926da67e70SIvan Malov 
1936da67e70SIvan Malov 	if (sw_qid_minp != NULL)
1946da67e70SIvan Malov 		*sw_qid_minp = sw_qid_min;
1956da67e70SIvan Malov 
1966da67e70SIvan Malov 	return 0;
1976da67e70SIvan Malov }
1986da67e70SIvan Malov 
1996da67e70SIvan Malov struct sfc_flow_rss_ctx *
2006da67e70SIvan Malov sfc_flow_rss_ctx_reuse(struct sfc_adapter *sa,
2016da67e70SIvan Malov 		       const struct sfc_flow_rss_conf *conf,
2026da67e70SIvan Malov 		       uint16_t sw_qid_min, const uint16_t *sw_qids)
2036da67e70SIvan Malov {
2046da67e70SIvan Malov 	struct sfc_flow_rss *flow_rss = &sa->flow_rss;
2056da67e70SIvan Malov 	static struct sfc_flow_rss_ctx *ctx;
2066da67e70SIvan Malov 
2076da67e70SIvan Malov 	SFC_ASSERT(sfc_adapter_is_locked(sa));
2086da67e70SIvan Malov 
2096da67e70SIvan Malov 	TAILQ_FOREACH(ctx, &flow_rss->ctx_list, entries) {
2106da67e70SIvan Malov 		if (memcmp(&ctx->conf, conf, sizeof(*conf)) != 0)
2116da67e70SIvan Malov 			continue;
2126da67e70SIvan Malov 
2136da67e70SIvan Malov 		if (conf->nb_qid_offsets != 0) {
2146da67e70SIvan Malov 			bool match_confirmed = true;
2156da67e70SIvan Malov 			unsigned int i;
2166da67e70SIvan Malov 
2176da67e70SIvan Malov 			for (i = 0; i < conf->nb_qid_offsets; ++i) {
2186da67e70SIvan Malov 				uint16_t qid_offset = sw_qids[i] - sw_qid_min;
2196da67e70SIvan Malov 
2206da67e70SIvan Malov 				if (ctx->qid_offsets[i] != qid_offset) {
2216da67e70SIvan Malov 					match_confirmed = false;
2226da67e70SIvan Malov 					break;
2236da67e70SIvan Malov 				}
2246da67e70SIvan Malov 			}
2256da67e70SIvan Malov 
2266da67e70SIvan Malov 			if (!match_confirmed)
2276da67e70SIvan Malov 				continue;
2286da67e70SIvan Malov 		}
2296da67e70SIvan Malov 
2306da67e70SIvan Malov 		sfc_dbg(sa, "flow-rss: reusing ctx=%p", ctx);
2316da67e70SIvan Malov 		++(ctx->refcnt);
2326da67e70SIvan Malov 		return ctx;
2336da67e70SIvan Malov 	}
2346da67e70SIvan Malov 
2356da67e70SIvan Malov 	return NULL;
2366da67e70SIvan Malov }
2376da67e70SIvan Malov 
2386da67e70SIvan Malov int
2396da67e70SIvan Malov sfc_flow_rss_ctx_add(struct sfc_adapter *sa,
2406da67e70SIvan Malov 		     const struct sfc_flow_rss_conf *conf, uint16_t sw_qid_min,
2416da67e70SIvan Malov 		     const uint16_t *sw_qids, struct sfc_flow_rss_ctx **ctxp)
2426da67e70SIvan Malov {
2436da67e70SIvan Malov 	struct sfc_flow_rss *flow_rss = &sa->flow_rss;
2446da67e70SIvan Malov 	struct sfc_flow_rss_ctx *ctx;
2456da67e70SIvan Malov 
2466da67e70SIvan Malov 	SFC_ASSERT(sfc_adapter_is_locked(sa));
2476da67e70SIvan Malov 
2486da67e70SIvan Malov 	ctx = rte_zmalloc("sfc_flow_rss_ctx", sizeof(*ctx), 0);
2496da67e70SIvan Malov 	if (ctx == NULL)
2506da67e70SIvan Malov 		return ENOMEM;
2516da67e70SIvan Malov 
2526da67e70SIvan Malov 	if (conf->nb_qid_offsets != 0) {
2536da67e70SIvan Malov 		unsigned int i;
2546da67e70SIvan Malov 
2556da67e70SIvan Malov 		ctx->qid_offsets = rte_calloc("sfc_flow_rss_ctx_qid_offsets",
2566da67e70SIvan Malov 					      conf->nb_qid_offsets,
2576da67e70SIvan Malov 					      sizeof(*ctx->qid_offsets), 0);
2586da67e70SIvan Malov 		if (ctx->qid_offsets == NULL) {
2596da67e70SIvan Malov 			rte_free(ctx);
2606da67e70SIvan Malov 			return ENOMEM;
2616da67e70SIvan Malov 		}
2626da67e70SIvan Malov 
2636da67e70SIvan Malov 		for (i = 0; i < conf->nb_qid_offsets; ++i)
2646da67e70SIvan Malov 			ctx->qid_offsets[i] = sw_qids[i] - sw_qid_min;
2656da67e70SIvan Malov 	}
2666da67e70SIvan Malov 
2676da67e70SIvan Malov 	ctx->conf = *conf;
2686da67e70SIvan Malov 	ctx->refcnt = 1;
2696da67e70SIvan Malov 
2706da67e70SIvan Malov 	TAILQ_INSERT_TAIL(&flow_rss->ctx_list, ctx, entries);
2716da67e70SIvan Malov 
2726da67e70SIvan Malov 	*ctxp = ctx;
2736da67e70SIvan Malov 
2746da67e70SIvan Malov 	sfc_dbg(sa, "flow-rss: added ctx=%p", ctx);
2756da67e70SIvan Malov 
2766da67e70SIvan Malov 	return 0;
2776da67e70SIvan Malov }
2786da67e70SIvan Malov 
2796da67e70SIvan Malov void
2806da67e70SIvan Malov sfc_flow_rss_ctx_del(struct sfc_adapter *sa, struct sfc_flow_rss_ctx *ctx)
2816da67e70SIvan Malov {
2826da67e70SIvan Malov 	struct sfc_flow_rss *flow_rss = &sa->flow_rss;
2836da67e70SIvan Malov 
2846da67e70SIvan Malov 	if (ctx == NULL)
2856da67e70SIvan Malov 		return;
2866da67e70SIvan Malov 
2876da67e70SIvan Malov 	SFC_ASSERT(sfc_adapter_is_locked(sa));
2886da67e70SIvan Malov 
2896da67e70SIvan Malov 	if (ctx->dummy)
2906da67e70SIvan Malov 		return;
2916da67e70SIvan Malov 
2926da67e70SIvan Malov 	SFC_ASSERT(ctx->refcnt != 0);
2936da67e70SIvan Malov 
2946da67e70SIvan Malov 	--(ctx->refcnt);
2956da67e70SIvan Malov 
2966da67e70SIvan Malov 	if (ctx->refcnt != 0)
2976da67e70SIvan Malov 		return;
2986da67e70SIvan Malov 
2996da67e70SIvan Malov 	if (ctx->nic_handle_refcnt != 0) {
3006da67e70SIvan Malov 		sfc_err(sa, "flow-rss: deleting ctx=%p abandons its NIC resource: handle=0x%08x, refcnt=%u",
3016da67e70SIvan Malov 			ctx, ctx->nic_handle, ctx->nic_handle_refcnt);
3026da67e70SIvan Malov 	}
3036da67e70SIvan Malov 
3046da67e70SIvan Malov 	TAILQ_REMOVE(&flow_rss->ctx_list, ctx, entries);
3056da67e70SIvan Malov 	rte_free(ctx->qid_offsets);
3066da67e70SIvan Malov 	sfc_dbg(sa, "flow-rss: deleted ctx=%p", ctx);
307*757b0b6fSStephen Hemminger 
308*757b0b6fSStephen Hemminger 	rte_free(ctx);
3096da67e70SIvan Malov }
3106da67e70SIvan Malov 
3116da67e70SIvan Malov static int
3126da67e70SIvan Malov sfc_flow_rss_ctx_program_tbl(struct sfc_adapter *sa,
313be5cbe47SIvan Malov 			     unsigned int nb_tbl_entries,
3146da67e70SIvan Malov 			     const struct sfc_flow_rss_ctx *ctx)
3156da67e70SIvan Malov {
3166da67e70SIvan Malov 	const struct sfc_flow_rss_conf *conf = &ctx->conf;
3176da67e70SIvan Malov 	unsigned int *tbl = sa->flow_rss.bounce_tbl;
3186da67e70SIvan Malov 	unsigned int i;
3196da67e70SIvan Malov 
3206da67e70SIvan Malov 	SFC_ASSERT(sfc_adapter_is_locked(sa));
3216da67e70SIvan Malov 
32268cde2a3SIvan Malov 	if (nb_tbl_entries == 0)
32368cde2a3SIvan Malov 		return 0;
32468cde2a3SIvan Malov 
3256da67e70SIvan Malov 	if (conf->nb_qid_offsets != 0) {
3266da67e70SIvan Malov 		SFC_ASSERT(ctx->qid_offsets != NULL);
3276da67e70SIvan Malov 
328be5cbe47SIvan Malov 		for (i = 0; i < nb_tbl_entries; ++i)
3296da67e70SIvan Malov 			tbl[i] = ctx->qid_offsets[i % conf->nb_qid_offsets];
3306da67e70SIvan Malov 	} else {
331be5cbe47SIvan Malov 		for (i = 0; i < nb_tbl_entries; ++i)
3326da67e70SIvan Malov 			tbl[i] = i % conf->qid_span;
3336da67e70SIvan Malov 	}
3346da67e70SIvan Malov 
3356da67e70SIvan Malov 	return efx_rx_scale_tbl_set(sa->nic, ctx->nic_handle,
336be5cbe47SIvan Malov 				    tbl, nb_tbl_entries);
3376da67e70SIvan Malov }
3386da67e70SIvan Malov 
3396da67e70SIvan Malov int
3406da67e70SIvan Malov sfc_flow_rss_ctx_program(struct sfc_adapter *sa, struct sfc_flow_rss_ctx *ctx)
3416da67e70SIvan Malov {
3426da67e70SIvan Malov 	efx_rx_scale_context_type_t ctx_type = EFX_RX_SCALE_EXCLUSIVE;
3436da67e70SIvan Malov 	struct sfc_adapter_shared * const sas = sfc_sa2shared(sa);
34468cde2a3SIvan Malov 	const efx_nic_cfg_t *encp = efx_nic_cfg_get(sa->nic);
345be5cbe47SIvan Malov 	const struct sfc_flow_rss *flow_rss = &sa->flow_rss;
3466da67e70SIvan Malov 	struct sfc_rss *ethdev_rss = &sas->rss;
3476da67e70SIvan Malov 	struct sfc_flow_rss_conf *conf;
3486da67e70SIvan Malov 	bool allocation_done = B_FALSE;
349be5cbe47SIvan Malov 	unsigned int nb_qid_offsets;
350be5cbe47SIvan Malov 	unsigned int nb_tbl_entries;
3516da67e70SIvan Malov 	int rc;
3526da67e70SIvan Malov 
3536da67e70SIvan Malov 	if (ctx == NULL)
3546da67e70SIvan Malov 		return 0;
3556da67e70SIvan Malov 
3566da67e70SIvan Malov 	conf = &ctx->conf;
3576da67e70SIvan Malov 
3586da67e70SIvan Malov 	SFC_ASSERT(sfc_adapter_is_locked(sa));
3596da67e70SIvan Malov 
360be5cbe47SIvan Malov 	if (conf->nb_qid_offsets != 0)
361be5cbe47SIvan Malov 		nb_qid_offsets = conf->nb_qid_offsets;
362be5cbe47SIvan Malov 	else
363be5cbe47SIvan Malov 		nb_qid_offsets = conf->qid_span;
364be5cbe47SIvan Malov 
365be5cbe47SIvan Malov 	if (!RTE_IS_POWER_OF_2(nb_qid_offsets)) {
366be5cbe47SIvan Malov 		/*
367be5cbe47SIvan Malov 		 * Most likely, it pays to enlarge the indirection
368be5cbe47SIvan Malov 		 * table to facilitate better distribution quality.
369be5cbe47SIvan Malov 		 */
370be5cbe47SIvan Malov 		nb_qid_offsets = flow_rss->nb_tbl_entries_max;
371be5cbe47SIvan Malov 	}
372be5cbe47SIvan Malov 
373be5cbe47SIvan Malov 	nb_tbl_entries = RTE_MAX(flow_rss->nb_tbl_entries_min, nb_qid_offsets);
374be5cbe47SIvan Malov 
37568cde2a3SIvan Malov 	if (conf->rte_hash_function == RTE_ETH_HASH_FUNCTION_DEFAULT &&
37668cde2a3SIvan Malov 	    conf->nb_qid_offsets == 0 &&
37768cde2a3SIvan Malov 	    conf->qid_span <= encp->enc_rx_scale_even_spread_max_nqueues) {
37868cde2a3SIvan Malov 		/*
37968cde2a3SIvan Malov 		 * Conformance to a specific hash algorithm is a don't care to
38068cde2a3SIvan Malov 		 * the user. The queue array is contiguous and ascending. That
38168cde2a3SIvan Malov 		 * means that the even spread context may be requested here in
38268cde2a3SIvan Malov 		 * order to avoid wasting precious indirection table resources.
38368cde2a3SIvan Malov 		 */
38468cde2a3SIvan Malov 		ctx_type = EFX_RX_SCALE_EVEN_SPREAD;
38568cde2a3SIvan Malov 		nb_tbl_entries = 0;
38668cde2a3SIvan Malov 	}
38768cde2a3SIvan Malov 
3886da67e70SIvan Malov 	if (ctx->nic_handle_refcnt == 0) {
389be5cbe47SIvan Malov 		rc = efx_rx_scale_context_alloc_v2(sa->nic, ctx_type,
3906da67e70SIvan Malov 						   conf->qid_span,
391be5cbe47SIvan Malov 						   nb_tbl_entries,
3926da67e70SIvan Malov 						   &ctx->nic_handle);
3936da67e70SIvan Malov 		if (rc != 0) {
394be5cbe47SIvan Malov 			sfc_err(sa, "flow-rss: failed to allocate NIC resource for ctx=%p: type=%d, qid_span=%u, nb_tbl_entries=%u; rc=%d",
395be5cbe47SIvan Malov 				ctx, ctx_type, conf->qid_span, nb_tbl_entries, rc);
3966da67e70SIvan Malov 			goto fail;
3976da67e70SIvan Malov 		}
3986da67e70SIvan Malov 
399be5cbe47SIvan Malov 		sfc_dbg(sa, "flow-rss: allocated NIC resource for ctx=%p: type=%d, qid_span=%u, nb_tbl_entries=%u; handle=0x%08x",
400be5cbe47SIvan Malov 			ctx, ctx_type, conf->qid_span, nb_tbl_entries,
4016da67e70SIvan Malov 			ctx->nic_handle);
4026da67e70SIvan Malov 
4036da67e70SIvan Malov 		++(ctx->nic_handle_refcnt);
4046da67e70SIvan Malov 		allocation_done = B_TRUE;
4056da67e70SIvan Malov 	} else {
4066da67e70SIvan Malov 		++(ctx->nic_handle_refcnt);
4076da67e70SIvan Malov 		return 0;
4086da67e70SIvan Malov 	}
4096da67e70SIvan Malov 
4106da67e70SIvan Malov 	rc = efx_rx_scale_mode_set(sa->nic, ctx->nic_handle,
4116da67e70SIvan Malov 				   ethdev_rss->hash_alg,
4126da67e70SIvan Malov 				   (ctx->dummy) ? ethdev_rss->hash_types :
4136da67e70SIvan Malov 						  conf->efx_hash_types,
4146da67e70SIvan Malov 				   B_TRUE);
4156da67e70SIvan Malov 	if (rc != 0) {
4166da67e70SIvan Malov 		sfc_err(sa, "flow-rss: failed to configure hash for ctx=%p: efx_hash_alg=%d, efx_hash_types=0x%08x; rc=%d",
4176da67e70SIvan Malov 			ctx, ethdev_rss->hash_alg,
4186da67e70SIvan Malov 			(ctx->dummy) ? ethdev_rss->hash_types :
4196da67e70SIvan Malov 				       conf->efx_hash_types,
4206da67e70SIvan Malov 			rc);
4216da67e70SIvan Malov 		goto fail;
4226da67e70SIvan Malov 	}
4236da67e70SIvan Malov 
4246da67e70SIvan Malov 	rc = efx_rx_scale_key_set(sa->nic, ctx->nic_handle,
4256da67e70SIvan Malov 				  (ctx->dummy) ? ethdev_rss->key : conf->key,
4266da67e70SIvan Malov 				  RTE_DIM(conf->key));
4276da67e70SIvan Malov 	if (rc != 0) {
4286da67e70SIvan Malov 		sfc_err(sa, "flow-rss: failed to set key for ctx=%p; rc=%d",
4296da67e70SIvan Malov 			ctx, rc);
4306da67e70SIvan Malov 		goto fail;
4316da67e70SIvan Malov 	}
4326da67e70SIvan Malov 
433be5cbe47SIvan Malov 	rc = sfc_flow_rss_ctx_program_tbl(sa, nb_tbl_entries, ctx);
4346da67e70SIvan Malov 	if (rc != 0) {
435be5cbe47SIvan Malov 		sfc_err(sa, "flow-rss: failed to program table for ctx=%p: nb_tbl_entries=%u; rc=%d",
436be5cbe47SIvan Malov 			ctx, nb_tbl_entries, rc);
4376da67e70SIvan Malov 		goto fail;
4386da67e70SIvan Malov 	}
4396da67e70SIvan Malov 
4406da67e70SIvan Malov 	return 0;
4416da67e70SIvan Malov 
4426da67e70SIvan Malov fail:
4436da67e70SIvan Malov 	if (allocation_done)
4446da67e70SIvan Malov 		sfc_flow_rss_ctx_terminate(sa, ctx);
4456da67e70SIvan Malov 
4466da67e70SIvan Malov 	return rc;
4476da67e70SIvan Malov }
4486da67e70SIvan Malov 
4496da67e70SIvan Malov void
4506da67e70SIvan Malov sfc_flow_rss_ctx_terminate(struct sfc_adapter *sa, struct sfc_flow_rss_ctx *ctx)
4516da67e70SIvan Malov {
4526da67e70SIvan Malov 	if (ctx == NULL)
4536da67e70SIvan Malov 		return;
4546da67e70SIvan Malov 
4556da67e70SIvan Malov 	SFC_ASSERT(sfc_adapter_is_locked(sa));
4566da67e70SIvan Malov 
4576da67e70SIvan Malov 	SFC_ASSERT(ctx->nic_handle_refcnt != 0);
4586da67e70SIvan Malov 	--(ctx->nic_handle_refcnt);
4596da67e70SIvan Malov 
4606da67e70SIvan Malov 	if (ctx->nic_handle_refcnt == 0) {
4616da67e70SIvan Malov 		int rc;
4626da67e70SIvan Malov 
4636da67e70SIvan Malov 		rc = efx_rx_scale_context_free(sa->nic, ctx->nic_handle);
4646da67e70SIvan Malov 		if (rc != 0) {
4656da67e70SIvan Malov 			sfc_err(sa, "flow-rss: failed to release NIC resource for ctx=%p: handle=0x%08x; rc=%d",
4666da67e70SIvan Malov 				ctx, ctx->nic_handle, rc);
4676da67e70SIvan Malov 
4686da67e70SIvan Malov 			sfc_warn(sa, "flow-rss: proceeding despite the prior error");
4696da67e70SIvan Malov 		}
4706da67e70SIvan Malov 
4716da67e70SIvan Malov 		sfc_dbg(sa, "flow-rss: released NIC resource for ctx=%p; rc=%d",
4726da67e70SIvan Malov 			ctx, rc);
4736da67e70SIvan Malov 	}
4746da67e70SIvan Malov }
475