Commit fd533a7a authored by David S. Miller's avatar David S. Miller

Merge branch 'netlink-auto-integers'

Jakub Kicinski says:

====================
netlink: add variable-length / auto integers

Add netlink support for "common" / variable-length / auto integers
which are carried at the message level as either 4B or 8B depending
on the exact value. This saves space and will hopefully decrease
the number of instances where we realize that we needed more bits
after uAPI is set is stone. It also loosens the alignment requirements,
avoiding the need for padding.

This mini-series is a fuller version of the previous RFC:
https://lore.kernel.org/netdev/20121204.130914.1457976839967676240.davem@davemloft.net/
No user included here. I have tested (and will use) it
in the upcoming page pool API but the assumption is that
it will be widely applicable. So sending without a user.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents a10f9bfe 7d4caf54
......@@ -149,7 +149,8 @@ properties:
name:
type: string
type: &attr-type
enum: [ unused, pad, flag, binary, u8, u16, u32, u64, s32, s64,
enum: [ unused, pad, flag, binary,
uint, sint, u8, u16, u32, u64, s32, s64,
string, nest, array-nest, nest-type-value ]
doc:
description: Documentation of the attribute.
......
......@@ -192,7 +192,8 @@ properties:
type: string
type: &attr-type
description: The netlink attribute type
enum: [ unused, pad, flag, binary, u8, u16, u32, u64, s32, s64,
enum: [ unused, pad, flag, binary,
uint, sint, u8, u16, u32, u64, s32, s64,
string, nest, array-nest, nest-type-value ]
doc:
description: Documentation of the attribute.
......
......@@ -122,7 +122,8 @@ properties:
name:
type: string
type: &attr-type
enum: [ unused, pad, flag, binary, u8, u16, u32, u64, s32, s64,
enum: [ unused, pad, flag, binary,
uint, sint, u8, u16, u32, u64, s32, s64,
string, nest, array-nest, nest-type-value ]
doc:
description: Documentation of the attribute.
......
......@@ -403,10 +403,21 @@ This section describes the attribute types supported by the ``genetlink``
compatibility level. Refer to documentation of different levels for additional
attribute types.
Scalar integer types
Common integer types
--------------------
Fixed-width integer types:
``sint`` and ``uint`` represent signed and unsigned 64 bit integers.
If the value can fit on 32 bits only 32 bits are carried in netlink
messages, otherwise full 64 bits are carried. Note that the payload
is only aligned to 4B, so the full 64 bit value may be unaligned!
Common integer types should be preferred over fix-width types in majority
of cases.
Fix-width integer types
-----------------------
Fixed-width integer types include:
``u8``, ``u16``, ``u32``, ``u64``, ``s8``, ``s16``, ``s32``, ``s64``.
Note that types smaller than 32 bit should be avoided as using them
......@@ -416,6 +427,9 @@ See :ref:`pad_type` for padding of 64 bit attributes.
The payload of the attribute is the integer in host order unless ``byte-order``
specifies otherwise.
64 bit values are usually aligned by the kernel but it is recommended
that the user space is able to deal with unaligned values.
.. _pad_type:
pad
......
......@@ -128,6 +128,8 @@
* nla_len(nla) length of attribute payload
*
* Attribute Payload Access for Basic Types:
* nla_get_uint(nla) get payload for a uint attribute
* nla_get_sint(nla) get payload for a sint attribute
* nla_get_u8(nla) get payload for a u8 attribute
* nla_get_u16(nla) get payload for a u16 attribute
* nla_get_u32(nla) get payload for a u32 attribute
......@@ -183,6 +185,8 @@ enum {
NLA_REJECT,
NLA_BE16,
NLA_BE32,
NLA_SINT,
NLA_UINT,
__NLA_TYPE_MAX,
};
......@@ -229,6 +233,7 @@ enum nla_policy_validation {
* nested header (or empty); len field is used if
* nested_policy is also used, for the max attr
* number in the nested policy.
* NLA_SINT, NLA_UINT,
* NLA_U8, NLA_U16,
* NLA_U32, NLA_U64,
* NLA_S8, NLA_S16,
......@@ -260,12 +265,14 @@ enum nla_policy_validation {
* while an array has the nested attributes at another
* level down and the attribute types directly in the
* nesting don't matter.
* NLA_UINT,
* NLA_U8,
* NLA_U16,
* NLA_U32,
* NLA_U64,
* NLA_BE16,
* NLA_BE32,
* NLA_SINT,
* NLA_S8,
* NLA_S16,
* NLA_S32,
......@@ -280,6 +287,7 @@ enum nla_policy_validation {
* or NLA_POLICY_FULL_RANGE_SIGNED() macros instead.
* Use the NLA_POLICY_MIN(), NLA_POLICY_MAX() and
* NLA_POLICY_RANGE() macros.
* NLA_UINT,
* NLA_U8,
* NLA_U16,
* NLA_U32,
......@@ -288,6 +296,7 @@ enum nla_policy_validation {
* to a struct netlink_range_validation that indicates
* the min/max values.
* Use NLA_POLICY_FULL_RANGE().
* NLA_SINT,
* NLA_S8,
* NLA_S16,
* NLA_S32,
......@@ -377,9 +386,11 @@ struct nla_policy {
#define __NLA_IS_UINT_TYPE(tp) \
(tp == NLA_U8 || tp == NLA_U16 || tp == NLA_U32 || \
tp == NLA_U64 || tp == NLA_BE16 || tp == NLA_BE32)
tp == NLA_U64 || tp == NLA_UINT || \
tp == NLA_BE16 || tp == NLA_BE32)
#define __NLA_IS_SINT_TYPE(tp) \
(tp == NLA_S8 || tp == NLA_S16 || tp == NLA_S32 || tp == NLA_S64)
(tp == NLA_S8 || tp == NLA_S16 || tp == NLA_S32 || tp == NLA_S64 || \
tp == NLA_SINT)
#define __NLA_ENSURE(condition) BUILD_BUG_ON_ZERO(!(condition))
#define NLA_ENSURE_UINT_TYPE(tp) \
......@@ -1357,6 +1368,22 @@ static inline int nla_put_u32(struct sk_buff *skb, int attrtype, u32 value)
return nla_put(skb, attrtype, sizeof(u32), &tmp);
}
/**
* nla_put_uint - Add a variable-size unsigned int to a socket buffer
* @skb: socket buffer to add attribute to
* @attrtype: attribute type
* @value: numeric value
*/
static inline int nla_put_uint(struct sk_buff *skb, int attrtype, u64 value)
{
u64 tmp64 = value;
u32 tmp32 = value;
if (tmp64 == tmp32)
return nla_put_u32(skb, attrtype, tmp32);
return nla_put(skb, attrtype, sizeof(u64), &tmp64);
}
/**
* nla_put_be32 - Add a __be32 netlink attribute to a socket buffer
* @skb: socket buffer to add attribute to
......@@ -1511,6 +1538,22 @@ static inline int nla_put_s64(struct sk_buff *skb, int attrtype, s64 value,
return nla_put_64bit(skb, attrtype, sizeof(s64), &tmp, padattr);
}
/**
* nla_put_sint - Add a variable-size signed int to a socket buffer
* @skb: socket buffer to add attribute to
* @attrtype: attribute type
* @value: numeric value
*/
static inline int nla_put_sint(struct sk_buff *skb, int attrtype, s64 value)
{
s64 tmp64 = value;
s32 tmp32 = value;
if (tmp64 == tmp32)
return nla_put_s32(skb, attrtype, tmp32);
return nla_put(skb, attrtype, sizeof(s64), &tmp64);
}
/**
* nla_put_string - Add a string netlink attribute to a socket buffer
* @skb: socket buffer to add attribute to
......@@ -1667,6 +1710,17 @@ static inline u64 nla_get_u64(const struct nlattr *nla)
return tmp;
}
/**
* nla_get_uint - return payload of uint attribute
* @nla: uint netlink attribute
*/
static inline u64 nla_get_uint(const struct nlattr *nla)
{
if (nla_len(nla) == sizeof(u32))
return nla_get_u32(nla);
return nla_get_u64(nla);
}
/**
* nla_get_be64 - return payload of __be64 attribute
* @nla: __be64 netlink attribute
......@@ -1729,6 +1783,17 @@ static inline s64 nla_get_s64(const struct nlattr *nla)
return tmp;
}
/**
* nla_get_sint - return payload of uint attribute
* @nla: uint netlink attribute
*/
static inline s64 nla_get_sint(const struct nlattr *nla)
{
if (nla_len(nla) == sizeof(s32))
return nla_get_s32(nla);
return nla_get_s64(nla);
}
/**
* nla_get_flag - return payload of flag attribute
* @nla: flag netlink attribute
......
......@@ -298,6 +298,8 @@ struct nla_bitfield32 {
* entry has attributes again, the policy for those inner ones
* and the corresponding maxtype may be specified.
* @NL_ATTR_TYPE_BITFIELD32: &struct nla_bitfield32 attribute
* @NL_ATTR_TYPE_SINT: 32-bit or 64-bit signed attribute, aligned to 4B
* @NL_ATTR_TYPE_UINT: 32-bit or 64-bit unsigned attribute, aligned to 4B
*/
enum netlink_attribute_type {
NL_ATTR_TYPE_INVALID,
......@@ -322,6 +324,9 @@ enum netlink_attribute_type {
NL_ATTR_TYPE_NESTED_ARRAY,
NL_ATTR_TYPE_BITFIELD32,
NL_ATTR_TYPE_SINT,
NL_ATTR_TYPE_UINT,
};
/**
......
......@@ -134,6 +134,7 @@ void nla_get_range_unsigned(const struct nla_policy *pt,
range->max = U32_MAX;
break;
case NLA_U64:
case NLA_UINT:
case NLA_MSECS:
range->max = U64_MAX;
break;
......@@ -183,6 +184,9 @@ static int nla_validate_range_unsigned(const struct nla_policy *pt,
case NLA_U64:
value = nla_get_u64(nla);
break;
case NLA_UINT:
value = nla_get_uint(nla);
break;
case NLA_MSECS:
value = nla_get_u64(nla);
break;
......@@ -248,6 +252,7 @@ void nla_get_range_signed(const struct nla_policy *pt,
range->max = S32_MAX;
break;
case NLA_S64:
case NLA_SINT:
range->min = S64_MIN;
range->max = S64_MAX;
break;
......@@ -295,6 +300,9 @@ static int nla_validate_int_range_signed(const struct nla_policy *pt,
case NLA_S64:
value = nla_get_s64(nla);
break;
case NLA_SINT:
value = nla_get_sint(nla);
break;
default:
return -EINVAL;
}
......@@ -320,6 +328,7 @@ static int nla_validate_int_range(const struct nla_policy *pt,
case NLA_U16:
case NLA_U32:
case NLA_U64:
case NLA_UINT:
case NLA_MSECS:
case NLA_BINARY:
case NLA_BE16:
......@@ -329,6 +338,7 @@ static int nla_validate_int_range(const struct nla_policy *pt,
case NLA_S16:
case NLA_S32:
case NLA_S64:
case NLA_SINT:
return nla_validate_int_range_signed(pt, nla, extack);
default:
WARN_ON(1);
......@@ -355,6 +365,9 @@ static int nla_validate_mask(const struct nla_policy *pt,
case NLA_U64:
value = nla_get_u64(nla);
break;
case NLA_UINT:
value = nla_get_uint(nla);
break;
case NLA_BE16:
value = ntohs(nla_get_be16(nla));
break;
......@@ -433,6 +446,15 @@ static int validate_nla(const struct nlattr *nla, int maxtype,
goto out_err;
break;
case NLA_SINT:
case NLA_UINT:
if (attrlen != sizeof(u32) && attrlen != sizeof(u64)) {
NL_SET_ERR_MSG_ATTR_POL(extack, nla, pt,
"invalid attribute length");
return -EINVAL;
}
break;
case NLA_BITFIELD32:
if (attrlen != sizeof(struct nla_bitfield32))
goto out_err;
......
......@@ -230,6 +230,8 @@ int netlink_policy_dump_attr_size_estimate(const struct nla_policy *pt)
case NLA_S16:
case NLA_S32:
case NLA_S64:
case NLA_SINT:
case NLA_UINT:
/* maximum is common, u64 min/max with padding */
return common +
2 * (nla_attr_size(0) + nla_attr_size(sizeof(u64)));
......@@ -288,6 +290,7 @@ __netlink_policy_dump_write_attr(struct netlink_policy_dump_state *state,
case NLA_U16:
case NLA_U32:
case NLA_U64:
case NLA_UINT:
case NLA_MSECS: {
struct netlink_range_validation range;
......@@ -297,8 +300,10 @@ __netlink_policy_dump_write_attr(struct netlink_policy_dump_state *state,
type = NL_ATTR_TYPE_U16;
else if (pt->type == NLA_U32)
type = NL_ATTR_TYPE_U32;
else
else if (pt->type == NLA_U64)
type = NL_ATTR_TYPE_U64;
else
type = NL_ATTR_TYPE_UINT;
if (pt->validation_type == NLA_VALIDATE_MASK) {
if (nla_put_u64_64bit(skb, NL_POLICY_TYPE_ATTR_MASK,
......@@ -320,7 +325,8 @@ __netlink_policy_dump_write_attr(struct netlink_policy_dump_state *state,
case NLA_S8:
case NLA_S16:
case NLA_S32:
case NLA_S64: {
case NLA_S64:
case NLA_SINT: {
struct netlink_range_validation_signed range;
if (pt->type == NLA_S8)
......@@ -329,8 +335,10 @@ __netlink_policy_dump_write_attr(struct netlink_policy_dump_state *state,
type = NL_ATTR_TYPE_S16;
else if (pt->type == NLA_S32)
type = NL_ATTR_TYPE_S32;
else
else if (pt->type == NLA_S64)
type = NL_ATTR_TYPE_S64;
else
type = NL_ATTR_TYPE_SINT;
nla_get_range_signed(pt, &range);
......
......@@ -149,6 +149,7 @@ class SpecAttr(SpecElement):
Represents a single attribute type within an attr space.
Attributes:
type string, attribute type
value numerical ID when serialized
attr_set Attribute Set containing this attr
is_multi bool, attr may repeat multiple times
......@@ -157,10 +158,13 @@ class SpecAttr(SpecElement):
len integer, optional byte length of binary types
display_hint string, hint to help choose format specifier
when displaying the value
is_auto_scalar bool, attr is a variable-size scalar
"""
def __init__(self, family, attr_set, yaml, value):
super().__init__(family, yaml)
self.type = yaml['type']
self.value = value
self.attr_set = attr_set
self.is_multi = yaml.get('multi-attr', False)
......@@ -170,6 +174,8 @@ class SpecAttr(SpecElement):
self.len = yaml.get('len')
self.display_hint = yaml.get('display-hint')
self.is_auto_scalar = self.type == "sint" or self.type == "uint"
class SpecAttrSet(SpecElement):
""" Netlink Attribute Set class.
......
......@@ -352,6 +352,12 @@ int ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr)
yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
"Invalid attribute (u64 %s)", policy->name);
return -1;
case YNL_PT_UINT:
if (len == sizeof(__u32) || len == sizeof(__u64))
break;
yerr(yarg->ys, YNL_ERROR_ATTR_INVALID,
"Invalid attribute (uint %s)", policy->name);
return -1;
case YNL_PT_FLAG:
/* Let flags grow into real attrs, why not.. */
break;
......
......@@ -133,6 +133,7 @@ enum ynl_policy_type {
YNL_PT_U16,
YNL_PT_U32,
YNL_PT_U64,
YNL_PT_UINT,
YNL_PT_NUL_STR,
};
......@@ -234,4 +235,20 @@ int ynl_exec_dump(struct ynl_sock *ys, struct nlmsghdr *req_nlh,
void ynl_error_unknown_notification(struct ynl_sock *ys, __u8 cmd);
int ynl_error_parse(struct ynl_parse_arg *yarg, const char *msg);
#ifndef MNL_HAS_AUTO_SCALARS
static inline uint64_t mnl_attr_get_uint(const struct nlattr *attr)
{
if (mnl_attr_get_len(attr) == 4)
return mnl_attr_get_u32(attr);
return mnl_attr_get_u64(attr);
}
static inline void
mnl_attr_put_uint(struct nlmsghdr *nlh, uint16_t type, uint64_t data)
{
if ((uint32_t)data == (uint64_t)data)
return mnl_attr_put_u32(nlh, type, data);
return mnl_attr_put_u64(nlh, type, data);
}
#endif
#endif
......@@ -130,6 +130,13 @@ class NlAttr:
format = self.get_format(attr_type, byte_order)
return format.unpack(self.raw)[0]
def as_auto_scalar(self, attr_type, byte_order=None):
if len(self.raw) != 4 and len(self.raw) != 8:
raise Exception(f"Auto-scalar len payload be 4 or 8 bytes, got {len(self.raw)}")
real_type = attr_type[0] + str(len(self.raw) * 8)
format = self.get_format(real_type, byte_order)
return format.unpack(self.raw)[0]
def as_strz(self):
return self.raw.decode('ascii')[:-1]
......@@ -463,6 +470,11 @@ class YnlFamily(SpecFamily):
attr_payload = bytes.fromhex(value)
else:
raise Exception(f'Unknown type for binary attribute, value: {value}')
elif attr.is_auto_scalar:
scalar = int(value)
real_type = attr["type"][0] + ('32' if scalar.bit_length() <= 32 else '64')
format = NlAttr.get_format(real_type, attr.byte_order)
attr_payload = format.pack(int(value))
elif attr['type'] in NlAttr.type_formats:
format = NlAttr.get_format(attr['type'], attr.byte_order)
attr_payload = format.pack(int(value))
......@@ -529,6 +541,8 @@ class YnlFamily(SpecFamily):
decoded = self._decode_binary(attr, attr_spec)
elif attr_spec["type"] == 'flag':
decoded = True
elif attr_spec.is_auto_scalar:
decoded = attr.as_auto_scalar(attr_spec['type'], attr_spec.byte_order)
elif attr_spec["type"] in NlAttr.type_formats:
decoded = attr.as_scalar(attr_spec['type'], attr_spec.byte_order)
elif attr_spec["type"] == 'array-nest':
......
......@@ -159,6 +159,15 @@ class Type(SpecAttr):
spec = self._attr_policy(policy)
cw.p(f"\t[{self.enum_name}] = {spec},")
def _mnl_type(self):
# mnl does not have helpers for signed integer types
# turn signed type into unsigned
# this only makes sense for scalar types
t = self.type
if t[0] == 's':
t = 'u' + t[1:]
return t
def _attr_typol(self):
raise Exception(f"Type policy not implemented for class type {self.type}")
......@@ -326,15 +335,13 @@ class TypeScalar(Type):
maybe_enum = not self.is_bitfield and 'enum' in self.attr
if maybe_enum and self.family.consts[self.attr['enum']].enum_name:
self.type_name = f"enum {self.family.name}_{c_lower(self.attr['enum'])}"
elif self.is_auto_scalar:
self.type_name = '__' + self.type[0] + '64'
else:
self.type_name = '__' + self.type
def _mnl_type(self):
t = self.type
# mnl does not have a helper for signed types
if t[0] == 's':
t = 'u' + t[1:]
return t
def mnl_type(self):
return self._mnl_type()
def _attr_policy(self, policy):
if 'flags-mask' in self.checks or self.is_bitfield:
......@@ -357,16 +364,16 @@ class TypeScalar(Type):
return super()._attr_policy(policy)
def _attr_typol(self):
return f'.type = YNL_PT_U{self.type[1:]}, '
return f'.type = YNL_PT_U{c_upper(self.type[1:])}, '
def arg_member(self, ri):
return [f'{self.type_name} {self.c_name}{self.byte_order_comment}']
def attr_put(self, ri, var):
self._attr_put_simple(ri, var, self._mnl_type())
self._attr_put_simple(ri, var, self.mnl_type())
def _attr_get(self, ri, var):
return f"{var}->{self.c_name} = mnl_attr_get_{self._mnl_type()}(attr);", None, None
return f"{var}->{self.c_name} = mnl_attr_get_{self.mnl_type()}(attr);", None, None
def _setter_lines(self, ri, member, presence):
return [f"{member} = {self.c_name};"]
......@@ -524,12 +531,8 @@ class TypeMultiAttr(Type):
def presence_type(self):
return 'count'
def _mnl_type(self):
t = self.type
# mnl does not have a helper for signed types
if t[0] == 's':
t = 'u' + t[1:]
return t
def mnl_type(self):
return self._mnl_type()
def _complex_member_type(self, ri):
if 'type' not in self.attr or self.attr['type'] == 'nest':
......@@ -564,7 +567,7 @@ class TypeMultiAttr(Type):
def attr_put(self, ri, var):
if self.attr['type'] in scalars:
put_type = self._mnl_type()
put_type = self.mnl_type()
ri.cw.p(f"for (unsigned int i = 0; i < {var}->n_{self.c_name}; i++)")
ri.cw.p(f"mnl_attr_put_{put_type}(nlh, {self.enum_name}, {var}->{self.c_name}[i]);")
elif 'type' not in self.attr or self.attr['type'] == 'nest':
......@@ -1290,7 +1293,7 @@ class CodeWriter:
self.p(line)
scalars = {'u8', 'u16', 'u32', 'u64', 's32', 's64'}
scalars = {'u8', 'u16', 'u32', 'u64', 's32', 's64', 'uint', 'sint'}
direction_to_suffix = {
'reply': '_rsp',
......@@ -1580,11 +1583,8 @@ def _multi_parse(ri, struct, init_lines, local_vars):
ri.cw.p(f"parg.data = &dst->{aspec.c_name}[i];")
ri.cw.p(f"if ({aspec.nested_render_name}_parse(&parg, attr))")
ri.cw.p('return MNL_CB_ERROR;')
elif aspec['type'] in scalars:
t = aspec['type']
if t[0] == 's':
t = 'u' + t[1:]
ri.cw.p(f"dst->{aspec.c_name}[i] = mnl_attr_get_{t}(attr);")
elif aspec.type in scalars:
ri.cw.p(f"dst->{aspec.c_name}[i] = mnl_attr_get_{aspec.mnl_type()}(attr);")
else:
raise Exception('Nest parsing type not supported yet')
ri.cw.p('i++;')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment