Commit 9faebeb2 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'ethtool-allow-dumping-policies-to-user-space'



Jakub Kicinski says:

====================
ethtool: allow dumping policies to user space

This series wires up ethtool policies to ops, so they can be
dumped to user space for feature discovery.

First patch wires up GET commands, and second patch wires up SETs.

The policy tables are trimmed to save space and LoC.

Next - take care of linking up nested policies for the header
(which is the policy what we actually care about). And once header
policy is linked make sure that attribute range validation for flags
is done by policy, not a conditions in the code. New type of policy
is needed to validate masks (patch 6).

Netlink as always staying a step ahead of all the other kernel
API interfaces :)

v2:
 - merge patches 1 & 2 -> 1
 - add patch 3 & 5
 - remove .max_attr from struct ethnl_request_ops
====================

Reviewed-by: default avatarJohannes Berg <johannes@sipsolutions.net>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 02da0b61 a0de1cd3
Loading
Loading
Loading
Loading
+19 −8
Original line number Diff line number Diff line
@@ -200,6 +200,7 @@ enum nla_policy_validation {
	NLA_VALIDATE_RANGE_WARN_TOO_LONG,
	NLA_VALIDATE_MIN,
	NLA_VALIDATE_MAX,
	NLA_VALIDATE_MASK,
	NLA_VALIDATE_RANGE_PTR,
	NLA_VALIDATE_FUNCTION,
};
@@ -317,6 +318,7 @@ struct nla_policy {
	u16		len;
	union {
		const u32 bitfield32_valid;
		const u32 mask;
		const char *reject_message;
		const struct nla_policy *nested_policy;
		struct netlink_range_validation *range;
@@ -362,20 +364,23 @@ struct nla_policy {
#define NLA_POLICY_BITFIELD32(valid) \
	{ .type = NLA_BITFIELD32, .bitfield32_valid = valid }

#define __NLA_IS_UINT_TYPE(tp)						\
	(tp == NLA_U8 || tp == NLA_U16 || tp == NLA_U32 || tp == NLA_U64)
#define __NLA_IS_SINT_TYPE(tp)						\
	(tp == NLA_S8 || tp == NLA_S16 || tp == NLA_S32 || tp == NLA_S64)

#define __NLA_ENSURE(condition) BUILD_BUG_ON_ZERO(!(condition))
#define NLA_ENSURE_UINT_TYPE(tp)			\
	(__NLA_ENSURE(__NLA_IS_UINT_TYPE(tp)) + tp)
#define NLA_ENSURE_UINT_OR_BINARY_TYPE(tp)		\
	(__NLA_ENSURE(tp == NLA_U8 || tp == NLA_U16 ||	\
		      tp == NLA_U32 || tp == NLA_U64 ||	\
	(__NLA_ENSURE(__NLA_IS_UINT_TYPE(tp) ||	\
		      tp == NLA_MSECS ||		\
		      tp == NLA_BINARY) + tp)
#define NLA_ENSURE_SINT_TYPE(tp)			\
	(__NLA_ENSURE(tp == NLA_S8 || tp == NLA_S16  ||	\
		      tp == NLA_S32 || tp == NLA_S64) + tp)
	(__NLA_ENSURE(__NLA_IS_SINT_TYPE(tp)) + tp)
#define NLA_ENSURE_INT_OR_BINARY_TYPE(tp)		\
	(__NLA_ENSURE(tp == NLA_S8 || tp == NLA_U8 ||	\
		      tp == NLA_S16 || tp == NLA_U16 ||	\
		      tp == NLA_S32 || tp == NLA_U32 ||	\
		      tp == NLA_S64 || tp == NLA_U64 ||	\
	(__NLA_ENSURE(__NLA_IS_UINT_TYPE(tp) ||		\
		      __NLA_IS_SINT_TYPE(tp) ||		\
		      tp == NLA_MSECS ||		\
		      tp == NLA_BINARY) + tp)
#define NLA_ENSURE_NO_VALIDATION_PTR(tp)		\
@@ -415,6 +420,12 @@ struct nla_policy {
	.max = _max,					\
}

#define NLA_POLICY_MASK(tp, _mask) {			\
	.type = NLA_ENSURE_UINT_TYPE(tp),		\
	.validation_type = NLA_VALIDATE_MASK,		\
	.mask = _mask,					\
}

#define NLA_POLICY_VALIDATE_FN(tp, fn, ...) {		\
	.type = NLA_ENSURE_NO_VALIDATION_PTR(tp),	\
	.validation_type = NLA_VALIDATE_FUNCTION,	\
+2 −0
Original line number Diff line number Diff line
@@ -331,6 +331,7 @@ enum netlink_attribute_type {
 *	the index, if limited inside the nesting (U32)
 * @NL_POLICY_TYPE_ATTR_BITFIELD32_MASK: valid mask for the
 *	bitfield32 type (U32)
 * @NL_POLICY_TYPE_ATTR_MASK: mask of valid bits for unsigned integers (U64)
 * @NL_POLICY_TYPE_ATTR_PAD: pad attribute for 64-bit alignment
 */
enum netlink_policy_type_attr {
@@ -346,6 +347,7 @@ enum netlink_policy_type_attr {
	NL_POLICY_TYPE_ATTR_POLICY_MAXTYPE,
	NL_POLICY_TYPE_ATTR_BITFIELD32_MASK,
	NL_POLICY_TYPE_ATTR_PAD,
	NL_POLICY_TYPE_ATTR_MASK,

	/* keep last */
	__NL_POLICY_TYPE_ATTR_MAX,
+36 −0
Original line number Diff line number Diff line
@@ -323,6 +323,37 @@ static int nla_validate_int_range(const struct nla_policy *pt,
	}
}

static int nla_validate_mask(const struct nla_policy *pt,
			     const struct nlattr *nla,
			     struct netlink_ext_ack *extack)
{
	u64 value;

	switch (pt->type) {
	case NLA_U8:
		value = nla_get_u8(nla);
		break;
	case NLA_U16:
		value = nla_get_u16(nla);
		break;
	case NLA_U32:
		value = nla_get_u32(nla);
		break;
	case NLA_U64:
		value = nla_get_u64(nla);
		break;
	default:
		return -EINVAL;
	}

	if (value & ~(u64)pt->mask) {
		NL_SET_ERR_MSG_ATTR(extack, nla, "reserved bit set");
		return -EINVAL;
	}

	return 0;
}

static int validate_nla(const struct nlattr *nla, int maxtype,
			const struct nla_policy *policy, unsigned int validate,
			struct netlink_ext_ack *extack, unsigned int depth)
@@ -503,6 +534,11 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
		if (err)
			return err;
		break;
	case NLA_VALIDATE_MASK:
		err = nla_validate_mask(pt, nla, extack);
		if (err)
			return err;
		break;
	case NLA_VALIDATE_FUNCTION:
		if (pt->validate) {
			err = pt->validate(nla, extack);
+12 −14
Original line number Diff line number Diff line
@@ -302,8 +302,7 @@ nla_put_failure:
	return -EMSGSIZE;
}

static const struct nla_policy bitset_policy[ETHTOOL_A_BITSET_MAX + 1] = {
	[ETHTOOL_A_BITSET_UNSPEC]	= { .type = NLA_REJECT },
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),
@@ -312,8 +311,7 @@ static const struct nla_policy bitset_policy[ETHTOOL_A_BITSET_MAX + 1] = {
	[ETHTOOL_A_BITSET_MASK]		= { .type = NLA_BINARY },
};

static const struct nla_policy bit_policy[ETHTOOL_A_BITSET_BIT_MAX + 1] = {
	[ETHTOOL_A_BITSET_BIT_UNSPEC]	= { .type = NLA_REJECT },
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 },
@@ -329,10 +327,10 @@ static const struct nla_policy bit_policy[ETHTOOL_A_BITSET_BIT_MAX + 1] = {
 */
int ethnl_bitset_is_compact(const struct nlattr *bitset, bool *compact)
{
	struct nlattr *tb[ETHTOOL_A_BITSET_MAX + 1];
	struct nlattr *tb[ARRAY_SIZE(bitset_policy)];
	int ret;

	ret = nla_parse_nested(tb, ETHTOOL_A_BITSET_MAX, bitset,
	ret = nla_parse_nested(tb, ARRAY_SIZE(bitset_policy) - 1, bitset,
			       bitset_policy, NULL);
	if (ret < 0)
		return ret;
@@ -381,10 +379,10 @@ static int ethnl_parse_bit(unsigned int *index, bool *val, unsigned int nbits,
			   ethnl_string_array_t names,
			   struct netlink_ext_ack *extack)
{
	struct nlattr *tb[ETHTOOL_A_BITSET_BIT_MAX + 1];
	struct nlattr *tb[ARRAY_SIZE(bit_policy)];
	int ret, idx;

	ret = nla_parse_nested(tb, ETHTOOL_A_BITSET_BIT_MAX, bit_attr,
	ret = nla_parse_nested(tb, ARRAY_SIZE(bit_policy) - 1, bit_attr,
			       bit_policy, extack);
	if (ret < 0)
		return ret;
@@ -555,15 +553,15 @@ int ethnl_update_bitset32(u32 *bitmap, unsigned int nbits,
			  const struct nlattr *attr, ethnl_string_array_t names,
			  struct netlink_ext_ack *extack, bool *mod)
{
	struct nlattr *tb[ETHTOOL_A_BITSET_MAX + 1];
	struct nlattr *tb[ARRAY_SIZE(bitset_policy)];
	unsigned int change_bits;
	bool no_mask;
	int ret;

	if (!attr)
		return 0;
	ret = nla_parse_nested(tb, ETHTOOL_A_BITSET_MAX, attr, bitset_policy,
			       extack);
	ret = nla_parse_nested(tb, ARRAY_SIZE(bitset_policy) - 1, attr,
			       bitset_policy, extack);
	if (ret < 0)
		return ret;

@@ -608,7 +606,7 @@ int ethnl_parse_bitset(unsigned long *val, unsigned long *mask,
		       ethnl_string_array_t names,
		       struct netlink_ext_ack *extack)
{
	struct nlattr *tb[ETHTOOL_A_BITSET_MAX + 1];
	struct nlattr *tb[ARRAY_SIZE(bitset_policy)];
	const struct nlattr *bit_attr;
	bool no_mask;
	int rem;
@@ -616,8 +614,8 @@ int ethnl_parse_bitset(unsigned long *val, unsigned long *mask,

	if (!attr)
		return 0;
	ret = nla_parse_nested(tb, ETHTOOL_A_BITSET_MAX, attr, bitset_policy,
			       extack);
	ret = nla_parse_nested(tb, ARRAY_SIZE(bitset_policy) - 1, attr,
			       bitset_policy, extack);
	if (ret < 0)
		return ret;
	no_mask = tb[ETHTOOL_A_BITSET_NOMASK];
+14 −27
Original line number Diff line number Diff line
@@ -11,10 +11,9 @@
 */
#define MAX_CABLE_LENGTH_CM (150 * 100)

static const struct nla_policy
cable_test_act_policy[ETHTOOL_A_CABLE_TEST_MAX + 1] = {
	[ETHTOOL_A_CABLE_TEST_UNSPEC]		= { .type = NLA_REJECT },
	[ETHTOOL_A_CABLE_TEST_HEADER]		= { .type = NLA_NESTED },
const struct nla_policy ethnl_cable_test_act_policy[] = {
	[ETHTOOL_A_CABLE_TEST_HEADER]		=
		NLA_POLICY_NESTED(ethnl_header_policy),
};

static int ethnl_cable_test_started(struct phy_device *phydev, u8 cmd)
@@ -56,18 +55,12 @@ out:

int ethnl_act_cable_test(struct sk_buff *skb, struct genl_info *info)
{
	struct nlattr *tb[ETHTOOL_A_CABLE_TEST_MAX + 1];
	struct ethnl_req_info req_info = {};
	const struct ethtool_phy_ops *ops;
	struct nlattr **tb = info->attrs;
	struct net_device *dev;
	int ret;

	ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb,
			  ETHTOOL_A_CABLE_TEST_MAX,
			  cable_test_act_policy, info->extack);
	if (ret < 0)
		return ret;

	ret = ethnl_parse_header_dev_get(&req_info,
					 tb[ETHTOOL_A_CABLE_TEST_HEADER],
					 genl_info_net(info), info->extack,
@@ -218,18 +211,16 @@ struct cable_test_tdr_req_info {
	struct ethnl_req_info		base;
};

static const struct nla_policy
cable_test_tdr_act_cfg_policy[ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX + 1] = {
static const struct nla_policy cable_test_tdr_act_cfg_policy[] = {
	[ETHTOOL_A_CABLE_TEST_TDR_CFG_FIRST]	= { .type = NLA_U32 },
	[ETHTOOL_A_CABLE_TEST_TDR_CFG_LAST]	= { .type = NLA_U32 },
	[ETHTOOL_A_CABLE_TEST_TDR_CFG_STEP]	= { .type = NLA_U32 },
	[ETHTOOL_A_CABLE_TEST_TDR_CFG_PAIR]	= { .type = NLA_U8 },
};

static const struct nla_policy
cable_test_tdr_act_policy[ETHTOOL_A_CABLE_TEST_TDR_MAX + 1] = {
	[ETHTOOL_A_CABLE_TEST_TDR_UNSPEC]	= { .type = NLA_REJECT },
	[ETHTOOL_A_CABLE_TEST_TDR_HEADER]	= { .type = NLA_NESTED },
const struct nla_policy ethnl_cable_test_tdr_act_policy[] = {
	[ETHTOOL_A_CABLE_TEST_TDR_HEADER]	=
		NLA_POLICY_NESTED(ethnl_header_policy),
	[ETHTOOL_A_CABLE_TEST_TDR_CFG]		= { .type = NLA_NESTED },
};

@@ -238,7 +229,7 @@ static int ethnl_act_cable_test_tdr_cfg(const struct nlattr *nest,
					struct genl_info *info,
					struct phy_tdr_config *cfg)
{
	struct nlattr *tb[ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX + 1];
	struct nlattr *tb[ARRAY_SIZE(cable_test_tdr_act_cfg_policy)];
	int ret;

	cfg->first = 100;
@@ -249,8 +240,10 @@ static int ethnl_act_cable_test_tdr_cfg(const struct nlattr *nest,
	if (!nest)
		return 0;

	ret = nla_parse_nested(tb, ETHTOOL_A_CABLE_TEST_TDR_CFG_MAX, nest,
			       cable_test_tdr_act_cfg_policy, info->extack);
	ret = nla_parse_nested(tb,
			       ARRAY_SIZE(cable_test_tdr_act_cfg_policy) - 1,
			       nest, cable_test_tdr_act_cfg_policy,
			       info->extack);
	if (ret < 0)
		return ret;

@@ -313,19 +306,13 @@ static int ethnl_act_cable_test_tdr_cfg(const struct nlattr *nest,

int ethnl_act_cable_test_tdr(struct sk_buff *skb, struct genl_info *info)
{
	struct nlattr *tb[ETHTOOL_A_CABLE_TEST_TDR_MAX + 1];
	struct ethnl_req_info req_info = {};
	const struct ethtool_phy_ops *ops;
	struct nlattr **tb = info->attrs;
	struct phy_tdr_config cfg;
	struct net_device *dev;
	int ret;

	ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb,
			  ETHTOOL_A_CABLE_TEST_TDR_MAX,
			  cable_test_tdr_act_policy, info->extack);
	if (ret < 0)
		return ret;

	ret = ethnl_parse_header_dev_get(&req_info,
					 tb[ETHTOOL_A_CABLE_TEST_TDR_HEADER],
					 genl_info_net(info), info->extack,
Loading