19948a064SJiri Pirko // SPDX-License-Identifier: BSD-3-Clause OR GPL-2.0
29948a064SJiri Pirko /* Copyright (c) 2017-2018 Mellanox Technologies. All rights reserved */
33f1a84e6SJiri Pirko 
43f1a84e6SJiri Pirko #include <linux/kernel.h>
53f1a84e6SJiri Pirko #include <linux/slab.h>
63f1a84e6SJiri Pirko #include <linux/list.h>
73f1a84e6SJiri Pirko #include <linux/errno.h>
899a9e7fbSAmit Cohen #include <linux/refcount.h>
93f1a84e6SJiri Pirko 
103f1a84e6SJiri Pirko #include "item.h"
113f1a84e6SJiri Pirko #include "core_acl_flex_keys.h"
123f1a84e6SJiri Pirko 
13e1da9618SIdo Schimmel /* For the purpose of the driver, define an internal storage scratchpad
14e1da9618SIdo Schimmel  * that will be used to store key/mask values. For each defined element type
15e1da9618SIdo Schimmel  * define an internal storage geometry.
16e1da9618SIdo Schimmel  *
17e1da9618SIdo Schimmel  * When adding new elements, MLXSW_AFK_ELEMENT_STORAGE_SIZE must be increased
18e1da9618SIdo Schimmel  * accordingly.
19e1da9618SIdo Schimmel  */
20e1da9618SIdo Schimmel static const struct mlxsw_afk_element_info mlxsw_afk_element_infos[] = {
21e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_U32(SRC_SYS_PORT, 0x00, 16, 16),
22e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_BUF(DMAC_32_47, 0x04, 2),
23e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_BUF(DMAC_0_31, 0x06, 4),
24e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_BUF(SMAC_32_47, 0x0A, 2),
25e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_BUF(SMAC_0_31, 0x0C, 4),
26e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_U32(ETHERTYPE, 0x00, 0, 16),
27e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_U32(IP_PROTO, 0x10, 0, 8),
28e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_U32(VID, 0x10, 8, 12),
29e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_U32(PCP, 0x10, 20, 3),
30e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_U32(TCP_FLAGS, 0x10, 23, 9),
31e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_U32(DST_L4_PORT, 0x14, 0, 16),
32e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_U32(SRC_L4_PORT, 0x14, 16, 16),
33e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_U32(IP_TTL_, 0x18, 0, 8),
34e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_U32(IP_ECN, 0x18, 9, 2),
35e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_U32(IP_DSCP, 0x18, 11, 6),
36348c976bSAmit Cohen 	MLXSW_AFK_ELEMENT_INFO_U32(VIRT_ROUTER_MSB, 0x18, 17, 4),
37348c976bSAmit Cohen 	MLXSW_AFK_ELEMENT_INFO_U32(VIRT_ROUTER_LSB, 0x18, 21, 8),
38e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_BUF(SRC_IP_96_127, 0x20, 4),
39e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_BUF(SRC_IP_64_95, 0x24, 4),
40e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_BUF(SRC_IP_32_63, 0x28, 4),
41e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_BUF(SRC_IP_0_31, 0x2C, 4),
42e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_BUF(DST_IP_96_127, 0x30, 4),
43e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_BUF(DST_IP_64_95, 0x34, 4),
44e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_BUF(DST_IP_32_63, 0x38, 4),
45e1da9618SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_BUF(DST_IP_0_31, 0x3C, 4),
46caa4c58aSIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_U32(FDB_MISS, 0x40, 0, 1),
47d65f24c9SIdo Schimmel 	MLXSW_AFK_ELEMENT_INFO_U32(L4_PORT_RANGE, 0x40, 1, 16),
48e1da9618SIdo Schimmel };
49e1da9618SIdo Schimmel 
503f1a84e6SJiri Pirko struct mlxsw_afk {
513f1a84e6SJiri Pirko 	struct list_head key_info_list;
523f1a84e6SJiri Pirko 	unsigned int max_blocks;
53c17d2083SJiri Pirko 	const struct mlxsw_afk_ops *ops;
543f1a84e6SJiri Pirko 	const struct mlxsw_afk_block *blocks;
553f1a84e6SJiri Pirko 	unsigned int blocks_count;
563f1a84e6SJiri Pirko };
573f1a84e6SJiri Pirko 
mlxsw_afk_blocks_check(struct mlxsw_afk * mlxsw_afk)583f1a84e6SJiri Pirko static bool mlxsw_afk_blocks_check(struct mlxsw_afk *mlxsw_afk)
593f1a84e6SJiri Pirko {
603f1a84e6SJiri Pirko 	int i;
613f1a84e6SJiri Pirko 	int j;
623f1a84e6SJiri Pirko 
633f1a84e6SJiri Pirko 	for (i = 0; i < mlxsw_afk->blocks_count; i++) {
643f1a84e6SJiri Pirko 		const struct mlxsw_afk_block *block = &mlxsw_afk->blocks[i];
653f1a84e6SJiri Pirko 
663f1a84e6SJiri Pirko 		for (j = 0; j < block->instances_count; j++) {
67e1da9618SIdo Schimmel 			const struct mlxsw_afk_element_info *elinfo;
683f1a84e6SJiri Pirko 			struct mlxsw_afk_element_inst *elinst;
693f1a84e6SJiri Pirko 
703f1a84e6SJiri Pirko 			elinst = &block->instances[j];
71e1da9618SIdo Schimmel 			elinfo = &mlxsw_afk_element_infos[elinst->element];
72e1da9618SIdo Schimmel 			if (elinst->type != elinfo->type ||
73ff5405f6SJiri Pirko 			    (!elinst->avoid_size_check &&
743f1a84e6SJiri Pirko 			     elinst->item.size.bits !=
75e1da9618SIdo Schimmel 			     elinfo->item.size.bits))
763f1a84e6SJiri Pirko 				return false;
773f1a84e6SJiri Pirko 		}
783f1a84e6SJiri Pirko 	}
793f1a84e6SJiri Pirko 	return true;
803f1a84e6SJiri Pirko }
813f1a84e6SJiri Pirko 
mlxsw_afk_create(unsigned int max_blocks,const struct mlxsw_afk_ops * ops)823f1a84e6SJiri Pirko struct mlxsw_afk *mlxsw_afk_create(unsigned int max_blocks,
83c17d2083SJiri Pirko 				   const struct mlxsw_afk_ops *ops)
843f1a84e6SJiri Pirko {
853f1a84e6SJiri Pirko 	struct mlxsw_afk *mlxsw_afk;
863f1a84e6SJiri Pirko 
873f1a84e6SJiri Pirko 	mlxsw_afk = kzalloc(sizeof(*mlxsw_afk), GFP_KERNEL);
883f1a84e6SJiri Pirko 	if (!mlxsw_afk)
893f1a84e6SJiri Pirko 		return NULL;
903f1a84e6SJiri Pirko 	INIT_LIST_HEAD(&mlxsw_afk->key_info_list);
913f1a84e6SJiri Pirko 	mlxsw_afk->max_blocks = max_blocks;
92c17d2083SJiri Pirko 	mlxsw_afk->ops = ops;
93c17d2083SJiri Pirko 	mlxsw_afk->blocks = ops->blocks;
94c17d2083SJiri Pirko 	mlxsw_afk->blocks_count = ops->blocks_count;
953f1a84e6SJiri Pirko 	WARN_ON(!mlxsw_afk_blocks_check(mlxsw_afk));
963f1a84e6SJiri Pirko 	return mlxsw_afk;
973f1a84e6SJiri Pirko }
983f1a84e6SJiri Pirko EXPORT_SYMBOL(mlxsw_afk_create);
993f1a84e6SJiri Pirko 
mlxsw_afk_destroy(struct mlxsw_afk * mlxsw_afk)1003f1a84e6SJiri Pirko void mlxsw_afk_destroy(struct mlxsw_afk *mlxsw_afk)
1013f1a84e6SJiri Pirko {
1023f1a84e6SJiri Pirko 	WARN_ON(!list_empty(&mlxsw_afk->key_info_list));
1033f1a84e6SJiri Pirko 	kfree(mlxsw_afk);
1043f1a84e6SJiri Pirko }
1053f1a84e6SJiri Pirko EXPORT_SYMBOL(mlxsw_afk_destroy);
1063f1a84e6SJiri Pirko 
1073f1a84e6SJiri Pirko struct mlxsw_afk_key_info {
1083f1a84e6SJiri Pirko 	struct list_head list;
10999a9e7fbSAmit Cohen 	refcount_t ref_count;
1103f1a84e6SJiri Pirko 	unsigned int blocks_count;
1113f1a84e6SJiri Pirko 	int element_to_block[MLXSW_AFK_ELEMENT_MAX]; /* index is element, value
1123f1a84e6SJiri Pirko 						      * is index inside "blocks"
1133f1a84e6SJiri Pirko 						      */
1143f1a84e6SJiri Pirko 	struct mlxsw_afk_element_usage elusage;
115e99f8e7fSGustavo A. R. Silva 	const struct mlxsw_afk_block *blocks[];
1163f1a84e6SJiri Pirko };
1173f1a84e6SJiri Pirko 
1183f1a84e6SJiri Pirko static bool
mlxsw_afk_key_info_elements_eq(struct mlxsw_afk_key_info * key_info,struct mlxsw_afk_element_usage * elusage)1193f1a84e6SJiri Pirko mlxsw_afk_key_info_elements_eq(struct mlxsw_afk_key_info *key_info,
1203f1a84e6SJiri Pirko 			       struct mlxsw_afk_element_usage *elusage)
1213f1a84e6SJiri Pirko {
1223f1a84e6SJiri Pirko 	return memcmp(&key_info->elusage, elusage, sizeof(*elusage)) == 0;
1233f1a84e6SJiri Pirko }
1243f1a84e6SJiri Pirko 
1253f1a84e6SJiri Pirko static struct mlxsw_afk_key_info *
mlxsw_afk_key_info_find(struct mlxsw_afk * mlxsw_afk,struct mlxsw_afk_element_usage * elusage)1263f1a84e6SJiri Pirko mlxsw_afk_key_info_find(struct mlxsw_afk *mlxsw_afk,
1273f1a84e6SJiri Pirko 			struct mlxsw_afk_element_usage *elusage)
1283f1a84e6SJiri Pirko {
1293f1a84e6SJiri Pirko 	struct mlxsw_afk_key_info *key_info;
1303f1a84e6SJiri Pirko 
1313f1a84e6SJiri Pirko 	list_for_each_entry(key_info, &mlxsw_afk->key_info_list, list) {
1323f1a84e6SJiri Pirko 		if (mlxsw_afk_key_info_elements_eq(key_info, elusage))
1333f1a84e6SJiri Pirko 			return key_info;
1343f1a84e6SJiri Pirko 	}
1353f1a84e6SJiri Pirko 	return NULL;
1363f1a84e6SJiri Pirko }
1373f1a84e6SJiri Pirko 
1383f1a84e6SJiri Pirko struct mlxsw_afk_picker {
1393f1a84e6SJiri Pirko 	DECLARE_BITMAP(element, MLXSW_AFK_ELEMENT_MAX);
1403f1a84e6SJiri Pirko 	unsigned int total;
1413f1a84e6SJiri Pirko };
1423f1a84e6SJiri Pirko 
mlxsw_afk_picker_count_hits(struct mlxsw_afk * mlxsw_afk,struct mlxsw_afk_picker * picker,enum mlxsw_afk_element element)1433f1a84e6SJiri Pirko static void mlxsw_afk_picker_count_hits(struct mlxsw_afk *mlxsw_afk,
1443f1a84e6SJiri Pirko 					struct mlxsw_afk_picker *picker,
1453f1a84e6SJiri Pirko 					enum mlxsw_afk_element element)
1463f1a84e6SJiri Pirko {
1473f1a84e6SJiri Pirko 	int i;
1483f1a84e6SJiri Pirko 	int j;
1493f1a84e6SJiri Pirko 
1503f1a84e6SJiri Pirko 	for (i = 0; i < mlxsw_afk->blocks_count; i++) {
1513f1a84e6SJiri Pirko 		const struct mlxsw_afk_block *block = &mlxsw_afk->blocks[i];
1523f1a84e6SJiri Pirko 
1533f1a84e6SJiri Pirko 		for (j = 0; j < block->instances_count; j++) {
1543f1a84e6SJiri Pirko 			struct mlxsw_afk_element_inst *elinst;
1553f1a84e6SJiri Pirko 
1563f1a84e6SJiri Pirko 			elinst = &block->instances[j];
157e1da9618SIdo Schimmel 			if (elinst->element == element) {
1589add5f19SIdo Schimmel 				__set_bit(element, picker[i].element);
1599add5f19SIdo Schimmel 				picker[i].total++;
1603f1a84e6SJiri Pirko 			}
1613f1a84e6SJiri Pirko 		}
1623f1a84e6SJiri Pirko 	}
1633f1a84e6SJiri Pirko }
1643f1a84e6SJiri Pirko 
mlxsw_afk_picker_subtract_hits(struct mlxsw_afk * mlxsw_afk,struct mlxsw_afk_picker * picker,int block_index)1653f1a84e6SJiri Pirko static void mlxsw_afk_picker_subtract_hits(struct mlxsw_afk *mlxsw_afk,
1663f1a84e6SJiri Pirko 					   struct mlxsw_afk_picker *picker,
1673f1a84e6SJiri Pirko 					   int block_index)
1683f1a84e6SJiri Pirko {
1693f1a84e6SJiri Pirko 	DECLARE_BITMAP(hits_element, MLXSW_AFK_ELEMENT_MAX);
1703f1a84e6SJiri Pirko 	int i;
1713f1a84e6SJiri Pirko 	int j;
1723f1a84e6SJiri Pirko 
1739add5f19SIdo Schimmel 	memcpy(&hits_element, &picker[block_index].element,
1743f1a84e6SJiri Pirko 	       sizeof(hits_element));
1753f1a84e6SJiri Pirko 
1763f1a84e6SJiri Pirko 	for (i = 0; i < mlxsw_afk->blocks_count; i++) {
1773f1a84e6SJiri Pirko 		for_each_set_bit(j, hits_element, MLXSW_AFK_ELEMENT_MAX) {
1789add5f19SIdo Schimmel 			if (__test_and_clear_bit(j, picker[i].element))
1799add5f19SIdo Schimmel 				picker[i].total--;
1803f1a84e6SJiri Pirko 		}
1813f1a84e6SJiri Pirko 	}
1823f1a84e6SJiri Pirko }
1833f1a84e6SJiri Pirko 
mlxsw_afk_picker_most_hits_get(struct mlxsw_afk * mlxsw_afk,struct mlxsw_afk_picker * picker)1843f1a84e6SJiri Pirko static int mlxsw_afk_picker_most_hits_get(struct mlxsw_afk *mlxsw_afk,
1853f1a84e6SJiri Pirko 					  struct mlxsw_afk_picker *picker)
1863f1a84e6SJiri Pirko {
1873f1a84e6SJiri Pirko 	int most_index = -EINVAL; /* Should never happen to return this */
1883f1a84e6SJiri Pirko 	int most_hits = 0;
1893f1a84e6SJiri Pirko 	int i;
1903f1a84e6SJiri Pirko 
1913f1a84e6SJiri Pirko 	for (i = 0; i < mlxsw_afk->blocks_count; i++) {
1929add5f19SIdo Schimmel 		if (picker[i].total > most_hits) {
1939add5f19SIdo Schimmel 			most_hits = picker[i].total;
1943f1a84e6SJiri Pirko 			most_index = i;
1953f1a84e6SJiri Pirko 		}
1963f1a84e6SJiri Pirko 	}
1973f1a84e6SJiri Pirko 	return most_index;
1983f1a84e6SJiri Pirko }
1993f1a84e6SJiri Pirko 
mlxsw_afk_picker_key_info_add(struct mlxsw_afk * mlxsw_afk,struct mlxsw_afk_picker * picker,int block_index,struct mlxsw_afk_key_info * key_info)2003f1a84e6SJiri Pirko static int mlxsw_afk_picker_key_info_add(struct mlxsw_afk *mlxsw_afk,
2013f1a84e6SJiri Pirko 					 struct mlxsw_afk_picker *picker,
2023f1a84e6SJiri Pirko 					 int block_index,
2033f1a84e6SJiri Pirko 					 struct mlxsw_afk_key_info *key_info)
2043f1a84e6SJiri Pirko {
2053f1a84e6SJiri Pirko 	enum mlxsw_afk_element element;
2063f1a84e6SJiri Pirko 
2073f1a84e6SJiri Pirko 	if (key_info->blocks_count == mlxsw_afk->max_blocks)
2083f1a84e6SJiri Pirko 		return -EINVAL;
2093f1a84e6SJiri Pirko 
2109add5f19SIdo Schimmel 	for_each_set_bit(element, picker[block_index].element,
2113f1a84e6SJiri Pirko 			 MLXSW_AFK_ELEMENT_MAX) {
2123f1a84e6SJiri Pirko 		key_info->element_to_block[element] = key_info->blocks_count;
2133f1a84e6SJiri Pirko 		mlxsw_afk_element_usage_add(&key_info->elusage, element);
2143f1a84e6SJiri Pirko 	}
2153f1a84e6SJiri Pirko 
2163f1a84e6SJiri Pirko 	key_info->blocks[key_info->blocks_count] =
2173f1a84e6SJiri Pirko 					&mlxsw_afk->blocks[block_index];
2183f1a84e6SJiri Pirko 	key_info->blocks_count++;
2193f1a84e6SJiri Pirko 	return 0;
2203f1a84e6SJiri Pirko }
2213f1a84e6SJiri Pirko 
mlxsw_afk_picker(struct mlxsw_afk * mlxsw_afk,struct mlxsw_afk_key_info * key_info,struct mlxsw_afk_element_usage * elusage)2223f1a84e6SJiri Pirko static int mlxsw_afk_picker(struct mlxsw_afk *mlxsw_afk,
2233f1a84e6SJiri Pirko 			    struct mlxsw_afk_key_info *key_info,
2243f1a84e6SJiri Pirko 			    struct mlxsw_afk_element_usage *elusage)
2253f1a84e6SJiri Pirko {
2263f1a84e6SJiri Pirko 	struct mlxsw_afk_picker *picker;
2273f1a84e6SJiri Pirko 	enum mlxsw_afk_element element;
2283f1a84e6SJiri Pirko 	int err;
2293f1a84e6SJiri Pirko 
2309add5f19SIdo Schimmel 	picker = kcalloc(mlxsw_afk->blocks_count, sizeof(*picker), GFP_KERNEL);
2313f1a84e6SJiri Pirko 	if (!picker)
2323f1a84e6SJiri Pirko 		return -ENOMEM;
2333f1a84e6SJiri Pirko 
2343f1a84e6SJiri Pirko 	/* Since the same elements could be present in multiple blocks,
2353f1a84e6SJiri Pirko 	 * we must find out optimal block list in order to make the
2363f1a84e6SJiri Pirko 	 * block count as low as possible.
2373f1a84e6SJiri Pirko 	 *
2383f1a84e6SJiri Pirko 	 * First, we count hits. We go over all available blocks and count
2393f1a84e6SJiri Pirko 	 * how many of requested elements are covered by each.
2403f1a84e6SJiri Pirko 	 *
2413f1a84e6SJiri Pirko 	 * Then in loop, we find block with most hits and add it to
2423f1a84e6SJiri Pirko 	 * output key_info. Then we have to subtract this block hits so
2433f1a84e6SJiri Pirko 	 * the next iteration will find most suitable block for
2443f1a84e6SJiri Pirko 	 * the rest of requested elements.
2453f1a84e6SJiri Pirko 	 */
2463f1a84e6SJiri Pirko 
2473f1a84e6SJiri Pirko 	mlxsw_afk_element_usage_for_each(element, elusage)
2483f1a84e6SJiri Pirko 		mlxsw_afk_picker_count_hits(mlxsw_afk, picker, element);
2493f1a84e6SJiri Pirko 
2503f1a84e6SJiri Pirko 	do {
2513f1a84e6SJiri Pirko 		int block_index;
2523f1a84e6SJiri Pirko 
2533f1a84e6SJiri Pirko 		block_index = mlxsw_afk_picker_most_hits_get(mlxsw_afk, picker);
2543f1a84e6SJiri Pirko 		if (block_index < 0) {
2553f1a84e6SJiri Pirko 			err = block_index;
2563f1a84e6SJiri Pirko 			goto out;
2573f1a84e6SJiri Pirko 		}
2583f1a84e6SJiri Pirko 		err = mlxsw_afk_picker_key_info_add(mlxsw_afk, picker,
2593f1a84e6SJiri Pirko 						    block_index, key_info);
2603f1a84e6SJiri Pirko 		if (err)
2613f1a84e6SJiri Pirko 			goto out;
2623f1a84e6SJiri Pirko 		mlxsw_afk_picker_subtract_hits(mlxsw_afk, picker, block_index);
2633f1a84e6SJiri Pirko 	} while (!mlxsw_afk_key_info_elements_eq(key_info, elusage));
2643f1a84e6SJiri Pirko 
2653f1a84e6SJiri Pirko 	err = 0;
2663f1a84e6SJiri Pirko out:
2673f1a84e6SJiri Pirko 	kfree(picker);
2683f1a84e6SJiri Pirko 	return err;
2693f1a84e6SJiri Pirko }
2703f1a84e6SJiri Pirko 
2713f1a84e6SJiri Pirko static struct mlxsw_afk_key_info *
mlxsw_afk_key_info_create(struct mlxsw_afk * mlxsw_afk,struct mlxsw_afk_element_usage * elusage)2723f1a84e6SJiri Pirko mlxsw_afk_key_info_create(struct mlxsw_afk *mlxsw_afk,
2733f1a84e6SJiri Pirko 			  struct mlxsw_afk_element_usage *elusage)
2743f1a84e6SJiri Pirko {
2753f1a84e6SJiri Pirko 	struct mlxsw_afk_key_info *key_info;
2763f1a84e6SJiri Pirko 	int err;
2773f1a84e6SJiri Pirko 
27878844068SGustavo A. R. Silva 	key_info = kzalloc(struct_size(key_info, blocks, mlxsw_afk->max_blocks),
27978844068SGustavo A. R. Silva 			   GFP_KERNEL);
2803f1a84e6SJiri Pirko 	if (!key_info)
2813f1a84e6SJiri Pirko 		return ERR_PTR(-ENOMEM);
2823f1a84e6SJiri Pirko 	err = mlxsw_afk_picker(mlxsw_afk, key_info, elusage);
2833f1a84e6SJiri Pirko 	if (err)
2843f1a84e6SJiri Pirko 		goto err_picker;
2853f1a84e6SJiri Pirko 	list_add(&key_info->list, &mlxsw_afk->key_info_list);
28699a9e7fbSAmit Cohen 	refcount_set(&key_info->ref_count, 1);
2873f1a84e6SJiri Pirko 	return key_info;
2883f1a84e6SJiri Pirko 
2893f1a84e6SJiri Pirko err_picker:
2903f1a84e6SJiri Pirko 	kfree(key_info);
2913f1a84e6SJiri Pirko 	return ERR_PTR(err);
2923f1a84e6SJiri Pirko }
2933f1a84e6SJiri Pirko 
mlxsw_afk_key_info_destroy(struct mlxsw_afk_key_info * key_info)2943f1a84e6SJiri Pirko static void mlxsw_afk_key_info_destroy(struct mlxsw_afk_key_info *key_info)
2953f1a84e6SJiri Pirko {
2963f1a84e6SJiri Pirko 	list_del(&key_info->list);
2973f1a84e6SJiri Pirko 	kfree(key_info);
2983f1a84e6SJiri Pirko }
2993f1a84e6SJiri Pirko 
3003f1a84e6SJiri Pirko struct mlxsw_afk_key_info *
mlxsw_afk_key_info_get(struct mlxsw_afk * mlxsw_afk,struct mlxsw_afk_element_usage * elusage)3013f1a84e6SJiri Pirko mlxsw_afk_key_info_get(struct mlxsw_afk *mlxsw_afk,
3023f1a84e6SJiri Pirko 		       struct mlxsw_afk_element_usage *elusage)
3033f1a84e6SJiri Pirko {
3043f1a84e6SJiri Pirko 	struct mlxsw_afk_key_info *key_info;
3053f1a84e6SJiri Pirko 
3063f1a84e6SJiri Pirko 	key_info = mlxsw_afk_key_info_find(mlxsw_afk, elusage);
3073f1a84e6SJiri Pirko 	if (key_info) {
30899a9e7fbSAmit Cohen 		refcount_inc(&key_info->ref_count);
3093f1a84e6SJiri Pirko 		return key_info;
3103f1a84e6SJiri Pirko 	}
3113f1a84e6SJiri Pirko 	return mlxsw_afk_key_info_create(mlxsw_afk, elusage);
3123f1a84e6SJiri Pirko }
3133f1a84e6SJiri Pirko EXPORT_SYMBOL(mlxsw_afk_key_info_get);
3143f1a84e6SJiri Pirko 
mlxsw_afk_key_info_put(struct mlxsw_afk_key_info * key_info)3153f1a84e6SJiri Pirko void mlxsw_afk_key_info_put(struct mlxsw_afk_key_info *key_info)
3163f1a84e6SJiri Pirko {
31799a9e7fbSAmit Cohen 	if (!refcount_dec_and_test(&key_info->ref_count))
3183f1a84e6SJiri Pirko 		return;
3193f1a84e6SJiri Pirko 	mlxsw_afk_key_info_destroy(key_info);
3203f1a84e6SJiri Pirko }
3213f1a84e6SJiri Pirko EXPORT_SYMBOL(mlxsw_afk_key_info_put);
3223f1a84e6SJiri Pirko 
mlxsw_afk_key_info_subset(struct mlxsw_afk_key_info * key_info,struct mlxsw_afk_element_usage * elusage)3233f1a84e6SJiri Pirko bool mlxsw_afk_key_info_subset(struct mlxsw_afk_key_info *key_info,
3243f1a84e6SJiri Pirko 			       struct mlxsw_afk_element_usage *elusage)
3253f1a84e6SJiri Pirko {
3263f1a84e6SJiri Pirko 	return mlxsw_afk_element_usage_subset(elusage, &key_info->elusage);
3273f1a84e6SJiri Pirko }
3283f1a84e6SJiri Pirko EXPORT_SYMBOL(mlxsw_afk_key_info_subset);
3293f1a84e6SJiri Pirko 
3303f1a84e6SJiri Pirko static const struct mlxsw_afk_element_inst *
mlxsw_afk_block_elinst_get(const struct mlxsw_afk_block * block,enum mlxsw_afk_element element)3313f1a84e6SJiri Pirko mlxsw_afk_block_elinst_get(const struct mlxsw_afk_block *block,
3323f1a84e6SJiri Pirko 			   enum mlxsw_afk_element element)
3333f1a84e6SJiri Pirko {
3343f1a84e6SJiri Pirko 	int i;
3353f1a84e6SJiri Pirko 
3363f1a84e6SJiri Pirko 	for (i = 0; i < block->instances_count; i++) {
3373f1a84e6SJiri Pirko 		struct mlxsw_afk_element_inst *elinst;
3383f1a84e6SJiri Pirko 
3393f1a84e6SJiri Pirko 		elinst = &block->instances[i];
340e1da9618SIdo Schimmel 		if (elinst->element == element)
3413f1a84e6SJiri Pirko 			return elinst;
3423f1a84e6SJiri Pirko 	}
3433f1a84e6SJiri Pirko 	return NULL;
3443f1a84e6SJiri Pirko }
3453f1a84e6SJiri Pirko 
3463f1a84e6SJiri Pirko static const struct mlxsw_afk_element_inst *
mlxsw_afk_key_info_elinst_get(struct mlxsw_afk_key_info * key_info,enum mlxsw_afk_element element,int * p_block_index)3473f1a84e6SJiri Pirko mlxsw_afk_key_info_elinst_get(struct mlxsw_afk_key_info *key_info,
3483f1a84e6SJiri Pirko 			      enum mlxsw_afk_element element,
3493f1a84e6SJiri Pirko 			      int *p_block_index)
3503f1a84e6SJiri Pirko {
3513f1a84e6SJiri Pirko 	const struct mlxsw_afk_element_inst *elinst;
3523f1a84e6SJiri Pirko 	const struct mlxsw_afk_block *block;
3533f1a84e6SJiri Pirko 	int block_index;
3543f1a84e6SJiri Pirko 
3553f1a84e6SJiri Pirko 	if (WARN_ON(!test_bit(element, key_info->elusage.usage)))
3563f1a84e6SJiri Pirko 		return NULL;
3573f1a84e6SJiri Pirko 	block_index = key_info->element_to_block[element];
3583f1a84e6SJiri Pirko 	block = key_info->blocks[block_index];
3593f1a84e6SJiri Pirko 
3603f1a84e6SJiri Pirko 	elinst = mlxsw_afk_block_elinst_get(block, element);
3613f1a84e6SJiri Pirko 	if (WARN_ON(!elinst))
3623f1a84e6SJiri Pirko 		return NULL;
3633f1a84e6SJiri Pirko 
3643f1a84e6SJiri Pirko 	*p_block_index = block_index;
3653f1a84e6SJiri Pirko 	return elinst;
3663f1a84e6SJiri Pirko }
3673f1a84e6SJiri Pirko 
3683f1a84e6SJiri Pirko u16
mlxsw_afk_key_info_block_encoding_get(const struct mlxsw_afk_key_info * key_info,int block_index)3693f1a84e6SJiri Pirko mlxsw_afk_key_info_block_encoding_get(const struct mlxsw_afk_key_info *key_info,
3703f1a84e6SJiri Pirko 				      int block_index)
3713f1a84e6SJiri Pirko {
3723f1a84e6SJiri Pirko 	return key_info->blocks[block_index]->encoding;
3733f1a84e6SJiri Pirko }
3743f1a84e6SJiri Pirko EXPORT_SYMBOL(mlxsw_afk_key_info_block_encoding_get);
3753f1a84e6SJiri Pirko 
3763f1a84e6SJiri Pirko unsigned int
mlxsw_afk_key_info_blocks_count_get(const struct mlxsw_afk_key_info * key_info)3773f1a84e6SJiri Pirko mlxsw_afk_key_info_blocks_count_get(const struct mlxsw_afk_key_info *key_info)
3783f1a84e6SJiri Pirko {
3793f1a84e6SJiri Pirko 	return key_info->blocks_count;
3803f1a84e6SJiri Pirko }
3813f1a84e6SJiri Pirko EXPORT_SYMBOL(mlxsw_afk_key_info_blocks_count_get);
3823f1a84e6SJiri Pirko 
mlxsw_afk_values_add_u32(struct mlxsw_afk_element_values * values,enum mlxsw_afk_element element,u32 key_value,u32 mask_value)3833f1a84e6SJiri Pirko void mlxsw_afk_values_add_u32(struct mlxsw_afk_element_values *values,
3843f1a84e6SJiri Pirko 			      enum mlxsw_afk_element element,
3853f1a84e6SJiri Pirko 			      u32 key_value, u32 mask_value)
3863f1a84e6SJiri Pirko {
3873f1a84e6SJiri Pirko 	const struct mlxsw_afk_element_info *elinfo =
3883f1a84e6SJiri Pirko 				&mlxsw_afk_element_infos[element];
3893f1a84e6SJiri Pirko 	const struct mlxsw_item *storage_item = &elinfo->item;
3903f1a84e6SJiri Pirko 
3913f1a84e6SJiri Pirko 	if (!mask_value)
3923f1a84e6SJiri Pirko 		return;
3933f1a84e6SJiri Pirko 	if (WARN_ON(elinfo->type != MLXSW_AFK_ELEMENT_TYPE_U32))
3943f1a84e6SJiri Pirko 		return;
3953f1a84e6SJiri Pirko 	__mlxsw_item_set32(values->storage.key, storage_item, 0, key_value);
3963f1a84e6SJiri Pirko 	__mlxsw_item_set32(values->storage.mask, storage_item, 0, mask_value);
3973f1a84e6SJiri Pirko 	mlxsw_afk_element_usage_add(&values->elusage, element);
3983f1a84e6SJiri Pirko }
3993f1a84e6SJiri Pirko EXPORT_SYMBOL(mlxsw_afk_values_add_u32);
4003f1a84e6SJiri Pirko 
mlxsw_afk_values_add_buf(struct mlxsw_afk_element_values * values,enum mlxsw_afk_element element,const char * key_value,const char * mask_value,unsigned int len)4013f1a84e6SJiri Pirko void mlxsw_afk_values_add_buf(struct mlxsw_afk_element_values *values,
4023f1a84e6SJiri Pirko 			      enum mlxsw_afk_element element,
4033f1a84e6SJiri Pirko 			      const char *key_value, const char *mask_value,
4043f1a84e6SJiri Pirko 			      unsigned int len)
4053f1a84e6SJiri Pirko {
4063f1a84e6SJiri Pirko 	const struct mlxsw_afk_element_info *elinfo =
4073f1a84e6SJiri Pirko 				&mlxsw_afk_element_infos[element];
4083f1a84e6SJiri Pirko 	const struct mlxsw_item *storage_item = &elinfo->item;
4093f1a84e6SJiri Pirko 
4103f1a84e6SJiri Pirko 	if (!memchr_inv(mask_value, 0, len)) /* If mask is zero */
4113f1a84e6SJiri Pirko 		return;
4123f1a84e6SJiri Pirko 	if (WARN_ON(elinfo->type != MLXSW_AFK_ELEMENT_TYPE_BUF) ||
4133f1a84e6SJiri Pirko 	    WARN_ON(elinfo->item.size.bytes != len))
4143f1a84e6SJiri Pirko 		return;
4153f1a84e6SJiri Pirko 	__mlxsw_item_memcpy_to(values->storage.key, key_value,
4163f1a84e6SJiri Pirko 			       storage_item, 0);
4173f1a84e6SJiri Pirko 	__mlxsw_item_memcpy_to(values->storage.mask, mask_value,
4183f1a84e6SJiri Pirko 			       storage_item, 0);
4193f1a84e6SJiri Pirko 	mlxsw_afk_element_usage_add(&values->elusage, element);
4203f1a84e6SJiri Pirko }
4213f1a84e6SJiri Pirko EXPORT_SYMBOL(mlxsw_afk_values_add_buf);
4223f1a84e6SJiri Pirko 
mlxsw_sp_afk_encode_u32(const struct mlxsw_item * storage_item,const struct mlxsw_item * output_item,char * storage,char * output,int diff)423a6d70a87SIdo Schimmel static void mlxsw_sp_afk_encode_u32(const struct mlxsw_item *storage_item,
424a6d70a87SIdo Schimmel 				    const struct mlxsw_item *output_item,
425511a5adcSJiri Pirko 				    char *storage, char *output, int diff)
426a6d70a87SIdo Schimmel {
427a6d70a87SIdo Schimmel 	u32 value;
428a6d70a87SIdo Schimmel 
429a6d70a87SIdo Schimmel 	value = __mlxsw_item_get32(storage, storage_item, 0);
430511a5adcSJiri Pirko 	__mlxsw_item_set32(output, output_item, 0, value + diff);
431a6d70a87SIdo Schimmel }
432a6d70a87SIdo Schimmel 
mlxsw_sp_afk_encode_buf(const struct mlxsw_item * storage_item,const struct mlxsw_item * output_item,char * storage,char * output)433a6d70a87SIdo Schimmel static void mlxsw_sp_afk_encode_buf(const struct mlxsw_item *storage_item,
434a6d70a87SIdo Schimmel 				    const struct mlxsw_item *output_item,
435a6d70a87SIdo Schimmel 				    char *storage, char *output)
436a6d70a87SIdo Schimmel {
437a6d70a87SIdo Schimmel 	char *storage_data = __mlxsw_item_data(storage, storage_item, 0);
438a6d70a87SIdo Schimmel 	char *output_data = __mlxsw_item_data(output, output_item, 0);
439a6d70a87SIdo Schimmel 	size_t len = output_item->size.bytes;
440a6d70a87SIdo Schimmel 
441a6d70a87SIdo Schimmel 	memcpy(output_data, storage_data, len);
442a6d70a87SIdo Schimmel }
443a6d70a87SIdo Schimmel 
444a6d70a87SIdo Schimmel static void
mlxsw_sp_afk_encode_one(const struct mlxsw_afk_element_inst * elinst,char * output,char * storage,int u32_diff)445a6d70a87SIdo Schimmel mlxsw_sp_afk_encode_one(const struct mlxsw_afk_element_inst *elinst,
446511a5adcSJiri Pirko 			char *output, char *storage, int u32_diff)
447a6d70a87SIdo Schimmel {
448a6d70a87SIdo Schimmel 	const struct mlxsw_item *output_item = &elinst->item;
449e1da9618SIdo Schimmel 	const struct mlxsw_afk_element_info *elinfo;
450e1da9618SIdo Schimmel 	const struct mlxsw_item *storage_item;
451a6d70a87SIdo Schimmel 
452e1da9618SIdo Schimmel 	elinfo = &mlxsw_afk_element_infos[elinst->element];
453e1da9618SIdo Schimmel 	storage_item = &elinfo->item;
454a6d70a87SIdo Schimmel 	if (elinst->type == MLXSW_AFK_ELEMENT_TYPE_U32)
455a6d70a87SIdo Schimmel 		mlxsw_sp_afk_encode_u32(storage_item, output_item,
456511a5adcSJiri Pirko 					storage, output, u32_diff);
457a6d70a87SIdo Schimmel 	else if (elinst->type == MLXSW_AFK_ELEMENT_TYPE_BUF)
458a6d70a87SIdo Schimmel 		mlxsw_sp_afk_encode_buf(storage_item, output_item,
459a6d70a87SIdo Schimmel 					storage, output);
460a6d70a87SIdo Schimmel }
461a6d70a87SIdo Schimmel 
462a6d70a87SIdo Schimmel #define MLXSW_SP_AFK_KEY_BLOCK_MAX_SIZE 16
463a6d70a87SIdo Schimmel 
mlxsw_afk_encode(struct mlxsw_afk * mlxsw_afk,struct mlxsw_afk_key_info * key_info,struct mlxsw_afk_element_values * values,char * key,char * mask)464a5995cc8SJiri Pirko void mlxsw_afk_encode(struct mlxsw_afk *mlxsw_afk,
465a5995cc8SJiri Pirko 		      struct mlxsw_afk_key_info *key_info,
4663f1a84e6SJiri Pirko 		      struct mlxsw_afk_element_values *values,
46759600844SJiri Pirko 		      char *key, char *mask)
4683f1a84e6SJiri Pirko {
46959600844SJiri Pirko 	unsigned int blocks_count =
47059600844SJiri Pirko 			mlxsw_afk_key_info_blocks_count_get(key_info);
471a6d70a87SIdo Schimmel 	char block_mask[MLXSW_SP_AFK_KEY_BLOCK_MAX_SIZE];
472a6d70a87SIdo Schimmel 	char block_key[MLXSW_SP_AFK_KEY_BLOCK_MAX_SIZE];
4733f1a84e6SJiri Pirko 	const struct mlxsw_afk_element_inst *elinst;
4743f1a84e6SJiri Pirko 	enum mlxsw_afk_element element;
475a6d70a87SIdo Schimmel 	int block_index, i;
476a6d70a87SIdo Schimmel 
47759600844SJiri Pirko 	for (i = 0; i < blocks_count; i++) {
478a6d70a87SIdo Schimmel 		memset(block_key, 0, MLXSW_SP_AFK_KEY_BLOCK_MAX_SIZE);
479a6d70a87SIdo Schimmel 		memset(block_mask, 0, MLXSW_SP_AFK_KEY_BLOCK_MAX_SIZE);
4803f1a84e6SJiri Pirko 
4813f1a84e6SJiri Pirko 		mlxsw_afk_element_usage_for_each(element, &values->elusage) {
482a6d70a87SIdo Schimmel 			elinst = mlxsw_afk_key_info_elinst_get(key_info,
483a6d70a87SIdo Schimmel 							       element,
4843f1a84e6SJiri Pirko 							       &block_index);
485a6d70a87SIdo Schimmel 			if (!elinst || block_index != i)
4863f1a84e6SJiri Pirko 				continue;
487a6d70a87SIdo Schimmel 
488a6d70a87SIdo Schimmel 			mlxsw_sp_afk_encode_one(elinst, block_key,
489511a5adcSJiri Pirko 						values->storage.key,
490511a5adcSJiri Pirko 						elinst->u32_key_diff);
491a6d70a87SIdo Schimmel 			mlxsw_sp_afk_encode_one(elinst, block_mask,
492511a5adcSJiri Pirko 						values->storage.mask, 0);
493a6d70a87SIdo Schimmel 		}
494a6d70a87SIdo Schimmel 
4953bc6f385SJiri Pirko 		mlxsw_afk->ops->encode_block(key, i, block_key);
4963bc6f385SJiri Pirko 		mlxsw_afk->ops->encode_block(mask, i, block_mask);
4973f1a84e6SJiri Pirko 	}
4983f1a84e6SJiri Pirko }
4993f1a84e6SJiri Pirko EXPORT_SYMBOL(mlxsw_afk_encode);
500b1ce60e6SJiri Pirko 
mlxsw_afk_clear(struct mlxsw_afk * mlxsw_afk,char * key,int block_start,int block_end)501b1ce60e6SJiri Pirko void mlxsw_afk_clear(struct mlxsw_afk *mlxsw_afk, char *key,
502b1ce60e6SJiri Pirko 		     int block_start, int block_end)
503b1ce60e6SJiri Pirko {
504b1ce60e6SJiri Pirko 	int i;
505b1ce60e6SJiri Pirko 
506b1ce60e6SJiri Pirko 	for (i = block_start; i <= block_end; i++)
507b1ce60e6SJiri Pirko 		mlxsw_afk->ops->clear_block(key, i);
508b1ce60e6SJiri Pirko }
509b1ce60e6SJiri Pirko EXPORT_SYMBOL(mlxsw_afk_clear);
510