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

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'tools-ynl-gen-additional-c-types-and-classic-netlink-handling'

Jakub Kicinski says:

====================
tools: ynl-gen: additional C types and classic netlink handling

This series is a bit of a random grab bag adding things we need
to generate code for rt-link.

First two patches are pretty random code cleanups.

Patch 3 adds default values if the spec is missing them.

Patch 4 adds support for setting Netlink request flags
(NLM_F_CREATE, NLM_F_REPLACE etc.). Classic netlink uses those
quite a bit.

Patches 5 and 6 extend the notification handling for variations
used in classic netlink. Patch 6 adds support for when notification
ID is the same as the ID of the response message to GET.

Next 4 patches add support for handling a couple of complex types.
These are supported by the schema and Python but C code gen wasn't
there.

Patch 11 is a bit of a hack, it skips code related to kernel
policy generation, since we don't need it for classic netlink.

Patch 12 adds support for having different fixed headers per op.
Something we could avoid in previous rtnetlink specs but some
specs do mix.

v2: https://lore.kernel.org/20250425024311.1589323-1-kuba@kernel.org
v1: https://lore.kernel.org/20250424021207.1167791-1-kuba@kernel.org
====================

Link: https://patch.msgid.link/20250429154704.2613851-1-kuba@kernel.org
Signed-off-by: Paolo Abeni <pabeni@redhat.com>

