Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ethtool: add ethnl_parse_bitset() helper

Unlike other SET type commands, modifying netdev features is required to
provide a reply telling userspace what was actually changed, compared to
what was requested. For that purpose, the "modified" flag provided by
ethnl_update_bitset() is not sufficient, we need full information which
bits were requested to change.

Therefore provide ethnl_parse_bitset() returning effective value and mask
bitmaps equivalent to the contents of a bitset nested attribute.

v2: use non-atomic __set_bit() (suggested by David Miller)

Signed-off-by: Michal Kubecek <mkubecek@suse.cz>
Signed-off-by: David S. Miller <davem@davemloft.net>

authored by

Michal Kubecek and committed by
David S. Miller
88db6d1e 0524399d

+98
+94
net/ethtool/bitset.c
··· 588 588 return 0; 589 589 } 590 590 591 + /** 592 + * ethnl_parse_bitset() - Compute effective value and mask from bitset nest 593 + * @val: unsigned long based bitmap to put value into 594 + * @mask: unsigned long based bitmap to put mask into 595 + * @nbits: size of @val and @mask bitmaps 596 + * @attr: nest attribute to parse and apply 597 + * @names: array of bit names; may be null for compact format 598 + * @extack: extack for error reporting 599 + * 600 + * Provide @nbits size long bitmaps for value and mask so that 601 + * x = (val & mask) | (x & ~mask) would modify any @nbits sized bitmap x 602 + * the same way ethnl_update_bitset() with the same bitset attribute would. 603 + * 604 + * Return: negative error code on failure, 0 on success 605 + */ 606 + int ethnl_parse_bitset(unsigned long *val, unsigned long *mask, 607 + unsigned int nbits, const struct nlattr *attr, 608 + ethnl_string_array_t names, 609 + struct netlink_ext_ack *extack) 610 + { 611 + struct nlattr *tb[ETHTOOL_A_BITSET_MAX + 1]; 612 + const struct nlattr *bit_attr; 613 + bool no_mask; 614 + int rem; 615 + int ret; 616 + 617 + if (!attr) 618 + return 0; 619 + ret = nla_parse_nested(tb, ETHTOOL_A_BITSET_MAX, attr, bitset_policy, 620 + extack); 621 + if (ret < 0) 622 + return ret; 623 + no_mask = tb[ETHTOOL_A_BITSET_NOMASK]; 624 + 625 + if (!tb[ETHTOOL_A_BITSET_BITS]) { 626 + unsigned int change_bits; 627 + 628 + ret = ethnl_compact_sanity_checks(nbits, attr, tb, extack); 629 + if (ret < 0) 630 + return ret; 631 + 632 + change_bits = nla_get_u32(tb[ETHTOOL_A_BITSET_SIZE]); 633 + bitmap_from_arr32(val, nla_data(tb[ETHTOOL_A_BITSET_VALUE]), 634 + change_bits); 635 + if (change_bits < nbits) 636 + bitmap_clear(val, change_bits, nbits - change_bits); 637 + if (no_mask) { 638 + bitmap_fill(mask, nbits); 639 + } else { 640 + bitmap_from_arr32(mask, 641 + nla_data(tb[ETHTOOL_A_BITSET_MASK]), 642 + change_bits); 643 + if (change_bits < nbits) 644 + bitmap_clear(mask, change_bits, 645 + nbits - change_bits); 646 + } 647 + 648 + return 0; 649 + } 650 + 651 + if (tb[ETHTOOL_A_BITSET_VALUE]) { 652 + NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_VALUE], 653 + "value only allowed in compact bitset"); 654 + return -EINVAL; 655 + } 656 + if (tb[ETHTOOL_A_BITSET_MASK]) { 657 + NL_SET_ERR_MSG_ATTR(extack, tb[ETHTOOL_A_BITSET_MASK], 658 + "mask only allowed in compact bitset"); 659 + return -EINVAL; 660 + } 661 + 662 + bitmap_zero(val, nbits); 663 + if (no_mask) 664 + bitmap_fill(mask, nbits); 665 + else 666 + bitmap_zero(mask, nbits); 667 + 668 + nla_for_each_nested(bit_attr, tb[ETHTOOL_A_BITSET_BITS], rem) { 669 + unsigned int idx; 670 + bool bit_val; 671 + 672 + ret = ethnl_parse_bit(&idx, &bit_val, nbits, bit_attr, no_mask, 673 + names, extack); 674 + if (ret < 0) 675 + return ret; 676 + if (bit_val) 677 + __set_bit(idx, val); 678 + if (!no_mask) 679 + __set_bit(idx, mask); 680 + } 681 + 682 + return 0; 683 + } 684 + 591 685 #if BITS_PER_LONG == 64 && defined(__BIG_ENDIAN) 592 686 593 687 /* 64-bit big endian architectures are the only case when u32 based bitmaps
+4
net/ethtool/bitset.h
··· 26 26 int ethnl_update_bitset32(u32 *bitmap, unsigned int nbits, 27 27 const struct nlattr *attr, ethnl_string_array_t names, 28 28 struct netlink_ext_ack *extack, bool *mod); 29 + int ethnl_parse_bitset(unsigned long *val, unsigned long *mask, 30 + unsigned int nbits, const struct nlattr *attr, 31 + ethnl_string_array_t names, 32 + struct netlink_ext_ack *extack); 29 33 30 34 #endif /* _NET_ETHTOOL_BITSET_H */