162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include <linux/ethtool_netlink.h>
462306a36Sopenharmony_ci#include <linux/bitmap.h>
562306a36Sopenharmony_ci#include "netlink.h"
662306a36Sopenharmony_ci#include "bitset.h"
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci/* Some bitmaps are internally represented as an array of unsigned long, some
962306a36Sopenharmony_ci * as an array of u32 (some even as single u32 for now). To avoid the need of
1062306a36Sopenharmony_ci * wrappers on caller side, we provide two set of functions: those with "32"
1162306a36Sopenharmony_ci * suffix in their names expect u32 based bitmaps, those without it expect
1262306a36Sopenharmony_ci * unsigned long bitmaps.
1362306a36Sopenharmony_ci */
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_cistatic u32 ethnl_lower_bits(unsigned int n)
1662306a36Sopenharmony_ci{
1762306a36Sopenharmony_ci	return ~(u32)0 >> (32 - n % 32);
1862306a36Sopenharmony_ci}
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_cistatic u32 ethnl_upper_bits(unsigned int n)
2162306a36Sopenharmony_ci{
2262306a36Sopenharmony_ci	return ~(u32)0 << (n % 32);
2362306a36Sopenharmony_ci}
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci/**
2662306a36Sopenharmony_ci * ethnl_bitmap32_clear() - Clear u32 based bitmap
2762306a36Sopenharmony_ci * @dst:   bitmap to clear
2862306a36Sopenharmony_ci * @start: beginning of the interval
2962306a36Sopenharmony_ci * @end:   end of the interval
3062306a36Sopenharmony_ci * @mod:   set if bitmap was modified
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * Clear @nbits bits of a bitmap with indices @start <= i < @end
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_cistatic void ethnl_bitmap32_clear(u32 *dst, unsigned int start, unsigned int end,
3562306a36Sopenharmony_ci				 bool *mod)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	unsigned int start_word = start / 32;
3862306a36Sopenharmony_ci	unsigned int end_word = end / 32;
3962306a36Sopenharmony_ci	unsigned int i;
4062306a36Sopenharmony_ci	u32 mask;
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	if (end <= start)
4362306a36Sopenharmony_ci		return;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ci	if (start % 32) {
4662306a36Sopenharmony_ci		mask = ethnl_upper_bits(start);
4762306a36Sopenharmony_ci		if (end_word == start_word) {
4862306a36Sopenharmony_ci			mask &= ethnl_lower_bits(end);
4962306a36Sopenharmony_ci			if (dst[start_word] & mask) {
5062306a36Sopenharmony_ci				dst[start_word] &= ~mask;
5162306a36Sopenharmony_ci				*mod = true;
5262306a36Sopenharmony_ci			}
5362306a36Sopenharmony_ci			return;
5462306a36Sopenharmony_ci		}
5562306a36Sopenharmony_ci		if (dst[start_word] & mask) {
5662306a36Sopenharmony_ci			dst[start_word] &= ~mask;
5762306a36Sopenharmony_ci			*mod = true;
5862306a36Sopenharmony_ci		}
5962306a36Sopenharmony_ci		start_word++;
6062306a36Sopenharmony_ci	}
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci	for (i = start_word; i < end_word; i++) {
6362306a36Sopenharmony_ci		if (dst[i]) {
6462306a36Sopenharmony_ci			dst[i] = 0;
6562306a36Sopenharmony_ci			*mod = true;
6662306a36Sopenharmony_ci		}
6762306a36Sopenharmony_ci	}
6862306a36Sopenharmony_ci	if (end % 32) {
6962306a36Sopenharmony_ci		mask = ethnl_lower_bits(end);
7062306a36Sopenharmony_ci		if (dst[end_word] & mask) {
7162306a36Sopenharmony_ci			dst[end_word] &= ~mask;
7262306a36Sopenharmony_ci			*mod = true;
7362306a36Sopenharmony_ci		}
7462306a36Sopenharmony_ci	}
7562306a36Sopenharmony_ci}
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci/**
7862306a36Sopenharmony_ci * ethnl_bitmap32_not_zero() - Check if any bit is set in an interval
7962306a36Sopenharmony_ci * @map:   bitmap to test
8062306a36Sopenharmony_ci * @start: beginning of the interval
8162306a36Sopenharmony_ci * @end:   end of the interval
8262306a36Sopenharmony_ci *
8362306a36Sopenharmony_ci * Return: true if there is non-zero bit with  index @start <= i < @end,
8462306a36Sopenharmony_ci *         false if the whole interval is zero
8562306a36Sopenharmony_ci */
8662306a36Sopenharmony_cistatic bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start,
8762306a36Sopenharmony_ci				    unsigned int end)
8862306a36Sopenharmony_ci{
8962306a36Sopenharmony_ci	unsigned int start_word = start / 32;
9062306a36Sopenharmony_ci	unsigned int end_word = end / 32;
9162306a36Sopenharmony_ci	u32 mask;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	if (end <= start)
9462306a36Sopenharmony_ci		return true;
9562306a36Sopenharmony_ci
9662306a36Sopenharmony_ci	if (start % 32) {
9762306a36Sopenharmony_ci		mask = ethnl_upper_bits(start);
9862306a36Sopenharmony_ci		if (end_word == start_word) {
9962306a36Sopenharmony_ci			mask &= ethnl_lower_bits(end);
10062306a36Sopenharmony_ci			return map[start_word] & mask;
10162306a36Sopenharmony_ci		}
10262306a36Sopenharmony_ci		if (map[start_word] & mask)
10362306a36Sopenharmony_ci			return true;
10462306a36Sopenharmony_ci		start_word++;
10562306a36Sopenharmony_ci	}
10662306a36Sopenharmony_ci
10762306a36Sopenharmony_ci	if (!memchr_inv(map + start_word, '\0',
10862306a36Sopenharmony_ci			(end_word - start_word) * sizeof(u32)))
10962306a36Sopenharmony_ci		return true;
11062306a36Sopenharmony_ci	if (end % 32 == 0)
11162306a36Sopenharmony_ci		return true;
11262306a36Sopenharmony_ci	return map[end_word] & ethnl_lower_bits(end);
11362306a36Sopenharmony_ci}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci/**
11662306a36Sopenharmony_ci * ethnl_bitmap32_update() - Modify u32 based bitmap according to value/mask
11762306a36Sopenharmony_ci *			     pair
11862306a36Sopenharmony_ci * @dst:   bitmap to update
11962306a36Sopenharmony_ci * @nbits: bit size of the bitmap
12062306a36Sopenharmony_ci * @value: values to set
12162306a36Sopenharmony_ci * @mask:  mask of bits to set
12262306a36Sopenharmony_ci * @mod:   set to true if bitmap is modified, preserve if not
12362306a36Sopenharmony_ci *
12462306a36Sopenharmony_ci * Set bits in @dst bitmap which are set in @mask to values from @value, leave
12562306a36Sopenharmony_ci * the rest untouched. If destination bitmap was modified, set @mod to true,
12662306a36Sopenharmony_ci * leave as it is if not.
12762306a36Sopenharmony_ci */
12862306a36Sopenharmony_cistatic void ethnl_bitmap32_update(u32 *dst, unsigned int nbits,
12962306a36Sopenharmony_ci				  const u32 *value, const u32 *mask, bool *mod)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	while (nbits > 0) {
13262306a36Sopenharmony_ci		u32 real_mask = mask ? *mask : ~(u32)0;
13362306a36Sopenharmony_ci		u32 new_value;
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci		if (nbits < 32)
13662306a36Sopenharmony_ci			real_mask &= ethnl_lower_bits(nbits);
13762306a36Sopenharmony_ci		new_value = (*dst & ~real_mask) | (*value & real_mask);
13862306a36Sopenharmony_ci		if (new_value != *dst) {
13962306a36Sopenharmony_ci			*dst = new_value;
14062306a36Sopenharmony_ci			*mod = true;
14162306a36Sopenharmony_ci		}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci		if (nbits <= 32)
14462306a36Sopenharmony_ci			break;
14562306a36Sopenharmony_ci		dst++;
14662306a36Sopenharmony_ci		nbits -= 32;
14762306a36Sopenharmony_ci		value++;
14862306a36Sopenharmony_ci		if (mask)
14962306a36Sopenharmony_ci			mask++;
15062306a36Sopenharmony_ci	}
15162306a36Sopenharmony_ci}
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_cistatic bool ethnl_bitmap32_test_bit(const u32 *map, unsigned int index)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	return map[index / 32] & (1U << (index % 32));
15662306a36Sopenharmony_ci}
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci/**
15962306a36Sopenharmony_ci * ethnl_bitset32_size() - Calculate size of bitset nested attribute
16062306a36Sopenharmony_ci * @val:     value bitmap (u32 based)
16162306a36Sopenharmony_ci * @mask:    mask bitmap (u32 based, optional)
16262306a36Sopenharmony_ci * @nbits:   bit length of the bitset
16362306a36Sopenharmony_ci * @names:   array of bit names (optional)
16462306a36Sopenharmony_ci * @compact: assume compact format for output
16562306a36Sopenharmony_ci *
16662306a36Sopenharmony_ci * Estimate length of netlink attribute composed by a later call to
16762306a36Sopenharmony_ci * ethnl_put_bitset32() call with the same arguments.
16862306a36Sopenharmony_ci *
16962306a36Sopenharmony_ci * Return: negative error code or attribute length estimate
17062306a36Sopenharmony_ci */
17162306a36Sopenharmony_ciint ethnl_bitset32_size(const u32 *val, const u32 *mask, unsigned int nbits,
17262306a36Sopenharmony_ci			ethnl_string_array_t names, bool compact)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	unsigned int len = 0;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	/* list flag */
17762306a36Sopenharmony_ci	if (!mask)
17862306a36Sopenharmony_ci		len += nla_total_size(sizeof(u32));
17962306a36Sopenharmony_ci	/* size */
18062306a36Sopenharmony_ci	len += nla_total_size(sizeof(u32));
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	if (compact) {
18362306a36Sopenharmony_ci		unsigned int nwords = DIV_ROUND_UP(nbits, 32);
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci		/* value, mask */
18662306a36Sopenharmony_ci		len += (mask ? 2 : 1) * nla_total_size(nwords * sizeof(u32));
18762306a36Sopenharmony_ci	} else {
18862306a36Sopenharmony_ci		unsigned int bits_len = 0;
18962306a36Sopenharmony_ci		unsigned int bit_len, i;
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci		for (i = 0; i < nbits; i++) {
19262306a36Sopenharmony_ci			const char *name = names ? names[i] : NULL;
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci			if (!ethnl_bitmap32_test_bit(mask ?: val, i))
19562306a36Sopenharmony_ci				continue;
19662306a36Sopenharmony_ci			/* index */
19762306a36Sopenharmony_ci			bit_len = nla_total_size(sizeof(u32));
19862306a36Sopenharmony_ci			/* name */
19962306a36Sopenharmony_ci			if (name)
20062306a36Sopenharmony_ci				bit_len += ethnl_strz_size(name);
20162306a36Sopenharmony_ci			/* value */
20262306a36Sopenharmony_ci			if (mask && ethnl_bitmap32_test_bit(val, i))
20362306a36Sopenharmony_ci				bit_len += nla_total_size(0);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci			/* bit nest */
20662306a36Sopenharmony_ci			bits_len += nla_total_size(bit_len);
20762306a36Sopenharmony_ci		}
20862306a36Sopenharmony_ci		/* bits nest */
20962306a36Sopenharmony_ci		len += nla_total_size(bits_len);
21062306a36Sopenharmony_ci	}
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	/* outermost nest */
21362306a36Sopenharmony_ci	return nla_total_size(len);
21462306a36Sopenharmony_ci}
21562306a36Sopenharmony_ci
21662306a36Sopenharmony_ci/**
21762306a36Sopenharmony_ci * ethnl_put_bitset32() - Put a bitset nest into a message
21862306a36Sopenharmony_ci * @skb:      skb with the message
21962306a36Sopenharmony_ci * @attrtype: attribute type for the bitset nest
22062306a36Sopenharmony_ci * @val:      value bitmap (u32 based)
22162306a36Sopenharmony_ci * @mask:     mask bitmap (u32 based, optional)
22262306a36Sopenharmony_ci * @nbits:    bit length of the bitset
22362306a36Sopenharmony_ci * @names:    array of bit names (optional)
22462306a36Sopenharmony_ci * @compact:  use compact format for the output
22562306a36Sopenharmony_ci *
22662306a36Sopenharmony_ci * Compose a nested attribute representing a bitset. If @mask is null, simple
22762306a36Sopenharmony_ci * bitmap (bit list) is created, if @mask is provided, represent a value/mask
22862306a36Sopenharmony_ci * pair. Bit names are only used in verbose mode and when provided by calller.
22962306a36Sopenharmony_ci *
23062306a36Sopenharmony_ci * Return: 0 on success, negative error value on error
23162306a36Sopenharmony_ci */
23262306a36Sopenharmony_ciint ethnl_put_bitset32(struct sk_buff *skb, int attrtype, const u32 *val,
23362306a36Sopenharmony_ci		       const u32 *mask, unsigned int nbits,
23462306a36Sopenharmony_ci		       ethnl_string_array_t names, bool compact)
23562306a36Sopenharmony_ci{
23662306a36Sopenharmony_ci	struct nlattr *nest;
23762306a36Sopenharmony_ci	struct nlattr *attr;
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	nest = nla_nest_start(skb, attrtype);
24062306a36Sopenharmony_ci	if (!nest)
24162306a36Sopenharmony_ci		return -EMSGSIZE;
24262306a36Sopenharmony_ci
24362306a36Sopenharmony_ci	if (!mask && nla_put_flag(skb, ETHTOOL_A_BITSET_NOMASK))
24462306a36Sopenharmony_ci		goto nla_put_failure;
24562306a36Sopenharmony_ci	if (nla_put_u32(skb, ETHTOOL_A_BITSET_SIZE, nbits))
24662306a36Sopenharmony_ci		goto nla_put_failure;
24762306a36Sopenharmony_ci	if (compact) {
24862306a36Sopenharmony_ci		unsigned int nwords = DIV_ROUND_UP(nbits, 32);
24962306a36Sopenharmony_ci		unsigned int nbytes = nwords * sizeof(u32);
25062306a36Sopenharmony_ci		u32 *dst;
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci		attr = nla_reserve(skb, ETHTOOL_A_BITSET_VALUE, nbytes);
25362306a36Sopenharmony_ci		if (!attr)
25462306a36Sopenharmony_ci			goto nla_put_failure;
25562306a36Sopenharmony_ci		dst = nla_data(attr);
25662306a36Sopenharmony_ci		memcpy(dst, val, nbytes);
25762306a36Sopenharmony_ci		if (nbits % 32)
25862306a36Sopenharmony_ci			dst[nwords - 1] &= ethnl_lower_bits(nbits);
25962306a36Sopenharmony_ci
26062306a36Sopenharmony_ci		if (mask) {
26162306a36Sopenharmony_ci			attr = nla_reserve(skb, ETHTOOL_A_BITSET_MASK, nbytes);
26262306a36Sopenharmony_ci			if (!attr)
26362306a36Sopenharmony_ci				goto nla_put_failure;
26462306a36Sopenharmony_ci			dst = nla_data(attr);
26562306a36Sopenharmony_ci			memcpy(dst, mask, nbytes);
26662306a36Sopenharmony_ci			if (nbits % 32)
26762306a36Sopenharmony_ci				dst[nwords - 1] &= ethnl_lower_bits(nbits);
26862306a36Sopenharmony_ci		}
26962306a36Sopenharmony_ci	} else {
27062306a36Sopenharmony_ci		struct nlattr *bits;
27162306a36Sopenharmony_ci		unsigned int i;
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_ci		bits = nla_nest_start(skb, ETHTOOL_A_BITSET_BITS);
27462306a36Sopenharmony_ci		if (!bits)
27562306a36Sopenharmony_ci			goto nla_put_failure;
27662306a36Sopenharmony_ci		for (i = 0; i < nbits; i++) {
27762306a36Sopenharmony_ci			const char *name = names ? names[i] : NULL;
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci			if (!ethnl_bitmap32_test_bit(mask ?: val, i))
28062306a36Sopenharmony_ci				continue;
28162306a36Sopenharmony_ci			attr = nla_nest_start(skb, ETHTOOL_A_BITSET_BITS_BIT);
28262306a36Sopenharmony_ci			if (!attr)
28362306a36Sopenharmony_ci				goto nla_put_failure;
28462306a36Sopenharmony_ci			if (nla_put_u32(skb, ETHTOOL_A_BITSET_BIT_INDEX, i))
28562306a36Sopenharmony_ci				goto nla_put_failure;
28662306a36Sopenharmony_ci			if (name &&
28762306a36Sopenharmony_ci			    ethnl_put_strz(skb, ETHTOOL_A_BITSET_BIT_NAME, name))
28862306a36Sopenharmony_ci				goto nla_put_failure;
28962306a36Sopenharmony_ci			if (mask && ethnl_bitmap32_test_bit(val, i) &&
29062306a36Sopenharmony_ci			    nla_put_flag(skb, ETHTOOL_A_BITSET_BIT_VALUE))
29162306a36Sopenharmony_ci				goto nla_put_failure;
29262306a36Sopenharmony_ci			nla_nest_end(skb, attr);
29362306a36Sopenharmony_ci		}
29462306a36Sopenharmony_ci		nla_nest_end(skb, bits);
29562306a36Sopenharmony_ci	}
29662306a36Sopenharmony_ci
29762306a36Sopenharmony_ci	nla_nest_end(skb, nest);
29862306a36Sopenharmony_ci	return 0;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_cinla_put_failure:
30162306a36Sopenharmony_ci	nla_nest_cancel(skb, nest);
30262306a36Sopenharmony_ci	return -EMSGSIZE;
30362306a36Sopenharmony_ci}
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_cistatic const struct nla_policy bitset_policy[] = {
30662306a36Sopenharmony_ci	[ETHTOOL_A_BITSET_NOMASK]	= { .type = NLA_FLAG },
30762306a36Sopenharmony_ci	[ETHTOOL_A_BITSET_SIZE]		= NLA_POLICY_MAX(NLA_U32,
30862306a36Sopenharmony_ci							 ETHNL_MAX_BITSET_SIZE),
30962306a36Sopenharmony_ci	[ETHTOOL_A_BITSET_BITS]		= { .type = NLA_NESTED },
31062306a36Sopenharmony_ci	[ETHTOOL_A_BITSET_VALUE]	= { .type = NLA_BINARY },
31162306a36Sopenharmony_ci	[ETHTOOL_A_BITSET_MASK]		= { .type = NLA_BINARY },
31262306a36Sopenharmony_ci};
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_cistatic const struct nla_policy bit_policy[] = {
31562306a36Sopenharmony_ci	[ETHTOOL_A_BITSET_BIT_INDEX]	= { .type = NLA_U32 },
31662306a36Sopenharmony_ci	[ETHTOOL_A_BITSET_BIT_NAME]	= { .type = NLA_NUL_STRING },
31762306a36Sopenharmony_ci	[ETHTOOL_A_BITSET_BIT_VALUE]	= { .type = NLA_FLAG },
31862306a36Sopenharmony_ci};
31962306a36Sopenharmony_ci
32062306a36Sopenharmony_ci/**
32162306a36Sopenharmony_ci * ethnl_bitset_is_compact() - check if bitset attribute represents a compact
32262306a36Sopenharmony_ci *			       bitset
32362306a36Sopenharmony_ci * @bitset:  nested attribute representing a bitset
32462306a36Sopenharmony_ci * @compact: pointer for return value
32562306a36Sopenharmony_ci *
32662306a36Sopenharmony_ci * Return: 0 on success, negative error code on failure
32762306a36Sopenharmony_ci */
32862306a36Sopenharmony_ciint ethnl_bitset_is_compact(const struct nlattr *bitset, bool *compact)
32962306a36Sopenharmony_ci{
33062306a36Sopenharmony_ci	struct nlattr *tb[ARRAY_SIZE(bitset_policy)];
33162306a36Sopenharmony_ci	int ret;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	ret = nla_parse_nested(tb, ARRAY_SIZE(bitset_policy) - 1, bitset,
33462306a36Sopenharmony_ci			       bitset_policy, NULL);
33562306a36Sopenharmony_ci	if (ret < 0)
33662306a36Sopenharmony_ci		return ret;
33762306a36Sopenharmony_ci
33862306a36Sopenharmony_ci	if (tb[ETHTOOL_A_BITSET_BITS]) {
33962306a36Sopenharmony_ci		if (tb[ETHTOOL_A_BITSET_VALUE] || tb[ETHTOOL_A_BITSET_MASK])
34062306a36Sopenharmony_ci			return -EINVAL;
34162306a36Sopenharmony_ci		*compact = false;
34262306a36Sopenharmony_ci		return 0;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci	if (!tb[ETHTOOL_A_BITSET_SIZE] || !tb[ETHTOOL_A_BITSET_VALUE])
34562306a36Sopenharmony_ci		return -EINVAL;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	*compact = true;
34862306a36Sopenharmony_ci	return 0;
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ci
35162306a36Sopenharmony_ci/**
35262306a36Sopenharmony_ci * ethnl_name_to_idx() - look up string index for a name
35362306a36Sopenharmony_ci * @names:   array of ETH_GSTRING_LEN sized strings
35462306a36Sopenharmony_ci * @n_names: number of strings in the array
35562306a36Sopenharmony_ci * @name:    name to look up
35662306a36Sopenharmony_ci *
35762306a36Sopenharmony_ci * Return: index of the string if found, -ENOENT if not found
35862306a36Sopenharmony_ci */
35962306a36Sopenharmony_cistatic int ethnl_name_to_idx(ethnl_string_array_t names, unsigned int n_names,
36062306a36Sopenharmony_ci			     const char *name)
36162306a36Sopenharmony_ci{
36262306a36Sopenharmony_ci	unsigned int i;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	if (!names)
36562306a36Sopenharmony_ci		return -ENOENT;
36662306a36Sopenharmony_ci
36762306a36Sopenharmony_ci	for (i = 0; i < n_names; i++) {
36862306a36Sopenharmony_ci		/* names[i] may not be null terminated */
36962306a36Sopenharmony_ci		if (!strncmp(names[i], name, ETH_GSTRING_LEN) &&
37062306a36Sopenharmony_ci		    strlen(name) <= ETH_GSTRING_LEN)
37162306a36Sopenharmony_ci			return i;
37262306a36Sopenharmony_ci	}
37362306a36Sopenharmony_ci
37462306a36Sopenharmony_ci	return -ENOENT;
37562306a36Sopenharmony_ci}
37662306a36Sopenharmony_ci
37762306a36Sopenharmony_cistatic int ethnl_parse_bit(unsigned int *index, bool *val, unsigned int nbits,
37862306a36Sopenharmony_ci			   const struct nlattr *bit_attr, bool no_mask,
37962306a36Sopenharmony_ci			   ethnl_string_array_t names,
38062306a36Sopenharmony_ci			   struct netlink_ext_ack *extack)
38162306a36Sopenharmony_ci{
38262306a36Sopenharmony_ci	struct nlattr *tb[ARRAY_SIZE(bit_policy)];
38362306a36Sopenharmony_ci	int ret, idx;
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	ret = nla_parse_nested(tb, ARRAY_SIZE(bit_policy) - 1, bit_attr,
38662306a36Sopenharmony_ci			       bit_policy, extack);
38762306a36Sopenharmony_ci	if (ret < 0)
38862306a36Sopenharmony_ci		return ret;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	if (tb[ETHTOOL_A_BITSET_BIT_INDEX]) {
39162306a36Sopenharmony_ci		const char *name;
39262306a36Sopenharmony_ci
39362306a36Sopenharmony_ci		idx = nla_get_u32(tb[ETHTOOL_A_BITSET_BIT_INDEX]);
39462306a36Sopenharmony_ci		if (idx >= nbits) {
39562306a36Sopenharmony_ci			NL_SET_ERR_MSG_ATTR(extack,
39662306a36Sopenharmony_ci					    tb[ETHTOOL_A_BITSET_BIT_INDEX],
39762306a36Sopenharmony_ci					    "bit index too high");
39862306a36Sopenharmony_ci			return -EOPNOTSUPP;
39962306a36Sopenharmony_ci		}
40062306a36Sopenharmony_ci		name = names ? names[idx] : NULL;
40162306a36Sopenharmony_ci		if (tb[ETHTOOL_A_BITSET_BIT_NAME] && name &&
40262306a36Sopenharmony_ci		    strncmp(nla_data(tb[ETHTOOL_A_BITSET_BIT_NAME]), name,
40362306a36Sopenharmony_ci			    nla_len(tb[ETHTOOL_A_BITSET_BIT_NAME]))) {
40462306a36Sopenharmony_ci			NL_SET_ERR_MSG_ATTR(extack, bit_attr,
40562306a36Sopenharmony_ci					    "bit index and name mismatch");
40662306a36Sopenharmony_ci			return -EINVAL;
40762306a36Sopenharmony_ci		}
40862306a36Sopenharmony_ci	} else if (tb[ETHTOOL_A_BITSET_BIT_NAME]) {
40962306a36Sopenharmony_ci		idx = ethnl_name_to_idx(names, nbits,
41062306a36Sopenharmony_ci					nla_data(tb[ETHTOOL_A_BITSET_BIT_NAME]));
41162306a36Sopenharmony_ci		if (idx < 0) {
41262306a36Sopenharmony_ci			NL_SET_ERR_MSG_ATTR(extack,
41362306a36Sopenharmony_ci					    tb[ETHTOOL_A_BITSET_BIT_NAME],
41462306a36Sopenharmony_ci					    "bit name not found");
41562306a36Sopenharmony_ci			return -EOPNOTSUPP;
41662306a36Sopenharmony_ci		}
41762306a36Sopenharmony_ci	} else {
41862306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, bit_attr,
41962306a36Sopenharmony_ci				    "neither bit index nor name specified");
42062306a36Sopenharmony_ci		return -EINVAL;
42162306a36Sopenharmony_ci	}
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	*index = idx;
42462306a36Sopenharmony_ci	*val = no_mask || tb[ETHTOOL_A_BITSET_BIT_VALUE];
42562306a36Sopenharmony_ci	return 0;
42662306a36Sopenharmony_ci}
42762306a36Sopenharmony_ci
42862306a36Sopenharmony_cistatic int
42962306a36Sopenharmony_ciethnl_update_bitset32_verbose(u32 *bitmap, unsigned int nbits,
43062306a36Sopenharmony_ci			      const struct nlattr *attr, struct nlattr **tb,
43162306a36Sopenharmony_ci			      ethnl_string_array_t names,
43262306a36Sopenharmony_ci			      struct netlink_ext_ack *extack, bool *mod)
43362306a36Sopenharmony_ci{
43462306a36Sopenharmony_ci	struct nlattr *bit_attr;
43562306a36Sopenharmony_ci	bool no_mask;
43662306a36Sopenharmony_ci	int rem;
43762306a36Sopenharmony_ci	int ret;
43862306a36Sopenharmony_ci
43962306a36Sopenharmony_ci	if (tb[ETHTOOL_A_BITSET_VALUE]) {
44062306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_VALUE],
44162306a36Sopenharmony_ci				    "value only allowed in compact bitset");
44262306a36Sopenharmony_ci		return -EINVAL;
44362306a36Sopenharmony_ci	}
44462306a36Sopenharmony_ci	if (tb[ETHTOOL_A_BITSET_MASK]) {
44562306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK],
44662306a36Sopenharmony_ci				    "mask only allowed in compact bitset");
44762306a36Sopenharmony_ci		return -EINVAL;
44862306a36Sopenharmony_ci	}
44962306a36Sopenharmony_ci
45062306a36Sopenharmony_ci	no_mask = tb[ETHTOOL_A_BITSET_NOMASK];
45162306a36Sopenharmony_ci	if (no_mask)
45262306a36Sopenharmony_ci		ethnl_bitmap32_clear(bitmap, 0, nbits, mod);
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci	nla_for_each_nested(bit_attr, tb[ETHTOOL_A_BITSET_BITS], rem) {
45562306a36Sopenharmony_ci		bool old_val, new_val;
45662306a36Sopenharmony_ci		unsigned int idx;
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci		if (nla_type(bit_attr) != ETHTOOL_A_BITSET_BITS_BIT) {
45962306a36Sopenharmony_ci			NL_SET_ERR_MSG_ATTR(extack, bit_attr,
46062306a36Sopenharmony_ci					    "only ETHTOOL_A_BITSET_BITS_BIT allowed in ETHTOOL_A_BITSET_BITS");
46162306a36Sopenharmony_ci			return -EINVAL;
46262306a36Sopenharmony_ci		}
46362306a36Sopenharmony_ci		ret = ethnl_parse_bit(&idx, &new_val, nbits, bit_attr, no_mask,
46462306a36Sopenharmony_ci				      names, extack);
46562306a36Sopenharmony_ci		if (ret < 0)
46662306a36Sopenharmony_ci			return ret;
46762306a36Sopenharmony_ci		old_val = bitmap[idx / 32] & ((u32)1 << (idx % 32));
46862306a36Sopenharmony_ci		if (new_val != old_val) {
46962306a36Sopenharmony_ci			if (new_val)
47062306a36Sopenharmony_ci				bitmap[idx / 32] |= ((u32)1 << (idx % 32));
47162306a36Sopenharmony_ci			else
47262306a36Sopenharmony_ci				bitmap[idx / 32] &= ~((u32)1 << (idx % 32));
47362306a36Sopenharmony_ci			*mod = true;
47462306a36Sopenharmony_ci		}
47562306a36Sopenharmony_ci	}
47662306a36Sopenharmony_ci
47762306a36Sopenharmony_ci	return 0;
47862306a36Sopenharmony_ci}
47962306a36Sopenharmony_ci
48062306a36Sopenharmony_cistatic int ethnl_compact_sanity_checks(unsigned int nbits,
48162306a36Sopenharmony_ci				       const struct nlattr *nest,
48262306a36Sopenharmony_ci				       struct nlattr **tb,
48362306a36Sopenharmony_ci				       struct netlink_ext_ack *extack)
48462306a36Sopenharmony_ci{
48562306a36Sopenharmony_ci	bool no_mask = tb[ETHTOOL_A_BITSET_NOMASK];
48662306a36Sopenharmony_ci	unsigned int attr_nbits, attr_nwords;
48762306a36Sopenharmony_ci	const struct nlattr *test_attr;
48862306a36Sopenharmony_ci
48962306a36Sopenharmony_ci	if (no_mask && tb[ETHTOOL_A_BITSET_MASK]) {
49062306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK],
49162306a36Sopenharmony_ci				    "mask not allowed in list bitset");
49262306a36Sopenharmony_ci		return -EINVAL;
49362306a36Sopenharmony_ci	}
49462306a36Sopenharmony_ci	if (!tb[ETHTOOL_A_BITSET_SIZE]) {
49562306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, nest,
49662306a36Sopenharmony_ci				    "missing size in compact bitset");
49762306a36Sopenharmony_ci		return -EINVAL;
49862306a36Sopenharmony_ci	}
49962306a36Sopenharmony_ci	if (!tb[ETHTOOL_A_BITSET_VALUE]) {
50062306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, nest,
50162306a36Sopenharmony_ci				    "missing value in compact bitset");
50262306a36Sopenharmony_ci		return -EINVAL;
50362306a36Sopenharmony_ci	}
50462306a36Sopenharmony_ci	if (!no_mask && !tb[ETHTOOL_A_BITSET_MASK]) {
50562306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, nest,
50662306a36Sopenharmony_ci				    "missing mask in compact nonlist bitset");
50762306a36Sopenharmony_ci		return -EINVAL;
50862306a36Sopenharmony_ci	}
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	attr_nbits = nla_get_u32(tb[ETHTOOL_A_BITSET_SIZE]);
51162306a36Sopenharmony_ci	attr_nwords = DIV_ROUND_UP(attr_nbits, 32);
51262306a36Sopenharmony_ci	if (nla_len(tb[ETHTOOL_A_BITSET_VALUE]) != attr_nwords * sizeof(u32)) {
51362306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_VALUE],
51462306a36Sopenharmony_ci				    "bitset value length does not match size");
51562306a36Sopenharmony_ci		return -EINVAL;
51662306a36Sopenharmony_ci	}
51762306a36Sopenharmony_ci	if (tb[ETHTOOL_A_BITSET_MASK] &&
51862306a36Sopenharmony_ci	    nla_len(tb[ETHTOOL_A_BITSET_MASK]) != attr_nwords * sizeof(u32)) {
51962306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK],
52062306a36Sopenharmony_ci				    "bitset mask length does not match size");
52162306a36Sopenharmony_ci		return -EINVAL;
52262306a36Sopenharmony_ci	}
52362306a36Sopenharmony_ci	if (attr_nbits <= nbits)
52462306a36Sopenharmony_ci		return 0;
52562306a36Sopenharmony_ci
52662306a36Sopenharmony_ci	test_attr = no_mask ? tb[ETHTOOL_A_BITSET_VALUE] :
52762306a36Sopenharmony_ci			      tb[ETHTOOL_A_BITSET_MASK];
52862306a36Sopenharmony_ci	if (ethnl_bitmap32_not_zero(nla_data(test_attr), nbits, attr_nbits)) {
52962306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, test_attr,
53062306a36Sopenharmony_ci				    "cannot modify bits past kernel bitset size");
53162306a36Sopenharmony_ci		return -EINVAL;
53262306a36Sopenharmony_ci	}
53362306a36Sopenharmony_ci	return 0;
53462306a36Sopenharmony_ci}
53562306a36Sopenharmony_ci
53662306a36Sopenharmony_ci/**
53762306a36Sopenharmony_ci * ethnl_update_bitset32() - Apply a bitset nest to a u32 based bitmap
53862306a36Sopenharmony_ci * @bitmap:  bitmap to update
53962306a36Sopenharmony_ci * @nbits:   size of the updated bitmap in bits
54062306a36Sopenharmony_ci * @attr:    nest attribute to parse and apply
54162306a36Sopenharmony_ci * @names:   array of bit names; may be null for compact format
54262306a36Sopenharmony_ci * @extack:  extack for error reporting
54362306a36Sopenharmony_ci * @mod:     set this to true if bitmap is modified, leave as it is if not
54462306a36Sopenharmony_ci *
54562306a36Sopenharmony_ci * Apply bitset netsted attribute to a bitmap. If the attribute represents
54662306a36Sopenharmony_ci * a bit list, @bitmap is set to its contents; otherwise, bits in mask are
54762306a36Sopenharmony_ci * set to values from value. Bitmaps in the attribute may be longer than
54862306a36Sopenharmony_ci * @nbits but the message must not request modifying any bits past @nbits.
54962306a36Sopenharmony_ci *
55062306a36Sopenharmony_ci * Return: negative error code on failure, 0 on success
55162306a36Sopenharmony_ci */
55262306a36Sopenharmony_ciint ethnl_update_bitset32(u32 *bitmap, unsigned int nbits,
55362306a36Sopenharmony_ci			  const struct nlattr *attr, ethnl_string_array_t names,
55462306a36Sopenharmony_ci			  struct netlink_ext_ack *extack, bool *mod)
55562306a36Sopenharmony_ci{
55662306a36Sopenharmony_ci	struct nlattr *tb[ARRAY_SIZE(bitset_policy)];
55762306a36Sopenharmony_ci	unsigned int change_bits;
55862306a36Sopenharmony_ci	bool no_mask;
55962306a36Sopenharmony_ci	int ret;
56062306a36Sopenharmony_ci
56162306a36Sopenharmony_ci	if (!attr)
56262306a36Sopenharmony_ci		return 0;
56362306a36Sopenharmony_ci	ret = nla_parse_nested(tb, ARRAY_SIZE(bitset_policy) - 1, attr,
56462306a36Sopenharmony_ci			       bitset_policy, extack);
56562306a36Sopenharmony_ci	if (ret < 0)
56662306a36Sopenharmony_ci		return ret;
56762306a36Sopenharmony_ci
56862306a36Sopenharmony_ci	if (tb[ETHTOOL_A_BITSET_BITS])
56962306a36Sopenharmony_ci		return ethnl_update_bitset32_verbose(bitmap, nbits, attr, tb,
57062306a36Sopenharmony_ci						     names, extack, mod);
57162306a36Sopenharmony_ci	ret = ethnl_compact_sanity_checks(nbits, attr, tb, extack);
57262306a36Sopenharmony_ci	if (ret < 0)
57362306a36Sopenharmony_ci		return ret;
57462306a36Sopenharmony_ci
57562306a36Sopenharmony_ci	no_mask = tb[ETHTOOL_A_BITSET_NOMASK];
57662306a36Sopenharmony_ci	change_bits = min_t(unsigned int,
57762306a36Sopenharmony_ci			    nla_get_u32(tb[ETHTOOL_A_BITSET_SIZE]), nbits);
57862306a36Sopenharmony_ci	ethnl_bitmap32_update(bitmap, change_bits,
57962306a36Sopenharmony_ci			      nla_data(tb[ETHTOOL_A_BITSET_VALUE]),
58062306a36Sopenharmony_ci			      no_mask ? NULL :
58162306a36Sopenharmony_ci					nla_data(tb[ETHTOOL_A_BITSET_MASK]),
58262306a36Sopenharmony_ci			      mod);
58362306a36Sopenharmony_ci	if (no_mask && change_bits < nbits)
58462306a36Sopenharmony_ci		ethnl_bitmap32_clear(bitmap, change_bits, nbits, mod);
58562306a36Sopenharmony_ci
58662306a36Sopenharmony_ci	return 0;
58762306a36Sopenharmony_ci}
58862306a36Sopenharmony_ci
58962306a36Sopenharmony_ci/**
59062306a36Sopenharmony_ci * ethnl_parse_bitset() - Compute effective value and mask from bitset nest
59162306a36Sopenharmony_ci * @val:     unsigned long based bitmap to put value into
59262306a36Sopenharmony_ci * @mask:    unsigned long based bitmap to put mask into
59362306a36Sopenharmony_ci * @nbits:   size of @val and @mask bitmaps
59462306a36Sopenharmony_ci * @attr:    nest attribute to parse and apply
59562306a36Sopenharmony_ci * @names:   array of bit names; may be null for compact format
59662306a36Sopenharmony_ci * @extack:  extack for error reporting
59762306a36Sopenharmony_ci *
59862306a36Sopenharmony_ci * Provide @nbits size long bitmaps for value and mask so that
59962306a36Sopenharmony_ci * x = (val & mask) | (x & ~mask) would modify any @nbits sized bitmap x
60062306a36Sopenharmony_ci * the same way ethnl_update_bitset() with the same bitset attribute would.
60162306a36Sopenharmony_ci *
60262306a36Sopenharmony_ci * Return:   negative error code on failure, 0 on success
60362306a36Sopenharmony_ci */
60462306a36Sopenharmony_ciint ethnl_parse_bitset(unsigned long *val, unsigned long *mask,
60562306a36Sopenharmony_ci		       unsigned int nbits, const struct nlattr *attr,
60662306a36Sopenharmony_ci		       ethnl_string_array_t names,
60762306a36Sopenharmony_ci		       struct netlink_ext_ack *extack)
60862306a36Sopenharmony_ci{
60962306a36Sopenharmony_ci	struct nlattr *tb[ARRAY_SIZE(bitset_policy)];
61062306a36Sopenharmony_ci	const struct nlattr *bit_attr;
61162306a36Sopenharmony_ci	bool no_mask;
61262306a36Sopenharmony_ci	int rem;
61362306a36Sopenharmony_ci	int ret;
61462306a36Sopenharmony_ci
61562306a36Sopenharmony_ci	if (!attr)
61662306a36Sopenharmony_ci		return 0;
61762306a36Sopenharmony_ci	ret = nla_parse_nested(tb, ARRAY_SIZE(bitset_policy) - 1, attr,
61862306a36Sopenharmony_ci			       bitset_policy, extack);
61962306a36Sopenharmony_ci	if (ret < 0)
62062306a36Sopenharmony_ci		return ret;
62162306a36Sopenharmony_ci	no_mask = tb[ETHTOOL_A_BITSET_NOMASK];
62262306a36Sopenharmony_ci
62362306a36Sopenharmony_ci	if (!tb[ETHTOOL_A_BITSET_BITS]) {
62462306a36Sopenharmony_ci		unsigned int change_bits;
62562306a36Sopenharmony_ci
62662306a36Sopenharmony_ci		ret = ethnl_compact_sanity_checks(nbits, attr, tb, extack);
62762306a36Sopenharmony_ci		if (ret < 0)
62862306a36Sopenharmony_ci			return ret;
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci		change_bits = nla_get_u32(tb[ETHTOOL_A_BITSET_SIZE]);
63162306a36Sopenharmony_ci		if (change_bits > nbits)
63262306a36Sopenharmony_ci			change_bits = nbits;
63362306a36Sopenharmony_ci		bitmap_from_arr32(val, nla_data(tb[ETHTOOL_A_BITSET_VALUE]),
63462306a36Sopenharmony_ci				  change_bits);
63562306a36Sopenharmony_ci		if (change_bits < nbits)
63662306a36Sopenharmony_ci			bitmap_clear(val, change_bits, nbits - change_bits);
63762306a36Sopenharmony_ci		if (no_mask) {
63862306a36Sopenharmony_ci			bitmap_fill(mask, nbits);
63962306a36Sopenharmony_ci		} else {
64062306a36Sopenharmony_ci			bitmap_from_arr32(mask,
64162306a36Sopenharmony_ci					  nla_data(tb[ETHTOOL_A_BITSET_MASK]),
64262306a36Sopenharmony_ci					  change_bits);
64362306a36Sopenharmony_ci			if (change_bits < nbits)
64462306a36Sopenharmony_ci				bitmap_clear(mask, change_bits,
64562306a36Sopenharmony_ci					     nbits - change_bits);
64662306a36Sopenharmony_ci		}
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci		return 0;
64962306a36Sopenharmony_ci	}
65062306a36Sopenharmony_ci
65162306a36Sopenharmony_ci	if (tb[ETHTOOL_A_BITSET_VALUE]) {
65262306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_VALUE],
65362306a36Sopenharmony_ci				    "value only allowed in compact bitset");
65462306a36Sopenharmony_ci		return -EINVAL;
65562306a36Sopenharmony_ci	}
65662306a36Sopenharmony_ci	if (tb[ETHTOOL_A_BITSET_MASK]) {
65762306a36Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK],
65862306a36Sopenharmony_ci				    "mask only allowed in compact bitset");
65962306a36Sopenharmony_ci		return -EINVAL;
66062306a36Sopenharmony_ci	}
66162306a36Sopenharmony_ci
66262306a36Sopenharmony_ci	bitmap_zero(val, nbits);
66362306a36Sopenharmony_ci	if (no_mask)
66462306a36Sopenharmony_ci		bitmap_fill(mask, nbits);
66562306a36Sopenharmony_ci	else
66662306a36Sopenharmony_ci		bitmap_zero(mask, nbits);
66762306a36Sopenharmony_ci
66862306a36Sopenharmony_ci	nla_for_each_nested(bit_attr, tb[ETHTOOL_A_BITSET_BITS], rem) {
66962306a36Sopenharmony_ci		unsigned int idx;
67062306a36Sopenharmony_ci		bool bit_val;
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_ci		ret = ethnl_parse_bit(&idx, &bit_val, nbits, bit_attr, no_mask,
67362306a36Sopenharmony_ci				      names, extack);
67462306a36Sopenharmony_ci		if (ret < 0)
67562306a36Sopenharmony_ci			return ret;
67662306a36Sopenharmony_ci		if (bit_val)
67762306a36Sopenharmony_ci			__set_bit(idx, val);
67862306a36Sopenharmony_ci		if (!no_mask)
67962306a36Sopenharmony_ci			__set_bit(idx, mask);
68062306a36Sopenharmony_ci	}
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	return 0;
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ci
68562306a36Sopenharmony_ci#if BITS_PER_LONG == 64 && defined(__BIG_ENDIAN)
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci/* 64-bit big endian architectures are the only case when u32 based bitmaps
68862306a36Sopenharmony_ci * and unsigned long based bitmaps have different memory layout so that we
68962306a36Sopenharmony_ci * cannot simply cast the latter to the former and need actual wrappers
69062306a36Sopenharmony_ci * converting the latter to the former.
69162306a36Sopenharmony_ci *
69262306a36Sopenharmony_ci * To reduce the number of slab allocations, the wrappers use fixed size local
69362306a36Sopenharmony_ci * variables for bitmaps up to ETHNL_SMALL_BITMAP_BITS bits which is the
69462306a36Sopenharmony_ci * majority of bitmaps used by ethtool.
69562306a36Sopenharmony_ci */
69662306a36Sopenharmony_ci#define ETHNL_SMALL_BITMAP_BITS 128
69762306a36Sopenharmony_ci#define ETHNL_SMALL_BITMAP_WORDS DIV_ROUND_UP(ETHNL_SMALL_BITMAP_BITS, 32)
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ciint ethnl_bitset_size(const unsigned long *val, const unsigned long *mask,
70062306a36Sopenharmony_ci		      unsigned int nbits, ethnl_string_array_t names,
70162306a36Sopenharmony_ci		      bool compact)
70262306a36Sopenharmony_ci{
70362306a36Sopenharmony_ci	u32 small_mask32[ETHNL_SMALL_BITMAP_WORDS];
70462306a36Sopenharmony_ci	u32 small_val32[ETHNL_SMALL_BITMAP_WORDS];
70562306a36Sopenharmony_ci	u32 *mask32;
70662306a36Sopenharmony_ci	u32 *val32;
70762306a36Sopenharmony_ci	int ret;
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci	if (nbits > ETHNL_SMALL_BITMAP_BITS) {
71062306a36Sopenharmony_ci		unsigned int nwords = DIV_ROUND_UP(nbits, 32);
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci		val32 = kmalloc_array(2 * nwords, sizeof(u32), GFP_KERNEL);
71362306a36Sopenharmony_ci		if (!val32)
71462306a36Sopenharmony_ci			return -ENOMEM;
71562306a36Sopenharmony_ci		mask32 = val32 + nwords;
71662306a36Sopenharmony_ci	} else {
71762306a36Sopenharmony_ci		val32 = small_val32;
71862306a36Sopenharmony_ci		mask32 = small_mask32;
71962306a36Sopenharmony_ci	}
72062306a36Sopenharmony_ci
72162306a36Sopenharmony_ci	bitmap_to_arr32(val32, val, nbits);
72262306a36Sopenharmony_ci	if (mask)
72362306a36Sopenharmony_ci		bitmap_to_arr32(mask32, mask, nbits);
72462306a36Sopenharmony_ci	else
72562306a36Sopenharmony_ci		mask32 = NULL;
72662306a36Sopenharmony_ci	ret = ethnl_bitset32_size(val32, mask32, nbits, names, compact);
72762306a36Sopenharmony_ci
72862306a36Sopenharmony_ci	if (nbits > ETHNL_SMALL_BITMAP_BITS)
72962306a36Sopenharmony_ci		kfree(val32);
73062306a36Sopenharmony_ci
73162306a36Sopenharmony_ci	return ret;
73262306a36Sopenharmony_ci}
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ciint ethnl_put_bitset(struct sk_buff *skb, int attrtype,
73562306a36Sopenharmony_ci		     const unsigned long *val, const unsigned long *mask,
73662306a36Sopenharmony_ci		     unsigned int nbits, ethnl_string_array_t names,
73762306a36Sopenharmony_ci		     bool compact)
73862306a36Sopenharmony_ci{
73962306a36Sopenharmony_ci	u32 small_mask32[ETHNL_SMALL_BITMAP_WORDS];
74062306a36Sopenharmony_ci	u32 small_val32[ETHNL_SMALL_BITMAP_WORDS];
74162306a36Sopenharmony_ci	u32 *mask32;
74262306a36Sopenharmony_ci	u32 *val32;
74362306a36Sopenharmony_ci	int ret;
74462306a36Sopenharmony_ci
74562306a36Sopenharmony_ci	if (nbits > ETHNL_SMALL_BITMAP_BITS) {
74662306a36Sopenharmony_ci		unsigned int nwords = DIV_ROUND_UP(nbits, 32);
74762306a36Sopenharmony_ci
74862306a36Sopenharmony_ci		val32 = kmalloc_array(2 * nwords, sizeof(u32), GFP_KERNEL);
74962306a36Sopenharmony_ci		if (!val32)
75062306a36Sopenharmony_ci			return -ENOMEM;
75162306a36Sopenharmony_ci		mask32 = val32 + nwords;
75262306a36Sopenharmony_ci	} else {
75362306a36Sopenharmony_ci		val32 = small_val32;
75462306a36Sopenharmony_ci		mask32 = small_mask32;
75562306a36Sopenharmony_ci	}
75662306a36Sopenharmony_ci
75762306a36Sopenharmony_ci	bitmap_to_arr32(val32, val, nbits);
75862306a36Sopenharmony_ci	if (mask)
75962306a36Sopenharmony_ci		bitmap_to_arr32(mask32, mask, nbits);
76062306a36Sopenharmony_ci	else
76162306a36Sopenharmony_ci		mask32 = NULL;
76262306a36Sopenharmony_ci	ret = ethnl_put_bitset32(skb, attrtype, val32, mask32, nbits, names,
76362306a36Sopenharmony_ci				 compact);
76462306a36Sopenharmony_ci
76562306a36Sopenharmony_ci	if (nbits > ETHNL_SMALL_BITMAP_BITS)
76662306a36Sopenharmony_ci		kfree(val32);
76762306a36Sopenharmony_ci
76862306a36Sopenharmony_ci	return ret;
76962306a36Sopenharmony_ci}
77062306a36Sopenharmony_ci
77162306a36Sopenharmony_ciint ethnl_update_bitset(unsigned long *bitmap, unsigned int nbits,
77262306a36Sopenharmony_ci			const struct nlattr *attr, ethnl_string_array_t names,
77362306a36Sopenharmony_ci			struct netlink_ext_ack *extack, bool *mod)
77462306a36Sopenharmony_ci{
77562306a36Sopenharmony_ci	u32 small_bitmap32[ETHNL_SMALL_BITMAP_WORDS];
77662306a36Sopenharmony_ci	u32 *bitmap32 = small_bitmap32;
77762306a36Sopenharmony_ci	bool u32_mod = false;
77862306a36Sopenharmony_ci	int ret;
77962306a36Sopenharmony_ci
78062306a36Sopenharmony_ci	if (nbits > ETHNL_SMALL_BITMAP_BITS) {
78162306a36Sopenharmony_ci		unsigned int dst_words = DIV_ROUND_UP(nbits, 32);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci		bitmap32 = kmalloc_array(dst_words, sizeof(u32), GFP_KERNEL);
78462306a36Sopenharmony_ci		if (!bitmap32)
78562306a36Sopenharmony_ci			return -ENOMEM;
78662306a36Sopenharmony_ci	}
78762306a36Sopenharmony_ci
78862306a36Sopenharmony_ci	bitmap_to_arr32(bitmap32, bitmap, nbits);
78962306a36Sopenharmony_ci	ret = ethnl_update_bitset32(bitmap32, nbits, attr, names, extack,
79062306a36Sopenharmony_ci				    &u32_mod);
79162306a36Sopenharmony_ci	if (u32_mod) {
79262306a36Sopenharmony_ci		bitmap_from_arr32(bitmap, bitmap32, nbits);
79362306a36Sopenharmony_ci		*mod = true;
79462306a36Sopenharmony_ci	}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_ci	if (nbits > ETHNL_SMALL_BITMAP_BITS)
79762306a36Sopenharmony_ci		kfree(bitmap32);
79862306a36Sopenharmony_ci
79962306a36Sopenharmony_ci	return ret;
80062306a36Sopenharmony_ci}
80162306a36Sopenharmony_ci
80262306a36Sopenharmony_ci#else
80362306a36Sopenharmony_ci
80462306a36Sopenharmony_ci/* On little endian 64-bit and all 32-bit architectures, an unsigned long
80562306a36Sopenharmony_ci * based bitmap can be interpreted as u32 based one using a simple cast.
80662306a36Sopenharmony_ci */
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ciint ethnl_bitset_size(const unsigned long *val, const unsigned long *mask,
80962306a36Sopenharmony_ci		      unsigned int nbits, ethnl_string_array_t names,
81062306a36Sopenharmony_ci		      bool compact)
81162306a36Sopenharmony_ci{
81262306a36Sopenharmony_ci	return ethnl_bitset32_size((const u32 *)val, (const u32 *)mask, nbits,
81362306a36Sopenharmony_ci				   names, compact);
81462306a36Sopenharmony_ci}
81562306a36Sopenharmony_ci
81662306a36Sopenharmony_ciint ethnl_put_bitset(struct sk_buff *skb, int attrtype,
81762306a36Sopenharmony_ci		     const unsigned long *val, const unsigned long *mask,
81862306a36Sopenharmony_ci		     unsigned int nbits, ethnl_string_array_t names,
81962306a36Sopenharmony_ci		     bool compact)
82062306a36Sopenharmony_ci{
82162306a36Sopenharmony_ci	return ethnl_put_bitset32(skb, attrtype, (const u32 *)val,
82262306a36Sopenharmony_ci				  (const u32 *)mask, nbits, names, compact);
82362306a36Sopenharmony_ci}
82462306a36Sopenharmony_ci
82562306a36Sopenharmony_ciint ethnl_update_bitset(unsigned long *bitmap, unsigned int nbits,
82662306a36Sopenharmony_ci			const struct nlattr *attr, ethnl_string_array_t names,
82762306a36Sopenharmony_ci			struct netlink_ext_ack *extack, bool *mod)
82862306a36Sopenharmony_ci{
82962306a36Sopenharmony_ci	return ethnl_update_bitset32((u32 *)bitmap, nbits, attr, names, extack,
83062306a36Sopenharmony_ci				     mod);
83162306a36Sopenharmony_ci}
83262306a36Sopenharmony_ci
83362306a36Sopenharmony_ci#endif /* BITS_PER_LONG == 64 && defined(__BIG_ENDIAN) */
834