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