From: Jakub Kicinski Date: Thu, 15 May 2025 23:16:47 +0000 (-0700) Subject: tools: ynl-gen: submsg: support parsing and rendering sub-messages X-Git-Tag: v6.16-rc1~132^2~84^2~3 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b9e03e263610a8d872c753bc882676d3cba1797b;p=thirdparty%2Fkernel%2Flinux.git tools: ynl-gen: submsg: support parsing and rendering sub-messages Adjust parsing and rendering appropriately to make sub-messages work. Rendering is pretty trivial, as the submsg -> netlink conversion looks like rendering a nest in which only one attr was set. Only trick is that we use the enum value of the sub-message rather than the nest as the type, and effectively skip one layer of nesting. A real double nested struct would look like this: [SELECTOR] [SUBMSG] [NEST] [MSG1-ATTR] A submsg "is" the nest so by skipping I mean: [SELECTOR] [SUBMSG] [MSG1-ATTR] There is no extra validation in YNL if caller has set the selector matching the submsg type (e.g. link type = "macvlan" but the nest attrs are set to carry "veth"). Let the kernel handle that. Parsing side is a little more specialized as we need to render and insert a new kind of function which switches between what to parse based on the selector. But code isn't too complicated. Reviewed-by: Donald Hunter Link: https://patch.msgid.link/20250515231650.1325372-7-kuba@kernel.org Signed-off-by: Jakub Kicinski --- diff --git a/tools/net/ynl/lib/ynl-priv.h b/tools/net/ynl/lib/ynl-priv.h index 5debb09491e7b..fbc058dd1c3e2 100644 --- a/tools/net/ynl/lib/ynl-priv.h +++ b/tools/net/ynl/lib/ynl-priv.h @@ -25,6 +25,7 @@ enum ynl_policy_type { YNL_PT_UINT, YNL_PT_NUL_STR, YNL_PT_BITFIELD32, + YNL_PT_SUBMSG, }; enum ynl_parse_result { @@ -103,6 +104,8 @@ struct nlmsghdr * ynl_gemsg_start_dump(struct ynl_sock *ys, __u32 id, __u8 cmd, __u8 version); int ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr); +int ynl_submsg_failed(struct ynl_parse_arg *yarg, const char *field_name, + const char *sel_name); /* YNL specific helpers used by the auto-generated code */ diff --git a/tools/net/ynl/lib/ynl.c b/tools/net/ynl/lib/ynl.c index a0b54ad4c0738..25fc6501349bc 100644 --- a/tools/net/ynl/lib/ynl.c +++ b/tools/net/ynl/lib/ynl.c @@ -384,6 +384,15 @@ int ynl_attr_validate(struct ynl_parse_arg *yarg, const struct nlattr *attr) return 0; } +int ynl_submsg_failed(struct ynl_parse_arg *yarg, const char *field_name, + const char *sel_name) +{ + yerr(yarg->ys, YNL_ERROR_SUBMSG_KEY, + "Parsing error: Sub-message key not set (msg %s, key %s)", + field_name, sel_name); + return YNL_PARSE_CB_ERROR; +} + /* Generic code */ static void ynl_err_reset(struct ynl_sock *ys) diff --git a/tools/net/ynl/lib/ynl.h b/tools/net/ynl/lib/ynl.h index 32efeb2248293..db7c0591a63f5 100644 --- a/tools/net/ynl/lib/ynl.h +++ b/tools/net/ynl/lib/ynl.h @@ -23,6 +23,7 @@ enum ynl_error_code { YNL_ERROR_INV_RESP, YNL_ERROR_INPUT_INVALID, YNL_ERROR_INPUT_TOO_BIG, + YNL_ERROR_SUBMSG_KEY, }; /** diff --git a/tools/net/ynl/pyynl/ynl_gen_c.py b/tools/net/ynl/pyynl/ynl_gen_c.py index 020aa34b890bb..b6b54d6fa9065 100755 --- a/tools/net/ynl/pyynl/ynl_gen_c.py +++ b/tools/net/ynl/pyynl/ynl_gen_c.py @@ -878,7 +878,16 @@ class TypeNestTypeValue(Type): class TypeSubMessage(TypeNest): - pass + def _attr_get(self, ri, var): + sel = c_lower(self['selector']) + get_lines = [f'if (!{var}->{sel})', + f'return ynl_submsg_failed(yarg, "%s", "%s");' % + (self.name, self['selector']), + f"if ({self.nested_render_name}_parse(&parg, {var}->{sel}, attr))", + "return YNL_PARSE_CB_ERROR;"] + init_lines = [f"parg.rsp_policy = &{self.nested_render_name}_nest;", + f"parg.data = &{var}->{self.c_name};"] + return get_lines, init_lines, None class Struct: @@ -1818,11 +1827,34 @@ def print_dump_prototype(ri): print_prototype(ri, "request") +def put_typol_submsg(cw, struct): + cw.block_start(line=f'const struct ynl_policy_attr {struct.render_name}_policy[] =') + + i = 0 + for name, arg in struct.member_list(): + cw.p('[%d] = { .type = YNL_PT_SUBMSG, .name = "%s", .nest = &%s_nest, },' % + (i, name, arg.nested_render_name)) + i += 1 + + cw.block_end(line=';') + cw.nl() + + cw.block_start(line=f'const struct ynl_policy_nest {struct.render_name}_nest =') + cw.p(f'.max_attr = {i - 1},') + cw.p(f'.table = {struct.render_name}_policy,') + cw.block_end(line=';') + cw.nl() + + def put_typol_fwd(cw, struct): cw.p(f'extern const struct ynl_policy_nest {struct.render_name}_nest;') def put_typol(cw, struct): + if struct.submsg: + put_typol_submsg(cw, struct) + return + type_max = struct.attr_set.max_name cw.block_start(line=f'const struct ynl_policy_attr {struct.render_name}_policy[{type_max} + 1] =') @@ -1908,8 +1940,9 @@ def put_req_nested(ri, struct): local_vars = [] init_lines = [] - local_vars.append('struct nlattr *nest;') - init_lines.append("nest = ynl_attr_nest_start(nlh, attr_type);") + if struct.submsg is None: + local_vars.append('struct nlattr *nest;') + init_lines.append("nest = ynl_attr_nest_start(nlh, attr_type);") has_anest = False has_count = False @@ -1931,7 +1964,8 @@ def put_req_nested(ri, struct): for _, arg in struct.member_list(): arg.attr_put(ri, "obj") - ri.cw.p("ynl_attr_nest_end(nlh, nest);") + if struct.submsg is None: + ri.cw.p("ynl_attr_nest_end(nlh, nest);") ri.cw.nl() ri.cw.p('return 0;') @@ -1968,6 +2002,7 @@ def _multi_parse(ri, struct, init_lines, local_vars): if 'multi-attr' in aspec: multi_attrs.add(arg) needs_parg |= 'nested-attributes' in aspec + needs_parg |= 'sub-message' in aspec if array_nests or multi_attrs: local_vars.append('int i;') if needs_parg: @@ -2086,9 +2121,43 @@ def _multi_parse(ri, struct, init_lines, local_vars): ri.cw.nl() +def parse_rsp_submsg(ri, struct): + parse_rsp_nested_prototype(ri, struct, suffix='') + + var = 'dst' + + ri.cw.block_start() + ri.cw.write_func_lvar(['const struct nlattr *attr = nested;', + f'{struct.ptr_name}{var} = yarg->data;', + 'struct ynl_parse_arg parg;']) + + ri.cw.p('parg.ys = yarg->ys;') + ri.cw.nl() + + first = True + for name, arg in struct.member_list(): + kw = 'if' if first else 'else if' + first = False + + ri.cw.block_start(line=f'{kw} (!strcmp(sel, "{name}"))') + get_lines, init_lines, _ = arg._attr_get(ri, var) + for line in init_lines: + ri.cw.p(line) + for line in get_lines: + ri.cw.p(line) + if arg.presence_type() == 'present': + ri.cw.p(f"{var}->_present.{arg.c_name} = 1;") + ri.cw.block_end() + ri.cw.p('return 0;') + ri.cw.block_end() + ri.cw.nl() + + def parse_rsp_nested_prototype(ri, struct, suffix=';'): func_args = ['struct ynl_parse_arg *yarg', 'const struct nlattr *nested'] + if struct.submsg: + func_args.insert(1, 'const char *sel') for arg in struct.inherited: func_args.append('__u32 ' + arg) @@ -2097,6 +2166,9 @@ def parse_rsp_nested_prototype(ri, struct, suffix=';'): def parse_rsp_nested(ri, struct): + if struct.submsg: + return parse_rsp_submsg(ri, struct) + parse_rsp_nested_prototype(ri, struct, suffix='') local_vars = ['const struct nlattr *attr;',