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