+196 -48
+1 -1
tools/net/ynl/lib/ynl-priv.h
··· 94 94 unsigned char data[] __attribute__((aligned(8))); 95 95 }; 96 96 97 - struct nlmsghdr *ynl_msg_start_req(struct ynl_sock *ys, __u32 id); 97 + struct nlmsghdr *ynl_msg_start_req(struct ynl_sock *ys, __u32 id, __u16 flags); 98 98 struct nlmsghdr *ynl_msg_start_dump(struct ynl_sock *ys, __u32 id); 99 99 100 100 struct nlmsghdr *
+6 -6
tools/net/ynl/lib/ynl.c
··· 191 191 n = snprintf(bad_attr, sizeof(bad_attr), "%sbad attribute: ", 192 192 str ? " (" : ""); 193 193 194 - start = ynl_nlmsg_data_offset(ys->nlh, ys->family->hdr_len); 194 + start = ynl_nlmsg_data_offset(ys->nlh, ys->req_hdr_len); 195 195 end = ynl_nlmsg_end_addr(ys->nlh); 196 196 197 197 off = ys->err.attr_offs; 198 198 off -= sizeof(struct nlmsghdr); 199 - off -= ys->family->hdr_len; 199 + off -= ys->req_hdr_len; 200 200 201 201 n += ynl_err_walk(ys, start, end, off, ys->req_policy, 202 202 &bad_attr[n], sizeof(bad_attr) - n, NULL); ··· 216 216 n = snprintf(miss_attr, sizeof(miss_attr), "%smissing attribute: ", 217 217 bad_attr[0] ? ", " : (str ? " (" : "")); 218 218 219 - start = ynl_nlmsg_data_offset(ys->nlh, ys->family->hdr_len); 219 + start = ynl_nlmsg_data_offset(ys->nlh, ys->req_hdr_len); 220 220 end = ynl_nlmsg_end_addr(ys->nlh); 221 221 222 222 nest_pol = ys->req_policy; 223 223 if (tb[NLMSGERR_ATTR_MISS_NEST]) { 224 224 off = ynl_attr_get_u32(tb[NLMSGERR_ATTR_MISS_NEST]); 225 225 off -= sizeof(struct nlmsghdr); 226 - off -= ys->family->hdr_len; 226 + off -= ys->req_hdr_len; 227 227 228 228 n += ynl_err_walk(ys, start, end, off, ys->req_policy, 229 229 &miss_attr[n], sizeof(miss_attr) - n, ··· 451 451 return nlh; 452 452 } 453 453 454 - struct nlmsghdr *ynl_msg_start_req(struct ynl_sock *ys, __u32 id) 454 + struct nlmsghdr *ynl_msg_start_req(struct ynl_sock *ys, __u32 id, __u16 flags) 455 455 { 456 - return ynl_msg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK); 456 + return ynl_msg_start(ys, id, NLM_F_REQUEST | NLM_F_ACK | flags); 457 457 } 458 458 459 459 struct nlmsghdr *ynl_msg_start_dump(struct ynl_sock *ys, __u32 id)
+14
tools/net/ynl/lib/ynl.h
··· 80 80 81 81 struct nlmsghdr *nlh; 82 82 const struct ynl_policy_nest *req_policy; 83 + size_t req_hdr_len; 83 84 unsigned char *tx_buf; 84 85 unsigned char *rx_buf; 85 86 unsigned char raw_buf[]; 87 + }; 88 + 89 + /** 90 + * struct ynl_string - parsed individual string 91 + * @len: length of the string (excluding terminating character) 92 + * @str: value of the string 93 + * 94 + * Parsed and nul-terminated string. This struct is only used for arrays of 95 + * strings. Non-array string members are placed directly in respective types. 96 + */ 97 + struct ynl_string { 98 + unsigned int len; 99 + char str[]; 86 100 }; 87 101 88 102 struct ynl_sock *
+175 -41
tools/net/ynl/pyynl/ynl_gen_c.py
··· 175 175 def arg_member(self, ri): 176 176 member = self._complex_member_type(ri) 177 177 if member: 178 - arg = [member + ' *' + self.c_name] 178 + spc = ' ' if member[-1] != '*' else '' 179 + arg = [member + spc + '*' + self.c_name] 179 180 if self.presence_type() == 'count': 180 181 arg += ['unsigned int n_' + self.c_name] 181 182 return arg 182 183 raise Exception(f"Struct member not implemented for class type {self.type}") 183 184 184 185 def struct_member(self, ri): 185 - if self.is_multi_val(): 186 - ri.cw.p(f"unsigned int n_{self.c_name};") 187 186 member = self._complex_member_type(ri) 188 187 if member: 188 + if self.is_multi_val(): 189 + ri.cw.p(f"unsigned int n_{self.c_name};") 189 190 ptr = '*' if self.is_multi_val() else '' 190 191 if self.is_recursive_for_op(ri): 191 192 ptr = '*' 192 - ri.cw.p(f"{member} {ptr}{self.c_name};") 193 + spc = ' ' if member[-1] != '*' else '' 194 + ri.cw.p(f"{member}{spc}{ptr}{self.c_name};") 193 195 return 194 196 members = self.arg_member(ri) 195 197 for one in members: ··· 357 355 if 'byte-order' in attr: 358 356 self.byte_order_comment = f" /* {attr['byte-order']} */" 359 357 360 - if 'enum' in self.attr: 361 - enum = self.family.consts[self.attr['enum']] 362 - low, high = enum.value_range() 363 - if 'min' not in self.checks: 364 - if low != 0 or self.type[0] == 's': 365 - self.checks['min'] = low 366 - if 'max' not in self.checks: 367 - self.checks['max'] = high 368 - 369 - if 'min' in self.checks and 'max' in self.checks: 370 - if self.get_limit('min') > self.get_limit('max'): 371 - raise Exception(f'Invalid limit for "{self.name}" min: {self.get_limit("min")} max: {self.get_limit("max")}') 372 - self.checks['range'] = True 373 - 374 - low = min(self.get_limit('min', 0), self.get_limit('max', 0)) 375 - high = max(self.get_limit('min', 0), self.get_limit('max', 0)) 376 - if low < 0 and self.type[0] == 'u': 377 - raise Exception(f'Invalid limit for "{self.name}" negative limit for unsigned type') 378 - if low < -32768 or high > 32767: 379 - self.checks['full-range'] = True 358 + # Classic families have some funny enums, don't bother 359 + # computing checks, since we only need them for kernel policies 360 + if not family.is_classic(): 361 + self._init_checks() 380 362 381 363 # Added by resolve(): 382 364 self.is_bitfield = None ··· 384 398 self.type_name = '__' + self.type[0] + '64' 385 399 else: 386 400 self.type_name = '__' + self.type 401 + 402 + def _init_checks(self): 403 + if 'enum' in self.attr: 404 + enum = self.family.consts[self.attr['enum']] 405 + low, high = enum.value_range() 406 + if 'min' not in self.checks: 407 + if low != 0 or self.type[0] == 's': 408 + self.checks['min'] = low 409 + if 'max' not in self.checks: 410 + self.checks['max'] = high 411 + 412 + if 'min' in self.checks and 'max' in self.checks: 413 + if self.get_limit('min') > self.get_limit('max'): 414 + raise Exception(f'Invalid limit for "{self.name}" min: {self.get_limit("min")} max: {self.get_limit("max")}') 415 + self.checks['range'] = True 416 + 417 + low = min(self.get_limit('min', 0), self.get_limit('max', 0)) 418 + high = max(self.get_limit('min', 0), self.get_limit('max', 0)) 419 + if low < 0 and self.type[0] == 'u': 420 + raise Exception(f'Invalid limit for "{self.name}" negative limit for unsigned type') 421 + if low < -32768 or high > 32767: 422 + self.checks['full-range'] = True 387 423 388 424 def _attr_policy(self, policy): 389 425 if 'flags-mask' in self.checks or self.is_bitfield: ··· 646 638 def _complex_member_type(self, ri): 647 639 if 'type' not in self.attr or self.attr['type'] == 'nest': 648 640 return self.nested_struct_type 641 + elif self.attr['type'] == 'binary' and 'struct' in self.attr: 642 + return None # use arg_member() 643 + elif self.attr['type'] == 'string': 644 + return 'struct ynl_string *' 649 645 elif self.attr['type'] in scalars: 650 646 scalar_pfx = '__' if ri.ku_space == 'user' else '' 651 647 return scalar_pfx + self.attr['type'] 652 648 else: 653 649 raise Exception(f"Sub-type {self.attr['type']} not supported yet") 654 650 651 + def arg_member(self, ri): 652 + if self.type == 'binary' and 'struct' in self.attr: 653 + return [f'struct {c_lower(self.attr["struct"])} *{self.c_name}', 654 + f'unsigned int n_{self.c_name}'] 655 + return super().arg_member(ri) 656 + 655 657 def free_needs_iter(self): 656 - return 'type' not in self.attr or self.attr['type'] == 'nest' 658 + return self.attr['type'] in {'nest', 'string'} 657 659 658 660 def _free_lines(self, ri, var, ref): 659 661 lines = [] 660 662 if self.attr['type'] in scalars: 661 663 lines += [f"free({var}->{ref}{self.c_name});"] 664 + elif self.attr['type'] == 'binary' and 'struct' in self.attr: 665 + lines += [f"free({var}->{ref}{self.c_name});"] 666 + elif self.attr['type'] == 'string': 667 + lines += [ 668 + f"for (i = 0; i < {var}->{ref}n_{self.c_name}; i++)", 669 + f"free({var}->{ref}{self.c_name}[i]);", 670 + f"free({var}->{ref}{self.c_name});", 671 + ] 662 672 elif 'type' not in self.attr or self.attr['type'] == 'nest': 663 673 lines += [ 664 674 f"for (i = 0; i < {var}->{ref}n_{self.c_name}; i++)", ··· 701 675 put_type = self.type 702 676 ri.cw.p(f"for (i = 0; i < {var}->n_{self.c_name}; i++)") 703 677 ri.cw.p(f"ynl_attr_put_{put_type}(nlh, {self.enum_name}, {var}->{self.c_name}[i]);") 678 + elif self.attr['type'] == 'binary' and 'struct' in self.attr: 679 + ri.cw.p(f"for (i = 0; i < {var}->n_{self.c_name}; i++)") 680 + ri.cw.p(f"ynl_attr_put(nlh, {self.enum_name}, &{var}->{self.c_name}[i], sizeof(struct {c_lower(self.attr['struct'])}));") 681 + elif self.attr['type'] == 'string': 682 + ri.cw.p(f"for (i = 0; i < {var}->n_{self.c_name}; i++)") 683 + ri.cw.p(f"ynl_attr_put_str(nlh, {self.enum_name}, {var}->{self.c_name}[i]->str);") 704 684 elif 'type' not in self.attr or self.attr['type'] == 'nest': 705 685 ri.cw.p(f"for (i = 0; i < {var}->n_{self.c_name}; i++)") 706 686 self._attr_put_line(ri, var, f"{self.nested_render_name}_put(nlh, " + ··· 734 702 elif self.attr['sub-type'] in scalars: 735 703 scalar_pfx = '__' if ri.ku_space == 'user' else '' 736 704 return scalar_pfx + self.attr['sub-type'] 705 + elif self.attr['sub-type'] == 'binary' and 'exact-len' in self.checks: 706 + return None # use arg_member() 737 707 else: 738 708 raise Exception(f"Sub-type {self.attr['sub-type']} not supported yet") 709 + 710 + def arg_member(self, ri): 711 + if self.sub_type == 'binary' and 'exact-len' in self.checks: 712 + return [f'unsigned char (*{self.c_name})[{self.checks["exact-len"]}]', 713 + f'unsigned int n_{self.c_name}'] 714 + return super().arg_member(ri) 739 715 740 716 def _attr_typol(self): 741 717 if self.attr['sub-type'] in scalars: 742 718 return f'.type = YNL_PT_U{c_upper(self.sub_type[1:])}, ' 719 + elif self.attr['sub-type'] == 'binary' and 'exact-len' in self.checks: 720 + return f'.type = YNL_PT_BINARY, .len = {self.checks["exact-len"]}, ' 743 721 else: 744 722 return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, ' 745 723 ··· 762 720 f'\t{var}->n_{self.c_name}++;', 763 721 '}'] 764 722 return get_lines, None, local_vars 723 + 724 + def attr_put(self, ri, var): 725 + ri.cw.p(f'array = ynl_attr_nest_start(nlh, {self.enum_name});') 726 + if self.sub_type in scalars: 727 + put_type = self.sub_type 728 + ri.cw.block_start(line=f'for (i = 0; i < {var}->n_{self.c_name}; i++)') 729 + ri.cw.p(f"ynl_attr_put_{put_type}(nlh, i, {var}->{self.c_name}[i]);") 730 + ri.cw.block_end() 731 + elif self.sub_type == 'binary' and 'exact-len' in self.checks: 732 + ri.cw.p(f'for (i = 0; i < {var}->n_{self.c_name}; i++)') 733 + ri.cw.p(f"ynl_attr_put(nlh, i, {var}->{self.c_name}[i], {self.checks['exact-len']});") 734 + else: 735 + raise Exception(f"Put for ArrayNest sub-type {self.attr['sub-type']} not supported, yet") 736 + ri.cw.p('ynl_attr_nest_end(nlh, array);') 737 + 738 + def _setter_lines(self, ri, member, presence): 739 + # For multi-attr we have a count, not presence, hack up the presence 740 + presence = presence[:-(len('_present.') + len(self.c_name))] + "n_" + self.c_name 741 + return [f"{member} = {self.c_name};", 742 + f"{presence} = n_{self.c_name};"] 765 743 766 744 767 745 class TypeNestTypeValue(Type): ··· 870 808 if self._inherited != new_inherited: 871 809 raise Exception("Inheriting different members not supported") 872 810 self.inherited = [c_lower(x) for x in sorted(self._inherited)] 811 + 812 + def free_needs_iter(self): 813 + for _, attr in self.attr_list: 814 + if attr.free_needs_iter(): 815 + return True 816 + return False 873 817 874 818 875 819 class EnumEntry(SpecEnumEntry): ··· 983 915 elif elem['type'] == 'nest': 984 916 t = TypeNest(self.family, self, elem, value) 985 917 elif elem['type'] == 'indexed-array' and 'sub-type' in elem: 986 - if elem["sub-type"] in ['nest', 'u32']: 918 + if elem["sub-type"] in ['binary', 'nest', 'u32']: 987 919 t = TypeArrayNest(self.family, self, elem, value) 988 920 else: 989 921 raise Exception(f'new_attr: unsupported sub-type {elem["sub-type"]}') ··· 1000 932 1001 933 class Operation(SpecOperation): 1002 934 def __init__(self, family, yaml, req_value, rsp_value): 935 + # Fill in missing operation properties (for fixed hdr-only msgs) 936 + for mode in ['do', 'dump', 'event']: 937 + for direction in ['request', 'reply']: 938 + try: 939 + yaml[mode][direction].setdefault('attributes', []) 940 + except KeyError: 941 + pass 942 + 1003 943 super().__init__(family, yaml, req_value, rsp_value) 1004 944 1005 945 self.render_name = c_lower(family.ident_name + '_' + self.name) ··· 1091 1015 1092 1016 # dict space-name -> 'request': set(attrs), 'reply': set(attrs) 1093 1017 self.root_sets = dict() 1094 - # dict space-name -> set('request', 'reply') 1018 + # dict space-name -> Struct 1095 1019 self.pure_nested_structs = dict() 1096 1020 1097 1021 self._mark_notify() ··· 1311 1235 self.op = op 1312 1236 1313 1237 self.fixed_hdr = None 1238 + self.fixed_hdr_len = 'ys->family->hdr_len' 1314 1239 if op and op.fixed_header: 1315 1240 self.fixed_hdr = 'struct ' + c_lower(op.fixed_header) 1241 + if op.fixed_header != family.fixed_header: 1242 + if family.is_classic(): 1243 + self.fixed_hdr_len = f"sizeof({self.fixed_hdr})" 1244 + else: 1245 + raise Exception(f"Per-op fixed header not supported, yet") 1246 + 1316 1247 1317 1248 # 'do' and 'dump' response parsing is identical 1318 1249 self.type_consistent = True ··· 1350 1267 1351 1268 self.struct = dict() 1352 1269 if op_mode == 'notify': 1353 - op_mode = 'do' 1270 + op_mode = 'do' if 'do' in op else 'dump' 1354 1271 for op_dir in ['request', 'reply']: 1355 1272 if op: 1356 1273 type_list = [] ··· 1362 1279 1363 1280 def type_empty(self, key): 1364 1281 return len(self.struct[key].attr_list) == 0 and self.fixed_hdr is None 1282 + 1283 + def needs_nlflags(self, direction): 1284 + return self.op_mode == 'do' and direction == 'request' and self.family.is_classic() 1365 1285 1366 1286 1367 1287 class CodeWriter: ··· 1771 1685 local_vars.append('struct nlattr *nest;') 1772 1686 init_lines.append("nest = ynl_attr_nest_start(nlh, attr_type);") 1773 1687 1688 + has_anest = False 1689 + has_count = False 1774 1690 for _, arg in struct.member_list(): 1775 - if arg.presence_type() == 'count': 1776 - local_vars.append('unsigned int i;') 1777 - break 1691 + has_anest |= arg.type == 'indexed-array' 1692 + has_count |= arg.presence_type() == 'count' 1693 + if has_anest: 1694 + local_vars.append('struct nlattr *array;') 1695 + if has_count: 1696 + local_vars.append('unsigned int i;') 1778 1697 1779 1698 put_req_nested_prototype(ri, struct, suffix='') 1780 1699 ri.cw.block_start() ··· 1806 1715 if ri.fixed_hdr: 1807 1716 local_vars += ['void *hdr;'] 1808 1717 iter_line = "ynl_attr_for_each(attr, nlh, yarg->ys->family->hdr_len)" 1718 + if ri.op.fixed_header != ri.family.fixed_header: 1719 + if ri.family.is_classic(): 1720 + iter_line = f"ynl_attr_for_each(attr, nlh, sizeof({ri.fixed_hdr}))" 1721 + else: 1722 + raise Exception(f"Per-op fixed header not supported, yet") 1809 1723 1810 1724 array_nests = set() 1811 1725 multi_attrs = set() 1812 1726 needs_parg = False 1813 1727 for arg, aspec in struct.member_list(): 1814 1728 if aspec['type'] == 'indexed-array' and 'sub-type' in aspec: 1815 - if aspec["sub-type"] == 'nest': 1729 + if aspec["sub-type"] in {'binary', 'nest'}: 1816 1730 local_vars.append(f'const struct nlattr *attr_{aspec.c_name};') 1817 1731 array_nests.add(arg) 1818 1732 elif aspec['sub-type'] in scalars: ··· 1890 1794 ri.cw.p('return YNL_PARSE_CB_ERROR;') 1891 1795 elif aspec.sub_type in scalars: 1892 1796 ri.cw.p(f"dst->{aspec.c_name}[i] = ynl_attr_get_{aspec.sub_type}(attr);") 1797 + elif aspec.sub_type == 'binary' and 'exact-len' in aspec.checks: 1798 + # Length is validated by typol 1799 + ri.cw.p(f'memcpy(dst->{aspec.c_name}[i], ynl_attr_data(attr), {aspec.checks["exact-len"]});') 1893 1800 else: 1894 1801 raise Exception(f"Nest parsing type not supported in {aspec['name']}") 1895 1802 ri.cw.p('i++;') ··· 1916 1817 ri.cw.p('return YNL_PARSE_CB_ERROR;') 1917 1818 elif aspec.type in scalars: 1918 1819 ri.cw.p(f"dst->{aspec.c_name}[i] = ynl_attr_get_{aspec.type}(attr);") 1820 + elif aspec.type == 'binary' and 'struct' in aspec: 1821 + ri.cw.p('size_t len = ynl_attr_data_len(attr);') 1822 + ri.cw.nl() 1823 + ri.cw.p(f'if (len > sizeof(dst->{aspec.c_name}[0]))') 1824 + ri.cw.p(f'len = sizeof(dst->{aspec.c_name}[0]);') 1825 + ri.cw.p(f"memcpy(&dst->{aspec.c_name}[i], ynl_attr_data(attr), len);") 1826 + elif aspec.type == 'string': 1827 + ri.cw.p('unsigned int len;') 1828 + ri.cw.nl() 1829 + ri.cw.p('len = strnlen(ynl_attr_get_str(attr), ynl_attr_data_len(attr));') 1830 + ri.cw.p(f'dst->{aspec.c_name}[i] = malloc(sizeof(struct ynl_string) + len + 1);') 1831 + ri.cw.p(f"dst->{aspec.c_name}[i]->len = len;") 1832 + ri.cw.p(f"memcpy(dst->{aspec.c_name}[i]->str, ynl_attr_get_str(attr), len);") 1833 + ri.cw.p(f"dst->{aspec.c_name}[i]->str[len] = 0;") 1919 1834 else: 1920 - raise Exception('Nest parsing type not supported yet') 1835 + raise Exception(f'Nest parsing of type {aspec.type} not supported yet') 1921 1836 ri.cw.p('i++;') 1922 1837 ri.cw.block_end() 1923 1838 ri.cw.block_end() ··· 2023 1910 ri.cw.write_func_lvar(local_vars) 2024 1911 2025 1912 if ri.family.is_classic(): 2026 - ri.cw.p(f"nlh = ynl_msg_start_req(ys, {ri.op.enum_name});") 1913 + ri.cw.p(f"nlh = ynl_msg_start_req(ys, {ri.op.enum_name}, req->_nlmsg_flags);") 2027 1914 else: 2028 1915 ri.cw.p(f"nlh = ynl_gemsg_start_req(ys, {ri.nl.get_family_id()}, {ri.op.enum_name}, 1);") 2029 1916 2030 1917 ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;") 1918 + ri.cw.p(f"ys->req_hdr_len = {ri.fixed_hdr_len};") 2031 1919 if 'reply' in ri.op[ri.op_mode]: 2032 1920 ri.cw.p(f"yrs.yarg.rsp_policy = &{ri.struct['reply'].render_name}_nest;") 2033 1921 ri.cw.nl() ··· 2108 1994 2109 1995 if "request" in ri.op[ri.op_mode]: 2110 1996 ri.cw.p(f"ys->req_policy = &{ri.struct['request'].render_name}_nest;") 1997 + ri.cw.p(f"ys->req_hdr_len = {ri.fixed_hdr_len};") 2111 1998 ri.cw.nl() 2112 1999 for _, attr in ri.struct["request"].member_list(): 2113 2000 attr.attr_put(ri, "req") ··· 2154 2039 ri.cw.write_func_prot('void', f"{name}_free", [f"struct {struct_name} *{arg}"], suffix=suffix) 2155 2040 2156 2041 2042 + def print_nlflags_set(ri, direction): 2043 + name = op_prefix(ri, direction) 2044 + ri.cw.write_func_prot(f'static inline void', f"{name}_set_nlflags", 2045 + [f"struct {name} *req", "__u16 nl_flags"]) 2046 + ri.cw.block_start() 2047 + ri.cw.p('req->_nlmsg_flags = nl_flags;') 2048 + ri.cw.block_end() 2049 + ri.cw.nl() 2050 + 2051 + 2157 2052 def _print_type(ri, direction, struct): 2158 2053 suffix = f'_{ri.type_name}{direction_to_suffix[direction]}' 2159 2054 if not direction and ri.type_name_conflict: ··· 2174 2049 2175 2050 ri.cw.block_start(line=f"struct {ri.family.c_name}{suffix}") 2176 2051 2052 + if ri.needs_nlflags(direction): 2053 + ri.cw.p('__u16 _nlmsg_flags;') 2054 + ri.cw.nl() 2177 2055 if ri.fixed_hdr: 2178 2056 ri.cw.p(ri.fixed_hdr + ' _hdr;') 2179 2057 ri.cw.nl() ··· 2215 2087 def print_type_helpers(ri, direction, deref=False): 2216 2088 print_free_prototype(ri, direction) 2217 2089 ri.cw.nl() 2090 + 2091 + if ri.needs_nlflags(direction): 2092 + print_nlflags_set(ri, direction) 2218 2093 2219 2094 if ri.ku_space == 'user' and direction == 'request': 2220 2095 for _, attr in ri.struct[direction].member_list(): ··· 2287 2156 2288 2157 2289 2158 def _free_type_members_iter(ri, struct): 2290 - for _, attr in struct.member_list(): 2291 - if attr.free_needs_iter(): 2292 - ri.cw.p('unsigned int i;') 2293 - ri.cw.nl() 2294 - break 2159 + if struct.free_needs_iter(): 2160 + ri.cw.p('unsigned int i;') 2161 + ri.cw.nl() 2295 2162 2296 2163 2297 2164 def _free_type_members(ri, var, struct, ref=''): ··· 2885 2756 2886 2757 2887 2758 def _render_user_ntf_entry(ri, op): 2888 - ri.cw.block_start(line=f"[{op.enum_name}] = ") 2759 + if not ri.family.is_classic(): 2760 + ri.cw.block_start(line=f"[{op.enum_name}] = ") 2761 + else: 2762 + crud_op = ri.family.req_by_value[op.rsp_value] 2763 + ri.cw.block_start(line=f"[{crud_op.enum_name}] = ") 2889 2764 ri.cw.p(f".alloc_sz\t= sizeof({type_name(ri, 'event')}),") 2890 2765 ri.cw.p(f".cb\t\t= {op_prefix(ri, 'reply', deref=True)}_parse,") 2891 2766 ri.cw.p(f".policy\t\t= &{ri.struct['reply'].render_name}_nest,") ··· 2928 2795 cw.p(f'.is_classic\t= true,') 2929 2796 cw.p(f'.classic_id\t= {family.get("protonum")},') 2930 2797 if family.is_classic(): 2931 - cw.p(f'.hdr_len\t= sizeof(struct {c_lower(family.fixed_header)}),') 2798 + if family.fixed_header: 2799 + cw.p(f'.hdr_len\t= sizeof(struct {c_lower(family.fixed_header)}),') 2932 2800 elif family.fixed_header: 2933 2801 cw.p(f'.hdr_len\t= sizeof(struct genlmsghdr) + sizeof(struct {c_lower(family.fixed_header)}),') 2934 2802 else: