18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci
38c2ecf20Sopenharmony_ci#include <linux/ethtool_netlink.h>
48c2ecf20Sopenharmony_ci#include <linux/bitmap.h>
58c2ecf20Sopenharmony_ci#include "netlink.h"
68c2ecf20Sopenharmony_ci#include "bitset.h"
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/* Some bitmaps are internally represented as an array of unsigned long, some
98c2ecf20Sopenharmony_ci * as an array of u32 (some even as single u32 for now). To avoid the need of
108c2ecf20Sopenharmony_ci * wrappers on caller side, we provide two set of functions: those with "32"
118c2ecf20Sopenharmony_ci * suffix in their names expect u32 based bitmaps, those without it expect
128c2ecf20Sopenharmony_ci * unsigned long bitmaps.
138c2ecf20Sopenharmony_ci */
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_cistatic u32 ethnl_lower_bits(unsigned int n)
168c2ecf20Sopenharmony_ci{
178c2ecf20Sopenharmony_ci	return ~(u32)0 >> (32 - n % 32);
188c2ecf20Sopenharmony_ci}
198c2ecf20Sopenharmony_ci
208c2ecf20Sopenharmony_cistatic u32 ethnl_upper_bits(unsigned int n)
218c2ecf20Sopenharmony_ci{
228c2ecf20Sopenharmony_ci	return ~(u32)0 << (n % 32);
238c2ecf20Sopenharmony_ci}
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ci/**
268c2ecf20Sopenharmony_ci * ethnl_bitmap32_clear() - Clear u32 based bitmap
278c2ecf20Sopenharmony_ci * @dst:   bitmap to clear
288c2ecf20Sopenharmony_ci * @start: beginning of the interval
298c2ecf20Sopenharmony_ci * @end:   end of the interval
308c2ecf20Sopenharmony_ci * @mod:   set if bitmap was modified
318c2ecf20Sopenharmony_ci *
328c2ecf20Sopenharmony_ci * Clear @nbits bits of a bitmap with indices @start <= i < @end
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_cistatic void ethnl_bitmap32_clear(u32 *dst, unsigned int start, unsigned int end,
358c2ecf20Sopenharmony_ci				 bool *mod)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	unsigned int start_word = start / 32;
388c2ecf20Sopenharmony_ci	unsigned int end_word = end / 32;
398c2ecf20Sopenharmony_ci	unsigned int i;
408c2ecf20Sopenharmony_ci	u32 mask;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	if (end <= start)
438c2ecf20Sopenharmony_ci		return;
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (start % 32) {
468c2ecf20Sopenharmony_ci		mask = ethnl_upper_bits(start);
478c2ecf20Sopenharmony_ci		if (end_word == start_word) {
488c2ecf20Sopenharmony_ci			mask &= ethnl_lower_bits(end);
498c2ecf20Sopenharmony_ci			if (dst[start_word] & mask) {
508c2ecf20Sopenharmony_ci				dst[start_word] &= ~mask;
518c2ecf20Sopenharmony_ci				*mod = true;
528c2ecf20Sopenharmony_ci			}
538c2ecf20Sopenharmony_ci			return;
548c2ecf20Sopenharmony_ci		}
558c2ecf20Sopenharmony_ci		if (dst[start_word] & mask) {
568c2ecf20Sopenharmony_ci			dst[start_word] &= ~mask;
578c2ecf20Sopenharmony_ci			*mod = true;
588c2ecf20Sopenharmony_ci		}
598c2ecf20Sopenharmony_ci		start_word++;
608c2ecf20Sopenharmony_ci	}
618c2ecf20Sopenharmony_ci
628c2ecf20Sopenharmony_ci	for (i = start_word; i < end_word; i++) {
638c2ecf20Sopenharmony_ci		if (dst[i]) {
648c2ecf20Sopenharmony_ci			dst[i] = 0;
658c2ecf20Sopenharmony_ci			*mod = true;
668c2ecf20Sopenharmony_ci		}
678c2ecf20Sopenharmony_ci	}
688c2ecf20Sopenharmony_ci	if (end % 32) {
698c2ecf20Sopenharmony_ci		mask = ethnl_lower_bits(end);
708c2ecf20Sopenharmony_ci		if (dst[end_word] & mask) {
718c2ecf20Sopenharmony_ci			dst[end_word] &= ~mask;
728c2ecf20Sopenharmony_ci			*mod = true;
738c2ecf20Sopenharmony_ci		}
748c2ecf20Sopenharmony_ci	}
758c2ecf20Sopenharmony_ci}
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci/**
788c2ecf20Sopenharmony_ci * ethnl_bitmap32_not_zero() - Check if any bit is set in an interval
798c2ecf20Sopenharmony_ci * @map:   bitmap to test
808c2ecf20Sopenharmony_ci * @start: beginning of the interval
818c2ecf20Sopenharmony_ci * @end:   end of the interval
828c2ecf20Sopenharmony_ci *
838c2ecf20Sopenharmony_ci * Return: true if there is non-zero bit with  index @start <= i < @end,
848c2ecf20Sopenharmony_ci *         false if the whole interval is zero
858c2ecf20Sopenharmony_ci */
868c2ecf20Sopenharmony_cistatic bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start,
878c2ecf20Sopenharmony_ci				    unsigned int end)
888c2ecf20Sopenharmony_ci{
898c2ecf20Sopenharmony_ci	unsigned int start_word = start / 32;
908c2ecf20Sopenharmony_ci	unsigned int end_word = end / 32;
918c2ecf20Sopenharmony_ci	u32 mask;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	if (end <= start)
948c2ecf20Sopenharmony_ci		return true;
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	if (start % 32) {
978c2ecf20Sopenharmony_ci		mask = ethnl_upper_bits(start);
988c2ecf20Sopenharmony_ci		if (end_word == start_word) {
998c2ecf20Sopenharmony_ci			mask &= ethnl_lower_bits(end);
1008c2ecf20Sopenharmony_ci			return map[start_word] & mask;
1018c2ecf20Sopenharmony_ci		}
1028c2ecf20Sopenharmony_ci		if (map[start_word] & mask)
1038c2ecf20Sopenharmony_ci			return true;
1048c2ecf20Sopenharmony_ci		start_word++;
1058c2ecf20Sopenharmony_ci	}
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	if (!memchr_inv(map + start_word, '\0',
1088c2ecf20Sopenharmony_ci			(end_word - start_word) * sizeof(u32)))
1098c2ecf20Sopenharmony_ci		return true;
1108c2ecf20Sopenharmony_ci	if (end % 32 == 0)
1118c2ecf20Sopenharmony_ci		return true;
1128c2ecf20Sopenharmony_ci	return map[end_word] & ethnl_lower_bits(end);
1138c2ecf20Sopenharmony_ci}
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ci/**
1168c2ecf20Sopenharmony_ci * ethnl_bitmap32_update() - Modify u32 based bitmap according to value/mask
1178c2ecf20Sopenharmony_ci *			     pair
1188c2ecf20Sopenharmony_ci * @dst:   bitmap to update
1198c2ecf20Sopenharmony_ci * @nbits: bit size of the bitmap
1208c2ecf20Sopenharmony_ci * @value: values to set
1218c2ecf20Sopenharmony_ci * @mask:  mask of bits to set
1228c2ecf20Sopenharmony_ci * @mod:   set to true if bitmap is modified, preserve if not
1238c2ecf20Sopenharmony_ci *
1248c2ecf20Sopenharmony_ci * Set bits in @dst bitmap which are set in @mask to values from @value, leave
1258c2ecf20Sopenharmony_ci * the rest untouched. If destination bitmap was modified, set @mod to true,
1268c2ecf20Sopenharmony_ci * leave as it is if not.
1278c2ecf20Sopenharmony_ci */
1288c2ecf20Sopenharmony_cistatic void ethnl_bitmap32_update(u32 *dst, unsigned int nbits,
1298c2ecf20Sopenharmony_ci				  const u32 *value, const u32 *mask, bool *mod)
1308c2ecf20Sopenharmony_ci{
1318c2ecf20Sopenharmony_ci	while (nbits > 0) {
1328c2ecf20Sopenharmony_ci		u32 real_mask = mask ? *mask : ~(u32)0;
1338c2ecf20Sopenharmony_ci		u32 new_value;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci		if (nbits < 32)
1368c2ecf20Sopenharmony_ci			real_mask &= ethnl_lower_bits(nbits);
1378c2ecf20Sopenharmony_ci		new_value = (*dst & ~real_mask) | (*value & real_mask);
1388c2ecf20Sopenharmony_ci		if (new_value != *dst) {
1398c2ecf20Sopenharmony_ci			*dst = new_value;
1408c2ecf20Sopenharmony_ci			*mod = true;
1418c2ecf20Sopenharmony_ci		}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci		if (nbits <= 32)
1448c2ecf20Sopenharmony_ci			break;
1458c2ecf20Sopenharmony_ci		dst++;
1468c2ecf20Sopenharmony_ci		nbits -= 32;
1478c2ecf20Sopenharmony_ci		value++;
1488c2ecf20Sopenharmony_ci		if (mask)
1498c2ecf20Sopenharmony_ci			mask++;
1508c2ecf20Sopenharmony_ci	}
1518c2ecf20Sopenharmony_ci}
1528c2ecf20Sopenharmony_ci
1538c2ecf20Sopenharmony_cistatic bool ethnl_bitmap32_test_bit(const u32 *map, unsigned int index)
1548c2ecf20Sopenharmony_ci{
1558c2ecf20Sopenharmony_ci	return map[index / 32] & (1U << (index % 32));
1568c2ecf20Sopenharmony_ci}
1578c2ecf20Sopenharmony_ci
1588c2ecf20Sopenharmony_ci/**
1598c2ecf20Sopenharmony_ci * ethnl_bitset32_size() - Calculate size of bitset nested attribute
1608c2ecf20Sopenharmony_ci * @val:     value bitmap (u32 based)
1618c2ecf20Sopenharmony_ci * @mask:    mask bitmap (u32 based, optional)
1628c2ecf20Sopenharmony_ci * @nbits:   bit length of the bitset
1638c2ecf20Sopenharmony_ci * @names:   array of bit names (optional)
1648c2ecf20Sopenharmony_ci * @compact: assume compact format for output
1658c2ecf20Sopenharmony_ci *
1668c2ecf20Sopenharmony_ci * Estimate length of netlink attribute composed by a later call to
1678c2ecf20Sopenharmony_ci * ethnl_put_bitset32() call with the same arguments.
1688c2ecf20Sopenharmony_ci *
1698c2ecf20Sopenharmony_ci * Return: negative error code or attribute length estimate
1708c2ecf20Sopenharmony_ci */
1718c2ecf20Sopenharmony_ciint ethnl_bitset32_size(const u32 *val, const u32 *mask, unsigned int nbits,
1728c2ecf20Sopenharmony_ci			ethnl_string_array_t names, bool compact)
1738c2ecf20Sopenharmony_ci{
1748c2ecf20Sopenharmony_ci	unsigned int len = 0;
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_ci	/* list flag */
1778c2ecf20Sopenharmony_ci	if (!mask)
1788c2ecf20Sopenharmony_ci		len += nla_total_size(sizeof(u32));
1798c2ecf20Sopenharmony_ci	/* size */
1808c2ecf20Sopenharmony_ci	len += nla_total_size(sizeof(u32));
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci	if (compact) {
1838c2ecf20Sopenharmony_ci		unsigned int nwords = DIV_ROUND_UP(nbits, 32);
1848c2ecf20Sopenharmony_ci
1858c2ecf20Sopenharmony_ci		/* value, mask */
1868c2ecf20Sopenharmony_ci		len += (mask ? 2 : 1) * nla_total_size(nwords * sizeof(u32));
1878c2ecf20Sopenharmony_ci	} else {
1888c2ecf20Sopenharmony_ci		unsigned int bits_len = 0;
1898c2ecf20Sopenharmony_ci		unsigned int bit_len, i;
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_ci		for (i = 0; i < nbits; i++) {
1928c2ecf20Sopenharmony_ci			const char *name = names ? names[i] : NULL;
1938c2ecf20Sopenharmony_ci
1948c2ecf20Sopenharmony_ci			if (!ethnl_bitmap32_test_bit(mask ?: val, i))
1958c2ecf20Sopenharmony_ci				continue;
1968c2ecf20Sopenharmony_ci			/* index */
1978c2ecf20Sopenharmony_ci			bit_len = nla_total_size(sizeof(u32));
1988c2ecf20Sopenharmony_ci			/* name */
1998c2ecf20Sopenharmony_ci			if (name)
2008c2ecf20Sopenharmony_ci				bit_len += ethnl_strz_size(name);
2018c2ecf20Sopenharmony_ci			/* value */
2028c2ecf20Sopenharmony_ci			if (mask && ethnl_bitmap32_test_bit(val, i))
2038c2ecf20Sopenharmony_ci				bit_len += nla_total_size(0);
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci			/* bit nest */
2068c2ecf20Sopenharmony_ci			bits_len += nla_total_size(bit_len);
2078c2ecf20Sopenharmony_ci		}
2088c2ecf20Sopenharmony_ci		/* bits nest */
2098c2ecf20Sopenharmony_ci		len += nla_total_size(bits_len);
2108c2ecf20Sopenharmony_ci	}
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci	/* outermost nest */
2138c2ecf20Sopenharmony_ci	return nla_total_size(len);
2148c2ecf20Sopenharmony_ci}
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_ci/**
2178c2ecf20Sopenharmony_ci * ethnl_put_bitset32() - Put a bitset nest into a message
2188c2ecf20Sopenharmony_ci * @skb:      skb with the message
2198c2ecf20Sopenharmony_ci * @attrtype: attribute type for the bitset nest
2208c2ecf20Sopenharmony_ci * @val:      value bitmap (u32 based)
2218c2ecf20Sopenharmony_ci * @mask:     mask bitmap (u32 based, optional)
2228c2ecf20Sopenharmony_ci * @nbits:    bit length of the bitset
2238c2ecf20Sopenharmony_ci * @names:    array of bit names (optional)
2248c2ecf20Sopenharmony_ci * @compact:  use compact format for the output
2258c2ecf20Sopenharmony_ci *
2268c2ecf20Sopenharmony_ci * Compose a nested attribute representing a bitset. If @mask is null, simple
2278c2ecf20Sopenharmony_ci * bitmap (bit list) is created, if @mask is provided, represent a value/mask
2288c2ecf20Sopenharmony_ci * pair. Bit names are only used in verbose mode and when provided by calller.
2298c2ecf20Sopenharmony_ci *
2308c2ecf20Sopenharmony_ci * Return: 0 on success, negative error value on error
2318c2ecf20Sopenharmony_ci */
2328c2ecf20Sopenharmony_ciint ethnl_put_bitset32(struct sk_buff *skb, int attrtype, const u32 *val,
2338c2ecf20Sopenharmony_ci		       const u32 *mask, unsigned int nbits,
2348c2ecf20Sopenharmony_ci		       ethnl_string_array_t names, bool compact)
2358c2ecf20Sopenharmony_ci{
2368c2ecf20Sopenharmony_ci	struct nlattr *nest;
2378c2ecf20Sopenharmony_ci	struct nlattr *attr;
2388c2ecf20Sopenharmony_ci
2398c2ecf20Sopenharmony_ci	nest = nla_nest_start(skb, attrtype);
2408c2ecf20Sopenharmony_ci	if (!nest)
2418c2ecf20Sopenharmony_ci		return -EMSGSIZE;
2428c2ecf20Sopenharmony_ci
2438c2ecf20Sopenharmony_ci	if (!mask && nla_put_flag(skb, ETHTOOL_A_BITSET_NOMASK))
2448c2ecf20Sopenharmony_ci		goto nla_put_failure;
2458c2ecf20Sopenharmony_ci	if (nla_put_u32(skb, ETHTOOL_A_BITSET_SIZE, nbits))
2468c2ecf20Sopenharmony_ci		goto nla_put_failure;
2478c2ecf20Sopenharmony_ci	if (compact) {
2488c2ecf20Sopenharmony_ci		unsigned int nwords = DIV_ROUND_UP(nbits, 32);
2498c2ecf20Sopenharmony_ci		unsigned int nbytes = nwords * sizeof(u32);
2508c2ecf20Sopenharmony_ci		u32 *dst;
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci		attr = nla_reserve(skb, ETHTOOL_A_BITSET_VALUE, nbytes);
2538c2ecf20Sopenharmony_ci		if (!attr)
2548c2ecf20Sopenharmony_ci			goto nla_put_failure;
2558c2ecf20Sopenharmony_ci		dst = nla_data(attr);
2568c2ecf20Sopenharmony_ci		memcpy(dst, val, nbytes);
2578c2ecf20Sopenharmony_ci		if (nbits % 32)
2588c2ecf20Sopenharmony_ci			dst[nwords - 1] &= ethnl_lower_bits(nbits);
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci		if (mask) {
2618c2ecf20Sopenharmony_ci			attr = nla_reserve(skb, ETHTOOL_A_BITSET_MASK, nbytes);
2628c2ecf20Sopenharmony_ci			if (!attr)
2638c2ecf20Sopenharmony_ci				goto nla_put_failure;
2648c2ecf20Sopenharmony_ci			dst = nla_data(attr);
2658c2ecf20Sopenharmony_ci			memcpy(dst, mask, nbytes);
2668c2ecf20Sopenharmony_ci			if (nbits % 32)
2678c2ecf20Sopenharmony_ci				dst[nwords - 1] &= ethnl_lower_bits(nbits);
2688c2ecf20Sopenharmony_ci		}
2698c2ecf20Sopenharmony_ci	} else {
2708c2ecf20Sopenharmony_ci		struct nlattr *bits;
2718c2ecf20Sopenharmony_ci		unsigned int i;
2728c2ecf20Sopenharmony_ci
2738c2ecf20Sopenharmony_ci		bits = nla_nest_start(skb, ETHTOOL_A_BITSET_BITS);
2748c2ecf20Sopenharmony_ci		if (!bits)
2758c2ecf20Sopenharmony_ci			goto nla_put_failure;
2768c2ecf20Sopenharmony_ci		for (i = 0; i < nbits; i++) {
2778c2ecf20Sopenharmony_ci			const char *name = names ? names[i] : NULL;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci			if (!ethnl_bitmap32_test_bit(mask ?: val, i))
2808c2ecf20Sopenharmony_ci				continue;
2818c2ecf20Sopenharmony_ci			attr = nla_nest_start(skb, ETHTOOL_A_BITSET_BITS_BIT);
2828c2ecf20Sopenharmony_ci			if (!attr)
2838c2ecf20Sopenharmony_ci				goto nla_put_failure;
2848c2ecf20Sopenharmony_ci			if (nla_put_u32(skb, ETHTOOL_A_BITSET_BIT_INDEX, i))
2858c2ecf20Sopenharmony_ci				goto nla_put_failure;
2868c2ecf20Sopenharmony_ci			if (name &&
2878c2ecf20Sopenharmony_ci			    ethnl_put_strz(skb, ETHTOOL_A_BITSET_BIT_NAME, name))
2888c2ecf20Sopenharmony_ci				goto nla_put_failure;
2898c2ecf20Sopenharmony_ci			if (mask && ethnl_bitmap32_test_bit(val, i) &&
2908c2ecf20Sopenharmony_ci			    nla_put_flag(skb, ETHTOOL_A_BITSET_BIT_VALUE))
2918c2ecf20Sopenharmony_ci				goto nla_put_failure;
2928c2ecf20Sopenharmony_ci			nla_nest_end(skb, attr);
2938c2ecf20Sopenharmony_ci		}
2948c2ecf20Sopenharmony_ci		nla_nest_end(skb, bits);
2958c2ecf20Sopenharmony_ci	}
2968c2ecf20Sopenharmony_ci
2978c2ecf20Sopenharmony_ci	nla_nest_end(skb, nest);
2988c2ecf20Sopenharmony_ci	return 0;
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cinla_put_failure:
3018c2ecf20Sopenharmony_ci	nla_nest_cancel(skb, nest);
3028c2ecf20Sopenharmony_ci	return -EMSGSIZE;
3038c2ecf20Sopenharmony_ci}
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_cistatic const struct nla_policy bitset_policy[] = {
3068c2ecf20Sopenharmony_ci	[ETHTOOL_A_BITSET_NOMASK]	= { .type = NLA_FLAG },
3078c2ecf20Sopenharmony_ci	[ETHTOOL_A_BITSET_SIZE]		= NLA_POLICY_MAX(NLA_U32,
3088c2ecf20Sopenharmony_ci							 ETHNL_MAX_BITSET_SIZE),
3098c2ecf20Sopenharmony_ci	[ETHTOOL_A_BITSET_BITS]		= { .type = NLA_NESTED },
3108c2ecf20Sopenharmony_ci	[ETHTOOL_A_BITSET_VALUE]	= { .type = NLA_BINARY },
3118c2ecf20Sopenharmony_ci	[ETHTOOL_A_BITSET_MASK]		= { .type = NLA_BINARY },
3128c2ecf20Sopenharmony_ci};
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_cistatic const struct nla_policy bit_policy[] = {
3158c2ecf20Sopenharmony_ci	[ETHTOOL_A_BITSET_BIT_INDEX]	= { .type = NLA_U32 },
3168c2ecf20Sopenharmony_ci	[ETHTOOL_A_BITSET_BIT_NAME]	= { .type = NLA_NUL_STRING },
3178c2ecf20Sopenharmony_ci	[ETHTOOL_A_BITSET_BIT_VALUE]	= { .type = NLA_FLAG },
3188c2ecf20Sopenharmony_ci};
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci/**
3218c2ecf20Sopenharmony_ci * ethnl_bitset_is_compact() - check if bitset attribute represents a compact
3228c2ecf20Sopenharmony_ci *			       bitset
3238c2ecf20Sopenharmony_ci * @bitset:  nested attribute representing a bitset
3248c2ecf20Sopenharmony_ci * @compact: pointer for return value
3258c2ecf20Sopenharmony_ci *
3268c2ecf20Sopenharmony_ci * Return: 0 on success, negative error code on failure
3278c2ecf20Sopenharmony_ci */
3288c2ecf20Sopenharmony_ciint ethnl_bitset_is_compact(const struct nlattr *bitset, bool *compact)
3298c2ecf20Sopenharmony_ci{
3308c2ecf20Sopenharmony_ci	struct nlattr *tb[ARRAY_SIZE(bitset_policy)];
3318c2ecf20Sopenharmony_ci	int ret;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	ret = nla_parse_nested(tb, ARRAY_SIZE(bitset_policy) - 1, bitset,
3348c2ecf20Sopenharmony_ci			       bitset_policy, NULL);
3358c2ecf20Sopenharmony_ci	if (ret < 0)
3368c2ecf20Sopenharmony_ci		return ret;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	if (tb[ETHTOOL_A_BITSET_BITS]) {
3398c2ecf20Sopenharmony_ci		if (tb[ETHTOOL_A_BITSET_VALUE] || tb[ETHTOOL_A_BITSET_MASK])
3408c2ecf20Sopenharmony_ci			return -EINVAL;
3418c2ecf20Sopenharmony_ci		*compact = false;
3428c2ecf20Sopenharmony_ci		return 0;
3438c2ecf20Sopenharmony_ci	}
3448c2ecf20Sopenharmony_ci	if (!tb[ETHTOOL_A_BITSET_SIZE] || !tb[ETHTOOL_A_BITSET_VALUE])
3458c2ecf20Sopenharmony_ci		return -EINVAL;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	*compact = true;
3488c2ecf20Sopenharmony_ci	return 0;
3498c2ecf20Sopenharmony_ci}
3508c2ecf20Sopenharmony_ci
3518c2ecf20Sopenharmony_ci/**
3528c2ecf20Sopenharmony_ci * ethnl_name_to_idx() - look up string index for a name
3538c2ecf20Sopenharmony_ci * @names:   array of ETH_GSTRING_LEN sized strings
3548c2ecf20Sopenharmony_ci * @n_names: number of strings in the array
3558c2ecf20Sopenharmony_ci * @name:    name to look up
3568c2ecf20Sopenharmony_ci *
3578c2ecf20Sopenharmony_ci * Return: index of the string if found, -ENOENT if not found
3588c2ecf20Sopenharmony_ci */
3598c2ecf20Sopenharmony_cistatic int ethnl_name_to_idx(ethnl_string_array_t names, unsigned int n_names,
3608c2ecf20Sopenharmony_ci			     const char *name)
3618c2ecf20Sopenharmony_ci{
3628c2ecf20Sopenharmony_ci	unsigned int i;
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	if (!names)
3658c2ecf20Sopenharmony_ci		return -ENOENT;
3668c2ecf20Sopenharmony_ci
3678c2ecf20Sopenharmony_ci	for (i = 0; i < n_names; i++) {
3688c2ecf20Sopenharmony_ci		/* names[i] may not be null terminated */
3698c2ecf20Sopenharmony_ci		if (!strncmp(names[i], name, ETH_GSTRING_LEN) &&
3708c2ecf20Sopenharmony_ci		    strlen(name) <= ETH_GSTRING_LEN)
3718c2ecf20Sopenharmony_ci			return i;
3728c2ecf20Sopenharmony_ci	}
3738c2ecf20Sopenharmony_ci
3748c2ecf20Sopenharmony_ci	return -ENOENT;
3758c2ecf20Sopenharmony_ci}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_cistatic int ethnl_parse_bit(unsigned int *index, bool *val, unsigned int nbits,
3788c2ecf20Sopenharmony_ci			   const struct nlattr *bit_attr, bool no_mask,
3798c2ecf20Sopenharmony_ci			   ethnl_string_array_t names,
3808c2ecf20Sopenharmony_ci			   struct netlink_ext_ack *extack)
3818c2ecf20Sopenharmony_ci{
3828c2ecf20Sopenharmony_ci	struct nlattr *tb[ARRAY_SIZE(bit_policy)];
3838c2ecf20Sopenharmony_ci	int ret, idx;
3848c2ecf20Sopenharmony_ci
3858c2ecf20Sopenharmony_ci	ret = nla_parse_nested(tb, ARRAY_SIZE(bit_policy) - 1, bit_attr,
3868c2ecf20Sopenharmony_ci			       bit_policy, extack);
3878c2ecf20Sopenharmony_ci	if (ret < 0)
3888c2ecf20Sopenharmony_ci		return ret;
3898c2ecf20Sopenharmony_ci
3908c2ecf20Sopenharmony_ci	if (tb[ETHTOOL_A_BITSET_BIT_INDEX]) {
3918c2ecf20Sopenharmony_ci		const char *name;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci		idx = nla_get_u32(tb[ETHTOOL_A_BITSET_BIT_INDEX]);
3948c2ecf20Sopenharmony_ci		if (idx >= nbits) {
3958c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_ATTR(extack,
3968c2ecf20Sopenharmony_ci					    tb[ETHTOOL_A_BITSET_BIT_INDEX],
3978c2ecf20Sopenharmony_ci					    "bit index too high");
3988c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
3998c2ecf20Sopenharmony_ci		}
4008c2ecf20Sopenharmony_ci		name = names ? names[idx] : NULL;
4018c2ecf20Sopenharmony_ci		if (tb[ETHTOOL_A_BITSET_BIT_NAME] && name &&
4028c2ecf20Sopenharmony_ci		    strncmp(nla_data(tb[ETHTOOL_A_BITSET_BIT_NAME]), name,
4038c2ecf20Sopenharmony_ci			    nla_len(tb[ETHTOOL_A_BITSET_BIT_NAME]))) {
4048c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_ATTR(extack, bit_attr,
4058c2ecf20Sopenharmony_ci					    "bit index and name mismatch");
4068c2ecf20Sopenharmony_ci			return -EINVAL;
4078c2ecf20Sopenharmony_ci		}
4088c2ecf20Sopenharmony_ci	} else if (tb[ETHTOOL_A_BITSET_BIT_NAME]) {
4098c2ecf20Sopenharmony_ci		idx = ethnl_name_to_idx(names, nbits,
4108c2ecf20Sopenharmony_ci					nla_data(tb[ETHTOOL_A_BITSET_BIT_NAME]));
4118c2ecf20Sopenharmony_ci		if (idx < 0) {
4128c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_ATTR(extack,
4138c2ecf20Sopenharmony_ci					    tb[ETHTOOL_A_BITSET_BIT_NAME],
4148c2ecf20Sopenharmony_ci					    "bit name not found");
4158c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
4168c2ecf20Sopenharmony_ci		}
4178c2ecf20Sopenharmony_ci	} else {
4188c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, bit_attr,
4198c2ecf20Sopenharmony_ci				    "neither bit index nor name specified");
4208c2ecf20Sopenharmony_ci		return -EINVAL;
4218c2ecf20Sopenharmony_ci	}
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci	*index = idx;
4248c2ecf20Sopenharmony_ci	*val = no_mask || tb[ETHTOOL_A_BITSET_BIT_VALUE];
4258c2ecf20Sopenharmony_ci	return 0;
4268c2ecf20Sopenharmony_ci}
4278c2ecf20Sopenharmony_ci
4288c2ecf20Sopenharmony_cistatic int
4298c2ecf20Sopenharmony_ciethnl_update_bitset32_verbose(u32 *bitmap, unsigned int nbits,
4308c2ecf20Sopenharmony_ci			      const struct nlattr *attr, struct nlattr **tb,
4318c2ecf20Sopenharmony_ci			      ethnl_string_array_t names,
4328c2ecf20Sopenharmony_ci			      struct netlink_ext_ack *extack, bool *mod)
4338c2ecf20Sopenharmony_ci{
4348c2ecf20Sopenharmony_ci	struct nlattr *bit_attr;
4358c2ecf20Sopenharmony_ci	bool no_mask;
4368c2ecf20Sopenharmony_ci	int rem;
4378c2ecf20Sopenharmony_ci	int ret;
4388c2ecf20Sopenharmony_ci
4398c2ecf20Sopenharmony_ci	if (tb[ETHTOOL_A_BITSET_VALUE]) {
4408c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_VALUE],
4418c2ecf20Sopenharmony_ci				    "value only allowed in compact bitset");
4428c2ecf20Sopenharmony_ci		return -EINVAL;
4438c2ecf20Sopenharmony_ci	}
4448c2ecf20Sopenharmony_ci	if (tb[ETHTOOL_A_BITSET_MASK]) {
4458c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK],
4468c2ecf20Sopenharmony_ci				    "mask only allowed in compact bitset");
4478c2ecf20Sopenharmony_ci		return -EINVAL;
4488c2ecf20Sopenharmony_ci	}
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	no_mask = tb[ETHTOOL_A_BITSET_NOMASK];
4518c2ecf20Sopenharmony_ci	if (no_mask)
4528c2ecf20Sopenharmony_ci		ethnl_bitmap32_clear(bitmap, 0, nbits, mod);
4538c2ecf20Sopenharmony_ci
4548c2ecf20Sopenharmony_ci	nla_for_each_nested(bit_attr, tb[ETHTOOL_A_BITSET_BITS], rem) {
4558c2ecf20Sopenharmony_ci		bool old_val, new_val;
4568c2ecf20Sopenharmony_ci		unsigned int idx;
4578c2ecf20Sopenharmony_ci
4588c2ecf20Sopenharmony_ci		if (nla_type(bit_attr) != ETHTOOL_A_BITSET_BITS_BIT) {
4598c2ecf20Sopenharmony_ci			NL_SET_ERR_MSG_ATTR(extack, bit_attr,
4608c2ecf20Sopenharmony_ci					    "only ETHTOOL_A_BITSET_BITS_BIT allowed in ETHTOOL_A_BITSET_BITS");
4618c2ecf20Sopenharmony_ci			return -EINVAL;
4628c2ecf20Sopenharmony_ci		}
4638c2ecf20Sopenharmony_ci		ret = ethnl_parse_bit(&idx, &new_val, nbits, bit_attr, no_mask,
4648c2ecf20Sopenharmony_ci				      names, extack);
4658c2ecf20Sopenharmony_ci		if (ret < 0)
4668c2ecf20Sopenharmony_ci			return ret;
4678c2ecf20Sopenharmony_ci		old_val = bitmap[idx / 32] & ((u32)1 << (idx % 32));
4688c2ecf20Sopenharmony_ci		if (new_val != old_val) {
4698c2ecf20Sopenharmony_ci			if (new_val)
4708c2ecf20Sopenharmony_ci				bitmap[idx / 32] |= ((u32)1 << (idx % 32));
4718c2ecf20Sopenharmony_ci			else
4728c2ecf20Sopenharmony_ci				bitmap[idx / 32] &= ~((u32)1 << (idx % 32));
4738c2ecf20Sopenharmony_ci			*mod = true;
4748c2ecf20Sopenharmony_ci		}
4758c2ecf20Sopenharmony_ci	}
4768c2ecf20Sopenharmony_ci
4778c2ecf20Sopenharmony_ci	return 0;
4788c2ecf20Sopenharmony_ci}
4798c2ecf20Sopenharmony_ci
4808c2ecf20Sopenharmony_cistatic int ethnl_compact_sanity_checks(unsigned int nbits,
4818c2ecf20Sopenharmony_ci				       const struct nlattr *nest,
4828c2ecf20Sopenharmony_ci				       struct nlattr **tb,
4838c2ecf20Sopenharmony_ci				       struct netlink_ext_ack *extack)
4848c2ecf20Sopenharmony_ci{
4858c2ecf20Sopenharmony_ci	bool no_mask = tb[ETHTOOL_A_BITSET_NOMASK];
4868c2ecf20Sopenharmony_ci	unsigned int attr_nbits, attr_nwords;
4878c2ecf20Sopenharmony_ci	const struct nlattr *test_attr;
4888c2ecf20Sopenharmony_ci
4898c2ecf20Sopenharmony_ci	if (no_mask && tb[ETHTOOL_A_BITSET_MASK]) {
4908c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK],
4918c2ecf20Sopenharmony_ci				    "mask not allowed in list bitset");
4928c2ecf20Sopenharmony_ci		return -EINVAL;
4938c2ecf20Sopenharmony_ci	}
4948c2ecf20Sopenharmony_ci	if (!tb[ETHTOOL_A_BITSET_SIZE]) {
4958c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, nest,
4968c2ecf20Sopenharmony_ci				    "missing size in compact bitset");
4978c2ecf20Sopenharmony_ci		return -EINVAL;
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci	if (!tb[ETHTOOL_A_BITSET_VALUE]) {
5008c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, nest,
5018c2ecf20Sopenharmony_ci				    "missing value in compact bitset");
5028c2ecf20Sopenharmony_ci		return -EINVAL;
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci	if (!no_mask && !tb[ETHTOOL_A_BITSET_MASK]) {
5058c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, nest,
5068c2ecf20Sopenharmony_ci				    "missing mask in compact nonlist bitset");
5078c2ecf20Sopenharmony_ci		return -EINVAL;
5088c2ecf20Sopenharmony_ci	}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	attr_nbits = nla_get_u32(tb[ETHTOOL_A_BITSET_SIZE]);
5118c2ecf20Sopenharmony_ci	attr_nwords = DIV_ROUND_UP(attr_nbits, 32);
5128c2ecf20Sopenharmony_ci	if (nla_len(tb[ETHTOOL_A_BITSET_VALUE]) != attr_nwords * sizeof(u32)) {
5138c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_VALUE],
5148c2ecf20Sopenharmony_ci				    "bitset value length does not match size");
5158c2ecf20Sopenharmony_ci		return -EINVAL;
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci	if (tb[ETHTOOL_A_BITSET_MASK] &&
5188c2ecf20Sopenharmony_ci	    nla_len(tb[ETHTOOL_A_BITSET_MASK]) != attr_nwords * sizeof(u32)) {
5198c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK],
5208c2ecf20Sopenharmony_ci				    "bitset mask length does not match size");
5218c2ecf20Sopenharmony_ci		return -EINVAL;
5228c2ecf20Sopenharmony_ci	}
5238c2ecf20Sopenharmony_ci	if (attr_nbits <= nbits)
5248c2ecf20Sopenharmony_ci		return 0;
5258c2ecf20Sopenharmony_ci
5268c2ecf20Sopenharmony_ci	test_attr = no_mask ? tb[ETHTOOL_A_BITSET_VALUE] :
5278c2ecf20Sopenharmony_ci			      tb[ETHTOOL_A_BITSET_MASK];
5288c2ecf20Sopenharmony_ci	if (ethnl_bitmap32_not_zero(nla_data(test_attr), nbits, attr_nbits)) {
5298c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, test_attr,
5308c2ecf20Sopenharmony_ci				    "cannot modify bits past kernel bitset size");
5318c2ecf20Sopenharmony_ci		return -EINVAL;
5328c2ecf20Sopenharmony_ci	}
5338c2ecf20Sopenharmony_ci	return 0;
5348c2ecf20Sopenharmony_ci}
5358c2ecf20Sopenharmony_ci
5368c2ecf20Sopenharmony_ci/**
5378c2ecf20Sopenharmony_ci * ethnl_update_bitset32() - Apply a bitset nest to a u32 based bitmap
5388c2ecf20Sopenharmony_ci * @bitmap:  bitmap to update
5398c2ecf20Sopenharmony_ci * @nbits:   size of the updated bitmap in bits
5408c2ecf20Sopenharmony_ci * @attr:    nest attribute to parse and apply
5418c2ecf20Sopenharmony_ci * @names:   array of bit names; may be null for compact format
5428c2ecf20Sopenharmony_ci * @extack:  extack for error reporting
5438c2ecf20Sopenharmony_ci * @mod:     set this to true if bitmap is modified, leave as it is if not
5448c2ecf20Sopenharmony_ci *
5458c2ecf20Sopenharmony_ci * Apply bitset netsted attribute to a bitmap. If the attribute represents
5468c2ecf20Sopenharmony_ci * a bit list, @bitmap is set to its contents; otherwise, bits in mask are
5478c2ecf20Sopenharmony_ci * set to values from value. Bitmaps in the attribute may be longer than
5488c2ecf20Sopenharmony_ci * @nbits but the message must not request modifying any bits past @nbits.
5498c2ecf20Sopenharmony_ci *
5508c2ecf20Sopenharmony_ci * Return: negative error code on failure, 0 on success
5518c2ecf20Sopenharmony_ci */
5528c2ecf20Sopenharmony_ciint ethnl_update_bitset32(u32 *bitmap, unsigned int nbits,
5538c2ecf20Sopenharmony_ci			  const struct nlattr *attr, ethnl_string_array_t names,
5548c2ecf20Sopenharmony_ci			  struct netlink_ext_ack *extack, bool *mod)
5558c2ecf20Sopenharmony_ci{
5568c2ecf20Sopenharmony_ci	struct nlattr *tb[ARRAY_SIZE(bitset_policy)];
5578c2ecf20Sopenharmony_ci	unsigned int change_bits;
5588c2ecf20Sopenharmony_ci	bool no_mask;
5598c2ecf20Sopenharmony_ci	int ret;
5608c2ecf20Sopenharmony_ci
5618c2ecf20Sopenharmony_ci	if (!attr)
5628c2ecf20Sopenharmony_ci		return 0;
5638c2ecf20Sopenharmony_ci	ret = nla_parse_nested(tb, ARRAY_SIZE(bitset_policy) - 1, attr,
5648c2ecf20Sopenharmony_ci			       bitset_policy, extack);
5658c2ecf20Sopenharmony_ci	if (ret < 0)
5668c2ecf20Sopenharmony_ci		return ret;
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	if (tb[ETHTOOL_A_BITSET_BITS])
5698c2ecf20Sopenharmony_ci		return ethnl_update_bitset32_verbose(bitmap, nbits, attr, tb,
5708c2ecf20Sopenharmony_ci						     names, extack, mod);
5718c2ecf20Sopenharmony_ci	ret = ethnl_compact_sanity_checks(nbits, attr, tb, extack);
5728c2ecf20Sopenharmony_ci	if (ret < 0)
5738c2ecf20Sopenharmony_ci		return ret;
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	no_mask = tb[ETHTOOL_A_BITSET_NOMASK];
5768c2ecf20Sopenharmony_ci	change_bits = min_t(unsigned int,
5778c2ecf20Sopenharmony_ci			    nla_get_u32(tb[ETHTOOL_A_BITSET_SIZE]), nbits);
5788c2ecf20Sopenharmony_ci	ethnl_bitmap32_update(bitmap, change_bits,
5798c2ecf20Sopenharmony_ci			      nla_data(tb[ETHTOOL_A_BITSET_VALUE]),
5808c2ecf20Sopenharmony_ci			      no_mask ? NULL :
5818c2ecf20Sopenharmony_ci					nla_data(tb[ETHTOOL_A_BITSET_MASK]),
5828c2ecf20Sopenharmony_ci			      mod);
5838c2ecf20Sopenharmony_ci	if (no_mask && change_bits < nbits)
5848c2ecf20Sopenharmony_ci		ethnl_bitmap32_clear(bitmap, change_bits, nbits, mod);
5858c2ecf20Sopenharmony_ci
5868c2ecf20Sopenharmony_ci	return 0;
5878c2ecf20Sopenharmony_ci}
5888c2ecf20Sopenharmony_ci
5898c2ecf20Sopenharmony_ci/**
5908c2ecf20Sopenharmony_ci * ethnl_parse_bitset() - Compute effective value and mask from bitset nest
5918c2ecf20Sopenharmony_ci * @val:     unsigned long based bitmap to put value into
5928c2ecf20Sopenharmony_ci * @mask:    unsigned long based bitmap to put mask into
5938c2ecf20Sopenharmony_ci * @nbits:   size of @val and @mask bitmaps
5948c2ecf20Sopenharmony_ci * @attr:    nest attribute to parse and apply
5958c2ecf20Sopenharmony_ci * @names:   array of bit names; may be null for compact format
5968c2ecf20Sopenharmony_ci * @extack:  extack for error reporting
5978c2ecf20Sopenharmony_ci *
5988c2ecf20Sopenharmony_ci * Provide @nbits size long bitmaps for value and mask so that
5998c2ecf20Sopenharmony_ci * x = (val & mask) | (x & ~mask) would modify any @nbits sized bitmap x
6008c2ecf20Sopenharmony_ci * the same way ethnl_update_bitset() with the same bitset attribute would.
6018c2ecf20Sopenharmony_ci *
6028c2ecf20Sopenharmony_ci * Return:   negative error code on failure, 0 on success
6038c2ecf20Sopenharmony_ci */
6048c2ecf20Sopenharmony_ciint ethnl_parse_bitset(unsigned long *val, unsigned long *mask,
6058c2ecf20Sopenharmony_ci		       unsigned int nbits, const struct nlattr *attr,
6068c2ecf20Sopenharmony_ci		       ethnl_string_array_t names,
6078c2ecf20Sopenharmony_ci		       struct netlink_ext_ack *extack)
6088c2ecf20Sopenharmony_ci{
6098c2ecf20Sopenharmony_ci	struct nlattr *tb[ARRAY_SIZE(bitset_policy)];
6108c2ecf20Sopenharmony_ci	const struct nlattr *bit_attr;
6118c2ecf20Sopenharmony_ci	bool no_mask;
6128c2ecf20Sopenharmony_ci	int rem;
6138c2ecf20Sopenharmony_ci	int ret;
6148c2ecf20Sopenharmony_ci
6158c2ecf20Sopenharmony_ci	if (!attr)
6168c2ecf20Sopenharmony_ci		return 0;
6178c2ecf20Sopenharmony_ci	ret = nla_parse_nested(tb, ARRAY_SIZE(bitset_policy) - 1, attr,
6188c2ecf20Sopenharmony_ci			       bitset_policy, extack);
6198c2ecf20Sopenharmony_ci	if (ret < 0)
6208c2ecf20Sopenharmony_ci		return ret;
6218c2ecf20Sopenharmony_ci	no_mask = tb[ETHTOOL_A_BITSET_NOMASK];
6228c2ecf20Sopenharmony_ci
6238c2ecf20Sopenharmony_ci	if (!tb[ETHTOOL_A_BITSET_BITS]) {
6248c2ecf20Sopenharmony_ci		unsigned int change_bits;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci		ret = ethnl_compact_sanity_checks(nbits, attr, tb, extack);
6278c2ecf20Sopenharmony_ci		if (ret < 0)
6288c2ecf20Sopenharmony_ci			return ret;
6298c2ecf20Sopenharmony_ci
6308c2ecf20Sopenharmony_ci		change_bits = nla_get_u32(tb[ETHTOOL_A_BITSET_SIZE]);
6318c2ecf20Sopenharmony_ci		if (change_bits > nbits)
6328c2ecf20Sopenharmony_ci			change_bits = nbits;
6338c2ecf20Sopenharmony_ci		bitmap_from_arr32(val, nla_data(tb[ETHTOOL_A_BITSET_VALUE]),
6348c2ecf20Sopenharmony_ci				  change_bits);
6358c2ecf20Sopenharmony_ci		if (change_bits < nbits)
6368c2ecf20Sopenharmony_ci			bitmap_clear(val, change_bits, nbits - change_bits);
6378c2ecf20Sopenharmony_ci		if (no_mask) {
6388c2ecf20Sopenharmony_ci			bitmap_fill(mask, nbits);
6398c2ecf20Sopenharmony_ci		} else {
6408c2ecf20Sopenharmony_ci			bitmap_from_arr32(mask,
6418c2ecf20Sopenharmony_ci					  nla_data(tb[ETHTOOL_A_BITSET_MASK]),
6428c2ecf20Sopenharmony_ci					  change_bits);
6438c2ecf20Sopenharmony_ci			if (change_bits < nbits)
6448c2ecf20Sopenharmony_ci				bitmap_clear(mask, change_bits,
6458c2ecf20Sopenharmony_ci					     nbits - change_bits);
6468c2ecf20Sopenharmony_ci		}
6478c2ecf20Sopenharmony_ci
6488c2ecf20Sopenharmony_ci		return 0;
6498c2ecf20Sopenharmony_ci	}
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_ci	if (tb[ETHTOOL_A_BITSET_VALUE]) {
6528c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_VALUE],
6538c2ecf20Sopenharmony_ci				    "value only allowed in compact bitset");
6548c2ecf20Sopenharmony_ci		return -EINVAL;
6558c2ecf20Sopenharmony_ci	}
6568c2ecf20Sopenharmony_ci	if (tb[ETHTOOL_A_BITSET_MASK]) {
6578c2ecf20Sopenharmony_ci		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK],
6588c2ecf20Sopenharmony_ci				    "mask only allowed in compact bitset");
6598c2ecf20Sopenharmony_ci		return -EINVAL;
6608c2ecf20Sopenharmony_ci	}
6618c2ecf20Sopenharmony_ci
6628c2ecf20Sopenharmony_ci	bitmap_zero(val, nbits);
6638c2ecf20Sopenharmony_ci	if (no_mask)
6648c2ecf20Sopenharmony_ci		bitmap_fill(mask, nbits);
6658c2ecf20Sopenharmony_ci	else
6668c2ecf20Sopenharmony_ci		bitmap_zero(mask, nbits);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	nla_for_each_nested(bit_attr, tb[ETHTOOL_A_BITSET_BITS], rem) {
6698c2ecf20Sopenharmony_ci		unsigned int idx;
6708c2ecf20Sopenharmony_ci		bool bit_val;
6718c2ecf20Sopenharmony_ci
6728c2ecf20Sopenharmony_ci		ret = ethnl_parse_bit(&idx, &bit_val, nbits, bit_attr, no_mask,
6738c2ecf20Sopenharmony_ci				      names, extack);
6748c2ecf20Sopenharmony_ci		if (ret < 0)
6758c2ecf20Sopenharmony_ci			return ret;
6768c2ecf20Sopenharmony_ci		if (bit_val)
6778c2ecf20Sopenharmony_ci			__set_bit(idx, val);
6788c2ecf20Sopenharmony_ci		if (!no_mask)
6798c2ecf20Sopenharmony_ci			__set_bit(idx, mask);
6808c2ecf20Sopenharmony_ci	}
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	return 0;
6838c2ecf20Sopenharmony_ci}
6848c2ecf20Sopenharmony_ci
6858c2ecf20Sopenharmony_ci#if BITS_PER_LONG == 64 && defined(__BIG_ENDIAN)
6868c2ecf20Sopenharmony_ci
6878c2ecf20Sopenharmony_ci/* 64-bit big endian architectures are the only case when u32 based bitmaps
6888c2ecf20Sopenharmony_ci * and unsigned long based bitmaps have different memory layout so that we
6898c2ecf20Sopenharmony_ci * cannot simply cast the latter to the former and need actual wrappers
6908c2ecf20Sopenharmony_ci * converting the latter to the former.
6918c2ecf20Sopenharmony_ci *
6928c2ecf20Sopenharmony_ci * To reduce the number of slab allocations, the wrappers use fixed size local
6938c2ecf20Sopenharmony_ci * variables for bitmaps up to ETHNL_SMALL_BITMAP_BITS bits which is the
6948c2ecf20Sopenharmony_ci * majority of bitmaps used by ethtool.
6958c2ecf20Sopenharmony_ci */
6968c2ecf20Sopenharmony_ci#define ETHNL_SMALL_BITMAP_BITS 128
6978c2ecf20Sopenharmony_ci#define ETHNL_SMALL_BITMAP_WORDS DIV_ROUND_UP(ETHNL_SMALL_BITMAP_BITS, 32)
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ciint ethnl_bitset_size(const unsigned long *val, const unsigned long *mask,
7008c2ecf20Sopenharmony_ci		      unsigned int nbits, ethnl_string_array_t names,
7018c2ecf20Sopenharmony_ci		      bool compact)
7028c2ecf20Sopenharmony_ci{
7038c2ecf20Sopenharmony_ci	u32 small_mask32[ETHNL_SMALL_BITMAP_WORDS];
7048c2ecf20Sopenharmony_ci	u32 small_val32[ETHNL_SMALL_BITMAP_WORDS];
7058c2ecf20Sopenharmony_ci	u32 *mask32;
7068c2ecf20Sopenharmony_ci	u32 *val32;
7078c2ecf20Sopenharmony_ci	int ret;
7088c2ecf20Sopenharmony_ci
7098c2ecf20Sopenharmony_ci	if (nbits > ETHNL_SMALL_BITMAP_BITS) {
7108c2ecf20Sopenharmony_ci		unsigned int nwords = DIV_ROUND_UP(nbits, 32);
7118c2ecf20Sopenharmony_ci
7128c2ecf20Sopenharmony_ci		val32 = kmalloc_array(2 * nwords, sizeof(u32), GFP_KERNEL);
7138c2ecf20Sopenharmony_ci		if (!val32)
7148c2ecf20Sopenharmony_ci			return -ENOMEM;
7158c2ecf20Sopenharmony_ci		mask32 = val32 + nwords;
7168c2ecf20Sopenharmony_ci	} else {
7178c2ecf20Sopenharmony_ci		val32 = small_val32;
7188c2ecf20Sopenharmony_ci		mask32 = small_mask32;
7198c2ecf20Sopenharmony_ci	}
7208c2ecf20Sopenharmony_ci
7218c2ecf20Sopenharmony_ci	bitmap_to_arr32(val32, val, nbits);
7228c2ecf20Sopenharmony_ci	if (mask)
7238c2ecf20Sopenharmony_ci		bitmap_to_arr32(mask32, mask, nbits);
7248c2ecf20Sopenharmony_ci	else
7258c2ecf20Sopenharmony_ci		mask32 = NULL;
7268c2ecf20Sopenharmony_ci	ret = ethnl_bitset32_size(val32, mask32, nbits, names, compact);
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci	if (nbits > ETHNL_SMALL_BITMAP_BITS)
7298c2ecf20Sopenharmony_ci		kfree(val32);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci	return ret;
7328c2ecf20Sopenharmony_ci}
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ciint ethnl_put_bitset(struct sk_buff *skb, int attrtype,
7358c2ecf20Sopenharmony_ci		     const unsigned long *val, const unsigned long *mask,
7368c2ecf20Sopenharmony_ci		     unsigned int nbits, ethnl_string_array_t names,
7378c2ecf20Sopenharmony_ci		     bool compact)
7388c2ecf20Sopenharmony_ci{
7398c2ecf20Sopenharmony_ci	u32 small_mask32[ETHNL_SMALL_BITMAP_WORDS];
7408c2ecf20Sopenharmony_ci	u32 small_val32[ETHNL_SMALL_BITMAP_WORDS];
7418c2ecf20Sopenharmony_ci	u32 *mask32;
7428c2ecf20Sopenharmony_ci	u32 *val32;
7438c2ecf20Sopenharmony_ci	int ret;
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci	if (nbits > ETHNL_SMALL_BITMAP_BITS) {
7468c2ecf20Sopenharmony_ci		unsigned int nwords = DIV_ROUND_UP(nbits, 32);
7478c2ecf20Sopenharmony_ci
7488c2ecf20Sopenharmony_ci		val32 = kmalloc_array(2 * nwords, sizeof(u32), GFP_KERNEL);
7498c2ecf20Sopenharmony_ci		if (!val32)
7508c2ecf20Sopenharmony_ci			return -ENOMEM;
7518c2ecf20Sopenharmony_ci		mask32 = val32 + nwords;
7528c2ecf20Sopenharmony_ci	} else {
7538c2ecf20Sopenharmony_ci		val32 = small_val32;
7548c2ecf20Sopenharmony_ci		mask32 = small_mask32;
7558c2ecf20Sopenharmony_ci	}
7568c2ecf20Sopenharmony_ci
7578c2ecf20Sopenharmony_ci	bitmap_to_arr32(val32, val, nbits);
7588c2ecf20Sopenharmony_ci	if (mask)
7598c2ecf20Sopenharmony_ci		bitmap_to_arr32(mask32, mask, nbits);
7608c2ecf20Sopenharmony_ci	else
7618c2ecf20Sopenharmony_ci		mask32 = NULL;
7628c2ecf20Sopenharmony_ci	ret = ethnl_put_bitset32(skb, attrtype, val32, mask32, nbits, names,
7638c2ecf20Sopenharmony_ci				 compact);
7648c2ecf20Sopenharmony_ci
7658c2ecf20Sopenharmony_ci	if (nbits > ETHNL_SMALL_BITMAP_BITS)
7668c2ecf20Sopenharmony_ci		kfree(val32);
7678c2ecf20Sopenharmony_ci
7688c2ecf20Sopenharmony_ci	return ret;
7698c2ecf20Sopenharmony_ci}
7708c2ecf20Sopenharmony_ci
7718c2ecf20Sopenharmony_ciint ethnl_update_bitset(unsigned long *bitmap, unsigned int nbits,
7728c2ecf20Sopenharmony_ci			const struct nlattr *attr, ethnl_string_array_t names,
7738c2ecf20Sopenharmony_ci			struct netlink_ext_ack *extack, bool *mod)
7748c2ecf20Sopenharmony_ci{
7758c2ecf20Sopenharmony_ci	u32 small_bitmap32[ETHNL_SMALL_BITMAP_WORDS];
7768c2ecf20Sopenharmony_ci	u32 *bitmap32 = small_bitmap32;
7778c2ecf20Sopenharmony_ci	bool u32_mod = false;
7788c2ecf20Sopenharmony_ci	int ret;
7798c2ecf20Sopenharmony_ci
7808c2ecf20Sopenharmony_ci	if (nbits > ETHNL_SMALL_BITMAP_BITS) {
7818c2ecf20Sopenharmony_ci		unsigned int dst_words = DIV_ROUND_UP(nbits, 32);
7828c2ecf20Sopenharmony_ci
7838c2ecf20Sopenharmony_ci		bitmap32 = kmalloc_array(dst_words, sizeof(u32), GFP_KERNEL);
7848c2ecf20Sopenharmony_ci		if (!bitmap32)
7858c2ecf20Sopenharmony_ci			return -ENOMEM;
7868c2ecf20Sopenharmony_ci	}
7878c2ecf20Sopenharmony_ci
7888c2ecf20Sopenharmony_ci	bitmap_to_arr32(bitmap32, bitmap, nbits);
7898c2ecf20Sopenharmony_ci	ret = ethnl_update_bitset32(bitmap32, nbits, attr, names, extack,
7908c2ecf20Sopenharmony_ci				    &u32_mod);
7918c2ecf20Sopenharmony_ci	if (u32_mod) {
7928c2ecf20Sopenharmony_ci		bitmap_from_arr32(bitmap, bitmap32, nbits);
7938c2ecf20Sopenharmony_ci		*mod = true;
7948c2ecf20Sopenharmony_ci	}
7958c2ecf20Sopenharmony_ci
7968c2ecf20Sopenharmony_ci	if (nbits > ETHNL_SMALL_BITMAP_BITS)
7978c2ecf20Sopenharmony_ci		kfree(bitmap32);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci	return ret;
8008c2ecf20Sopenharmony_ci}
8018c2ecf20Sopenharmony_ci
8028c2ecf20Sopenharmony_ci#else
8038c2ecf20Sopenharmony_ci
8048c2ecf20Sopenharmony_ci/* On little endian 64-bit and all 32-bit architectures, an unsigned long
8058c2ecf20Sopenharmony_ci * based bitmap can be interpreted as u32 based one using a simple cast.
8068c2ecf20Sopenharmony_ci */
8078c2ecf20Sopenharmony_ci
8088c2ecf20Sopenharmony_ciint ethnl_bitset_size(const unsigned long *val, const unsigned long *mask,
8098c2ecf20Sopenharmony_ci		      unsigned int nbits, ethnl_string_array_t names,
8108c2ecf20Sopenharmony_ci		      bool compact)
8118c2ecf20Sopenharmony_ci{
8128c2ecf20Sopenharmony_ci	return ethnl_bitset32_size((const u32 *)val, (const u32 *)mask, nbits,
8138c2ecf20Sopenharmony_ci				   names, compact);
8148c2ecf20Sopenharmony_ci}
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ciint ethnl_put_bitset(struct sk_buff *skb, int attrtype,
8178c2ecf20Sopenharmony_ci		     const unsigned long *val, const unsigned long *mask,
8188c2ecf20Sopenharmony_ci		     unsigned int nbits, ethnl_string_array_t names,
8198c2ecf20Sopenharmony_ci		     bool compact)
8208c2ecf20Sopenharmony_ci{
8218c2ecf20Sopenharmony_ci	return ethnl_put_bitset32(skb, attrtype, (const u32 *)val,
8228c2ecf20Sopenharmony_ci				  (const u32 *)mask, nbits, names, compact);
8238c2ecf20Sopenharmony_ci}
8248c2ecf20Sopenharmony_ci
8258c2ecf20Sopenharmony_ciint ethnl_update_bitset(unsigned long *bitmap, unsigned int nbits,
8268c2ecf20Sopenharmony_ci			const struct nlattr *attr, ethnl_string_array_t names,
8278c2ecf20Sopenharmony_ci			struct netlink_ext_ack *extack, bool *mod)
8288c2ecf20Sopenharmony_ci{
8298c2ecf20Sopenharmony_ci	return ethnl_update_bitset32((u32 *)bitmap, nbits, attr, names, extack,
8308c2ecf20Sopenharmony_ci				     mod);
8318c2ecf20Sopenharmony_ci}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci#endif /* BITS_PER_LONG == 64 && defined(__BIG_ENDIAN) */
834