874 lines · 23.3 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-only #include <linux/ethtool_netlink.h>#include <linux/bitmap.h> #include "bitset.h"#include "netlink.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 false; 	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 false;	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){	unsigned int len = 0; 	/* list flag */	if (!mask)		len += nla_total_size(sizeof(u32));	/* size */	len += nla_total_size(sizeof(u32)); 	if (compact) {		unsigned int nwords = DIV_ROUND_UP(nbits, 32); 		/* value, mask */		len += (mask ? 2 : 1) * nla_total_size(nwords * sizeof(u32));	} else {		unsigned int bits_len = 0;		unsigned int bit_len, i; 		for (i = 0; i < nbits; i++) {			const char *name = names ? names[i] : NULL; 			if (!ethnl_bitmap32_test_bit(mask ?: val, i))				continue;			/* index */			bit_len = nla_total_size(sizeof(u32));			/* name */			if (name)				bit_len += ethnl_strz_size(name);			/* value */			if (mask && ethnl_bitmap32_test_bit(val, i))				bit_len += nla_total_size(0); 			/* bit nest */			bits_len += nla_total_size(bit_len);		}		/* bits nest */		len += nla_total_size(bits_len);	} 	/* outermost nest */	return nla_total_size(len);} /** * ethnl_put_bitset32() - Put a bitset nest into a message * @skb:      skb with the message * @attrtype: attribute type for the bitset nest * @val:      value bitmap (u32 based) * @mask:     mask bitmap (u32 based, optional) * @nbits:    bit length of the bitset * @names:    array of bit names (optional) * @compact:  use compact format for the output * * Compose a nested attribute representing a bitset. If @mask is null, simple * bitmap (bit list) is created, if @mask is provided, represent a value/mask * pair. Bit names are only used in verbose mode and when provided by calller. * * Return: 0 on success, negative error value on error */int ethnl_put_bitset32(struct sk_buff *skb, int attrtype, const u32 *val,		       const u32 *mask, unsigned int nbits,		       ethnl_string_array_t names, bool compact){	struct nlattr *nest;	struct nlattr *attr; 	nest = nla_nest_start(skb, attrtype);	if (!nest)		return -EMSGSIZE; 	if (!mask && nla_put_flag(skb, ETHTOOL_A_BITSET_NOMASK))		goto nla_put_failure;	if (nla_put_u32(skb, ETHTOOL_A_BITSET_SIZE, nbits))		goto nla_put_failure;	if (compact) {		unsigned int nwords = DIV_ROUND_UP(nbits, 32);		unsigned int nbytes = nwords * sizeof(u32);		u32 *dst; 		attr = nla_reserve(skb, ETHTOOL_A_BITSET_VALUE, nbytes);		if (!attr)			goto nla_put_failure;		dst = nla_data(attr);		memcpy(dst, val, nbytes);		if (nbits % 32)			dst[nwords - 1] &= ethnl_lower_bits(nbits); 		if (mask) {			attr = nla_reserve(skb, ETHTOOL_A_BITSET_MASK, nbytes);			if (!attr)				goto nla_put_failure;			dst = nla_data(attr);			memcpy(dst, mask, nbytes);			if (nbits % 32)				dst[nwords - 1] &= ethnl_lower_bits(nbits);		}	} else {		struct nlattr *bits;		unsigned int i; 		bits = nla_nest_start(skb, ETHTOOL_A_BITSET_BITS);		if (!bits)			goto nla_put_failure;		for (i = 0; i < nbits; i++) {			const char *name = names ? names[i] : NULL; 			if (!ethnl_bitmap32_test_bit(mask ?: val, i))				continue;			attr = nla_nest_start(skb, ETHTOOL_A_BITSET_BITS_BIT);			if (!attr)				goto nla_put_failure;			if (nla_put_u32(skb, ETHTOOL_A_BITSET_BIT_INDEX, i))				goto nla_put_failure;			if (name &&			    ethnl_put_strz(skb, ETHTOOL_A_BITSET_BIT_NAME, name))				goto nla_put_failure;			if (mask && ethnl_bitmap32_test_bit(val, i) &&			    nla_put_flag(skb, ETHTOOL_A_BITSET_BIT_VALUE))				goto nla_put_failure;			nla_nest_end(skb, attr);		}		nla_nest_end(skb, bits);	} 	nla_nest_end(skb, nest);	return 0; nla_put_failure:	nla_nest_cancel(skb, nest);	return -EMSGSIZE;} static const struct nla_policy bitset_policy[] = {	[ETHTOOL_A_BITSET_NOMASK]	= { .type = NLA_FLAG },	[ETHTOOL_A_BITSET_SIZE]		= NLA_POLICY_MAX(NLA_U32,							 ETHNL_MAX_BITSET_SIZE),	[ETHTOOL_A_BITSET_BITS]		= { .type = NLA_NESTED },	[ETHTOOL_A_BITSET_VALUE]	= { .type = NLA_BINARY },	[ETHTOOL_A_BITSET_MASK]		= { .type = NLA_BINARY },}; static const struct nla_policy bit_policy[] = {	[ETHTOOL_A_BITSET_BIT_INDEX]	= { .type = NLA_U32 },	[ETHTOOL_A_BITSET_BIT_NAME]	= { .type = NLA_NUL_STRING },	[ETHTOOL_A_BITSET_BIT_VALUE]	= { .type = NLA_FLAG },}; /** * ethnl_bitset_is_compact() - check if bitset attribute represents a compact *			       bitset * @bitset:  nested attribute representing a bitset * @compact: pointer for return value * * Return: 0 on success, negative error code on failure */int ethnl_bitset_is_compact(const struct nlattr *bitset, bool *compact){	struct nlattr *tb[ARRAY_SIZE(bitset_policy)];	int ret; 	ret = nla_parse_nested(tb, ARRAY_SIZE(bitset_policy) - 1, bitset,			       bitset_policy, NULL);	if (ret < 0)		return ret; 	if (tb[ETHTOOL_A_BITSET_BITS]) {		if (tb[ETHTOOL_A_BITSET_VALUE] || tb[ETHTOOL_A_BITSET_MASK])			return -EINVAL;		*compact = false;		return 0;	}	if (!tb[ETHTOOL_A_BITSET_SIZE] || !tb[ETHTOOL_A_BITSET_VALUE])		return -EINVAL; 	*compact = true;	return 0;} /** * ethnl_name_to_idx() - look up string index for a name * @names:   array of ETH_GSTRING_LEN sized strings * @n_names: number of strings in the array * @name:    name to look up * * Return: index of the string if found, -ENOENT if not found */static int ethnl_name_to_idx(ethnl_string_array_t names, unsigned int n_names,			     const char *name){	unsigned int i; 	if (!names)		return -ENOENT; 	for (i = 0; i < n_names; i++) {		/* names[i] may not be null terminated */		if (!strncmp(names[i], name, ETH_GSTRING_LEN) &&		    strlen(name) <= ETH_GSTRING_LEN)			return i;	} 	return -ENOENT;} static int ethnl_parse_bit(unsigned int *index, bool *val, unsigned int nbits,			   const struct nlattr *bit_attr, bool no_mask,			   ethnl_string_array_t names,			   struct netlink_ext_ack *extack){	struct nlattr *tb[ARRAY_SIZE(bit_policy)];	int ret, idx; 	ret = nla_parse_nested(tb, ARRAY_SIZE(bit_policy) - 1, bit_attr,			       bit_policy, extack);	if (ret < 0)		return ret; 	if (tb[ETHTOOL_A_BITSET_BIT_INDEX]) {		const char *name; 		idx = nla_get_u32(tb[ETHTOOL_A_BITSET_BIT_INDEX]);		if (idx >= nbits) {			NL_SET_ERR_MSG_ATTR(extack,					    tb[ETHTOOL_A_BITSET_BIT_INDEX],					    "bit index too high");			return -EOPNOTSUPP;		}		name = names ? names[idx] : NULL;		if (tb[ETHTOOL_A_BITSET_BIT_NAME] && name &&		    strncmp(nla_data(tb[ETHTOOL_A_BITSET_BIT_NAME]), name,			    nla_len(tb[ETHTOOL_A_BITSET_BIT_NAME]))) {			NL_SET_ERR_MSG_ATTR(extack, bit_attr,					    "bit index and name mismatch");			return -EINVAL;		}	} else if (tb[ETHTOOL_A_BITSET_BIT_NAME]) {		idx = ethnl_name_to_idx(names, nbits,					nla_data(tb[ETHTOOL_A_BITSET_BIT_NAME]));		if (idx < 0) {			NL_SET_ERR_MSG_ATTR(extack,					    tb[ETHTOOL_A_BITSET_BIT_NAME],					    "bit name not found");			return -EOPNOTSUPP;		}	} else {		NL_SET_ERR_MSG_ATTR(extack, bit_attr,				    "neither bit index nor name specified");		return -EINVAL;	} 	*index = idx;	*val = no_mask || tb[ETHTOOL_A_BITSET_BIT_VALUE];	return 0;} /** * ethnl_bitmap32_equal() - Compare two bitmaps * @map1:  first bitmap * @map2:  second bitmap * @nbits: bit size to compare * * Return: true if first @nbits are equal, false if not */static bool ethnl_bitmap32_equal(const u32 *map1, const u32 *map2,				 unsigned int nbits){	if (memcmp(map1, map2, nbits / 32 * sizeof(u32)))		return false;	if (nbits % 32 == 0)		return true;	return !((map1[nbits / 32] ^ map2[nbits / 32]) &		 ethnl_lower_bits(nbits % 32));} static intethnl_update_bitset32_verbose(u32 *bitmap, unsigned int nbits,			      const struct nlattr *attr, struct nlattr **tb,			      ethnl_string_array_t names,			      struct netlink_ext_ack *extack, bool *mod){	u32 *saved_bitmap = NULL;	struct nlattr *bit_attr;	bool no_mask;	int rem;	int ret; 	if (tb[ETHTOOL_A_BITSET_VALUE]) {		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_VALUE],				    "value only allowed in compact bitset");		return -EINVAL;	}	if (tb[ETHTOOL_A_BITSET_MASK]) {		NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK],				    "mask only allowed in compact bitset");		return -EINVAL;	} 	no_mask = tb[ETHTOOL_A_BITSET_NOMASK];	if (no_mask) {		unsigned int nwords = DIV_ROUND_UP(nbits, 32);		unsigned int nbytes = nwords * sizeof(u32);		bool dummy; 		/* The bitmap size is only the size of the map part without		 * its mask part.		 */		saved_bitmap = kcalloc(nwords, sizeof(u32), GFP_KERNEL);		if (!saved_bitmap)			return -ENOMEM;		memcpy(saved_bitmap, bitmap, nbytes);		ethnl_bitmap32_clear(bitmap, 0, nbits, &dummy);	} 	nla_for_each_nested(bit_attr, tb[ETHTOOL_A_BITSET_BITS], rem) {		bool old_val, new_val;		unsigned int idx; 		if (nla_type(bit_attr) != ETHTOOL_A_BITSET_BITS_BIT) {			NL_SET_ERR_MSG_ATTR(extack, bit_attr,					    "only ETHTOOL_A_BITSET_BITS_BIT allowed in ETHTOOL_A_BITSET_BITS");			kfree(saved_bitmap);			return -EINVAL;		}		ret = ethnl_parse_bit(&idx, &new_val, nbits, bit_attr, no_mask,				      names, extack);		if (ret < 0) {			kfree(saved_bitmap);

Lines 1–500 of 874.