// SPDX-License-Identifier: GPL-2.0-only
#include <linux/ethtool_netlink.h>
#include <linux/bitmap.h>
#include "netlink.h"
#include "bitset.h"
/* Some bitmaps are internally represented as an array of unsigned long, some
* as an array of u32 (some even as single u32 for now). To avoid the need of
* wrappers on caller side, we provide two set of functions: those with "32"
* suffix in their names expect u32 based bitmaps, those without it expect
* unsigned long bitmaps.
*/
static u32 ethnl_lower_bits(unsigned int n)
{
return ~(u32)0 >> (32 - n % 32);
}
static u32 ethnl_upper_bits(unsigned int n)
{
return ~(u32)0 << (n % 32);
}
/**
* ethnl_bitmap32_clear() - Clear u32 based bitmap
* @dst: bitmap to clear
* @start: beginning of the interval
* @end: end of the interval
* @mod: set if bitmap was modified
*
* Clear @nbits bits of a bitmap with indices @start <= i < @end
*/
static void ethnl_bitmap32_clear(u32 *dst, unsigned int start, unsigned int end,
bool *mod)
{
unsigned int start_word = start / 32;
unsigned int end_word = end / 32;
unsigned int i;
u32 mask;
if (end <= start)
return;
if (start % 32) {
mask = ethnl_upper_bits(start);
if (end_word == start_word) {
mask &= ethnl_lower_bits(end);
if (dst[start_word] & mask) {
dst[start_word] &= ~mask;
*mod = true;
}
return;
}
if (dst[start_word] & mask) {
dst[start_word] &= ~mask;
*mod = true;
}
start_word++;
}
for (i = start_word; i < end_word; i++) {
if (dst[i]) {
dst[i] = 0;
*mod = true;
}
}
if (end % 32) {
mask = ethnl_lower_bits(end);
if (dst[end_word] & mask) {
dst[end_word] &= ~mask;
*mod = true;
}
}
}
/**
* ethnl_bitmap32_not_zero() - Check if any bit is set in an interval
* @map: bitmap to test
* @start: beginning of the interval
* @end: end of the interval
*
* Return: true if there is non-zero bit with index @start <= i < @end,
* false if the whole interval is zero
*/
static bool ethnl_bitmap32_not_zero(const u32 *map, unsigned int start,
unsigned int end)
{
unsigned int start_word = start / 32;
unsigned int end_word = end / 32;
u32 mask;
if (end <= start)
return true;
if (start % 32) {
mask = ethnl_upper_bits(start);
if (end_word == start_word) {
mask &= ethnl_lower_bits(end);
return map[start_word] & mask;
}
if (map[start_word] & mask)
return true;
start_word++;
}
if (!memchr_inv(map + start_word, '\0',
(end_word - start_word) * sizeof(u32)))
return true;
if (end % 32 == 0)
return true;
return map[end_word] & ethnl_lower_bits(end);
}
/**
* ethnl_bitmap32_update() - Modify u32 based bitmap according to value/mask
* pair
* @dst: bitmap to update
* @nbits: bit size of the bitmap
* @value: values to set
* @mask: mask of bits to set
* @mod: set to true if bitmap is modified, preserve if not
*
* Set bits in @dst bitmap which are set in @mask to values from @value, leave
* the rest untouched. If destination bitmap was modified, set @mod to true,
* leave as it is if not.
*/
static void ethnl_bitmap32_update(u32 *dst, unsigned int nbits,
const u32 *value, const u32 *mask, bool *mod)
{
while (nbits > 0) {
u32 real_mask = mask ? *mask : ~(u32)0;
u32 new_value;
if (nbits < 32)
real_mask &= ethnl_lower_bits(nbits);
new_value = (*dst & ~real_mask) | (*value & real_mask);
if (new_value != *dst) {
*dst = new_value;
*mod = true;
}
if (nbits <= 32)
break;
dst++;
nbits -= 32;
value++;
if (mask)
mask++;
}
}
static bool ethnl_bitmap32_test_bit(const u32 *map, unsigned int index)
{
return map[index / 32] & (1U << (index % 32));
}
/**
* ethnl_bitset32_size() - Calculate size of bitset nested attribute
* @val: value bitmap (u32 based)
* @mask: mask bitmap (u32 based, optional)
* @nbits: bit length of the bitset
* @names: array of bit names (optional)
* @compact: assume compact format for output
*
* Estimate length of netlink attribute composed by a later call to
* ethnl_put_bitset32() call with the same arguments.
*
* Return: negative error code or attribute length estimate
*/
int ethnl_bitset32_size(const u32 *val, const u32 *mask, unsigned int nbits,
ethnl_string_array_t names, bool compact)
{<