loop-stop-using-vfs_iter_-read-write-for-buffered-i-.patch
ata-libata-sata-save-all-fields-from-sense-data-desc.patch
cxgb4-fix-memory-leak-in-cxgb4_init_ethtool_filters-.patch
-tools-ynl-gen-individually-free-previous-values-on-d.patch
-tools-ynl-gen-make-sure-we-validate-subtype-of-array.patch
netlink-specs-rt-link-add-an-attr-layer-around-alt-i.patch
netlink-specs-rt-link-adjust-mctp-attribute-naming.patch
net-b53-enable-bpdu-reception-for-management-port.patch
+++ /dev/null
-From 624965eab37b774683e2eb8643f22fb85c0f98cb Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Mon, 14 Apr 2025 14:18:46 -0700
-Subject: tools: ynl-gen: individually free previous values on double set
-
-From: Jakub Kicinski <kuba@kernel.org>
-
-[ Upstream commit ce6cb8113c842b94e77364b247c4f85c7b34e0c2 ]
-
-When user calls request_attrA_set() multiple times (for the same
-attribute), and attrA is of type which allocates memory -
-we try to free the previously associated values. For array
-types (including multi-attr) we have only freed the array,
-but the array may have contained pointers.
-
-Refactor the code generation for free attr and reuse the generated
-lines in setters to flush out the previous state. Since setters
-are static inlines in the header we need to add forward declarations
-for the free helpers of pure nested structs. Track which types get
-used by arrays and include the right forwad declarations.
-
-At least ethtool string set and bit set would not be freed without
-this. Tho, admittedly, overriding already set attribute twice is likely
-a very very rare thing to do.
-
-Fixes: be5bea1cc0bf ("net: add basic C code generators for Netlink")
-Reviewed-by: Donald Hunter <donald.hunter@gmail.com>
-Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
-Link: https://patch.msgid.link/20250414211851.602096-4-kuba@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- tools/net/ynl/ynl-gen-c.py | 62 +++++++++++++++++++++++++++-----------
- 1 file changed, 45 insertions(+), 17 deletions(-)
-
-diff --git a/tools/net/ynl/ynl-gen-c.py b/tools/net/ynl/ynl-gen-c.py
-index 463f1394ab971..265a0ec0ef811 100755
---- a/tools/net/ynl/ynl-gen-c.py
-+++ b/tools/net/ynl/ynl-gen-c.py
-@@ -126,9 +126,15 @@ class Type(SpecAttr):
- def free_needs_iter(self):
- return False
-
-- def free(self, ri, var, ref):
-+ def _free_lines(self, ri, var, ref):
- if self.is_multi_val() or self.presence_type() == 'len':
-- ri.cw.p(f'free({var}->{ref}{self.c_name});')
-+ return [f'free({var}->{ref}{self.c_name});']
-+ return []
-+
-+ def free(self, ri, var, ref):
-+ lines = self._free_lines(ri, var, ref)
-+ for line in lines:
-+ ri.cw.p(line)
-
- def arg_member(self, ri):
- member = self._complex_member_type(ri)
-@@ -224,6 +230,10 @@ class Type(SpecAttr):
- var = "req"
- member = f"{var}->{'.'.join(ref)}"
-
-+ local_vars = []
-+ if self.free_needs_iter():
-+ local_vars += ['unsigned int i;']
-+
- code = []
- presence = ''
- for i in range(0, len(ref)):
-@@ -233,6 +243,10 @@ class Type(SpecAttr):
- if i == len(ref) - 1 and self.presence_type() != 'bit':
- continue
- code.append(presence + ' = 1;')
-+ ref_path = '.'.join(ref[:-1])
-+ if ref_path:
-+ ref_path += '.'
-+ code += self._free_lines(ri, var, ref_path)
- code += self._setter_lines(ri, member, presence)
-
- func_name = f"{op_prefix(ri, direction, deref=deref)}_set_{'_'.join(ref)}"
-@@ -240,7 +254,8 @@ class Type(SpecAttr):
- alloc = bool([x for x in code if 'alloc(' in x])
- if free and not alloc:
- func_name = '__' + func_name
-- ri.cw.write_func('static inline void', func_name, body=code,
-+ ri.cw.write_func('static inline void', func_name, local_vars=local_vars,
-+ body=code,
- args=[f'{type_name(ri, direction, deref=deref)} *{var}'] + self.arg_member(ri))
-
-
-@@ -443,8 +458,7 @@ class TypeString(Type):
- ['unsigned int len;']
-
- def _setter_lines(self, ri, member, presence):
-- return [f"free({member});",
-- f"{presence}_len = strlen({self.c_name});",
-+ return [f"{presence}_len = strlen({self.c_name});",
- f"{member} = malloc({presence}_len + 1);",
- f'memcpy({member}, {self.c_name}, {presence}_len);',
- f'{member}[{presence}_len] = 0;']
-@@ -490,8 +504,7 @@ class TypeBinary(Type):
- ['unsigned int len;']
-
- def _setter_lines(self, ri, member, presence):
-- return [f"free({member});",
-- f"{presence}_len = len;",
-+ return [f"{presence}_len = len;",
- f"{member} = malloc({presence}_len);",
- f'memcpy({member}, {self.c_name}, {presence}_len);']
-
-@@ -528,12 +541,14 @@ class TypeNest(Type):
- def _complex_member_type(self, ri):
- return self.nested_struct_type
-
-- def free(self, ri, var, ref):
-+ def _free_lines(self, ri, var, ref):
-+ lines = []
- at = '&'
- if self.is_recursive_for_op(ri):
- at = ''
-- ri.cw.p(f'if ({var}->{ref}{self.c_name})')
-- ri.cw.p(f'{self.nested_render_name}_free({at}{var}->{ref}{self.c_name});')
-+ lines += [f'if ({var}->{ref}{self.c_name})']
-+ lines += [f'{self.nested_render_name}_free({at}{var}->{ref}{self.c_name});']
-+ return lines
-
- def _attr_typol(self):
- return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
-@@ -586,15 +601,19 @@ class TypeMultiAttr(Type):
- def free_needs_iter(self):
- return 'type' not in self.attr or self.attr['type'] == 'nest'
-
-- def free(self, ri, var, ref):
-+ def _free_lines(self, ri, var, ref):
-+ lines = []
- if self.attr['type'] in scalars:
-- ri.cw.p(f"free({var}->{ref}{self.c_name});")
-+ lines += [f"free({var}->{ref}{self.c_name});"]
- elif 'type' not in self.attr or self.attr['type'] == 'nest':
-- ri.cw.p(f"for (i = 0; i < {var}->{ref}n_{self.c_name}; i++)")
-- ri.cw.p(f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name}[i]);')
-- ri.cw.p(f"free({var}->{ref}{self.c_name});")
-+ lines += [
-+ f"for (i = 0; i < {var}->{ref}n_{self.c_name}; i++)",
-+ f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name}[i]);',
-+ f"free({var}->{ref}{self.c_name});",
-+ ]
- else:
- raise Exception(f"Free of MultiAttr sub-type {self.attr['type']} not supported yet")
-+ return lines
-
- def _attr_policy(self, policy):
- return self.base_type._attr_policy(policy)
-@@ -620,8 +639,7 @@ class TypeMultiAttr(Type):
- def _setter_lines(self, ri, member, presence):
- # For multi-attr we have a count, not presence, hack up the presence
- presence = presence[:-(len('_present.') + len(self.c_name))] + "n_" + self.c_name
-- return [f"free({member});",
-- f"{member} = {self.c_name};",
-+ return [f"{member} = {self.c_name};",
- f"{presence} = n_{self.c_name};"]
-
-
-@@ -706,6 +724,7 @@ class Struct:
- self.request = False
- self.reply = False
- self.recursive = False
-+ self.in_multi_val = False # used by a MultiAttr or and legacy arrays
-
- self.attr_list = []
- self.attrs = dict()
-@@ -1071,6 +1090,10 @@ class Family(SpecFamily):
- if attr in rs_members['reply']:
- self.pure_nested_structs[nested].reply = True
-
-+ if spec.is_multi_val():
-+ child = self.pure_nested_structs.get(nested)
-+ child.in_multi_val = True
-+
- self._sort_pure_types()
-
- # Propagate the request / reply / recursive
-@@ -1085,6 +1108,8 @@ class Family(SpecFamily):
- struct.child_nests.update(child.child_nests)
- child.request |= struct.request
- child.reply |= struct.reply
-+ if spec.is_multi_val():
-+ child.in_multi_val = True
- if attr_set in struct.child_nests:
- struct.recursive = True
-
-@@ -2794,6 +2819,9 @@ def main():
- for attr_set, struct in parsed.pure_nested_structs.items():
- ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
- print_type_full(ri, struct)
-+ if struct.request and struct.in_multi_val:
-+ free_rsp_nested_prototype(ri)
-+ cw.nl()
-
- for op_name, op in parsed.ops.items():
- cw.p(f"/* ============== {op.enum_name} ============== */")
---
-2.39.5
-
+++ /dev/null
-From 0d29e4654faad31df924e67e0609de22cce40449 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Mon, 14 Apr 2025 14:18:47 -0700
-Subject: tools: ynl-gen: make sure we validate subtype of array-nest
-
-From: Jakub Kicinski <kuba@kernel.org>
-
-[ Upstream commit 57e7dedf2b8c72caa6f04b9e08b19e4f370562fa ]
-
-ArrayNest AKA indexed-array support currently skips inner type
-validation. We count the attributes and then we parse them,
-make sure we call validate, too. Otherwise buggy / unexpected
-kernel response may lead to crashes.
-
-Fixes: be5bea1cc0bf ("net: add basic C code generators for Netlink")
-Reviewed-by: Donald Hunter <donald.hunter@gmail.com>
-Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
-Link: https://patch.msgid.link/20250414211851.602096-5-kuba@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- tools/net/ynl/ynl-gen-c.py | 7 +++++--
- 1 file changed, 5 insertions(+), 2 deletions(-)
-
-diff --git a/tools/net/ynl/ynl-gen-c.py b/tools/net/ynl/ynl-gen-c.py
-index 265a0ec0ef811..40f1c3631f985 100755
---- a/tools/net/ynl/ynl-gen-c.py
-+++ b/tools/net/ynl/ynl-gen-c.py
-@@ -665,8 +665,11 @@ class TypeArrayNest(Type):
- def _attr_get(self, ri, var):
- local_vars = ['const struct nlattr *attr2;']
- get_lines = [f'attr_{self.c_name} = attr;',
-- 'ynl_attr_for_each_nested(attr2, attr)',
-- f'\t{var}->n_{self.c_name}++;']
-+ 'ynl_attr_for_each_nested(attr2, attr) {',
-+ '\tif (ynl_attr_validate(yarg, attr2))',
-+ '\t\treturn YNL_PARSE_CB_ERROR;',
-+ f'\t{var}->n_{self.c_name}++;',
-+ '}']
- return get_lines, None, local_vars
-
-
---
-2.39.5
-
nvmet-pci-epf-clear-cc-and-csts-when-disabling-the-c.patch
ata-libata-sata-save-all-fields-from-sense-data-desc.patch
cxgb4-fix-memory-leak-in-cxgb4_init_ethtool_filters-.patch
-tools-ynl-gen-individually-free-previous-values-on-d.patch
-tools-ynl-gen-make-sure-we-validate-subtype-of-array.patch
netlink-specs-rt-link-add-an-attr-layer-around-alt-i.patch
netlink-specs-rtnetlink-attribute-naming-corrections.patch
netlink-specs-rt-link-adjust-mctp-attribute-naming.patch
+++ /dev/null
-From 6abb31e231b2e22e26349d0aa7b351841a506690 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Mon, 14 Apr 2025 14:18:46 -0700
-Subject: tools: ynl-gen: individually free previous values on double set
-
-From: Jakub Kicinski <kuba@kernel.org>
-
-[ Upstream commit ce6cb8113c842b94e77364b247c4f85c7b34e0c2 ]
-
-When user calls request_attrA_set() multiple times (for the same
-attribute), and attrA is of type which allocates memory -
-we try to free the previously associated values. For array
-types (including multi-attr) we have only freed the array,
-but the array may have contained pointers.
-
-Refactor the code generation for free attr and reuse the generated
-lines in setters to flush out the previous state. Since setters
-are static inlines in the header we need to add forward declarations
-for the free helpers of pure nested structs. Track which types get
-used by arrays and include the right forwad declarations.
-
-At least ethtool string set and bit set would not be freed without
-this. Tho, admittedly, overriding already set attribute twice is likely
-a very very rare thing to do.
-
-Fixes: be5bea1cc0bf ("net: add basic C code generators for Netlink")
-Reviewed-by: Donald Hunter <donald.hunter@gmail.com>
-Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
-Link: https://patch.msgid.link/20250414211851.602096-4-kuba@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- tools/net/ynl/pyynl/ynl_gen_c.py | 62 +++++++++++++++++++++++---------
- 1 file changed, 45 insertions(+), 17 deletions(-)
-
-diff --git a/tools/net/ynl/pyynl/ynl_gen_c.py b/tools/net/ynl/pyynl/ynl_gen_c.py
-index c2eabc90dce8c..9c9a62a93afe7 100755
---- a/tools/net/ynl/pyynl/ynl_gen_c.py
-+++ b/tools/net/ynl/pyynl/ynl_gen_c.py
-@@ -157,9 +157,15 @@ class Type(SpecAttr):
- def free_needs_iter(self):
- return False
-
-- def free(self, ri, var, ref):
-+ def _free_lines(self, ri, var, ref):
- if self.is_multi_val() or self.presence_type() == 'len':
-- ri.cw.p(f'free({var}->{ref}{self.c_name});')
-+ return [f'free({var}->{ref}{self.c_name});']
-+ return []
-+
-+ def free(self, ri, var, ref):
-+ lines = self._free_lines(ri, var, ref)
-+ for line in lines:
-+ ri.cw.p(line)
-
- def arg_member(self, ri):
- member = self._complex_member_type(ri)
-@@ -258,6 +264,10 @@ class Type(SpecAttr):
- var = "req"
- member = f"{var}->{'.'.join(ref)}"
-
-+ local_vars = []
-+ if self.free_needs_iter():
-+ local_vars += ['unsigned int i;']
-+
- code = []
- presence = ''
- for i in range(0, len(ref)):
-@@ -267,6 +277,10 @@ class Type(SpecAttr):
- if i == len(ref) - 1 and self.presence_type() != 'bit':
- continue
- code.append(presence + ' = 1;')
-+ ref_path = '.'.join(ref[:-1])
-+ if ref_path:
-+ ref_path += '.'
-+ code += self._free_lines(ri, var, ref_path)
- code += self._setter_lines(ri, member, presence)
-
- func_name = f"{op_prefix(ri, direction, deref=deref)}_set_{'_'.join(ref)}"
-@@ -274,7 +288,8 @@ class Type(SpecAttr):
- alloc = bool([x for x in code if 'alloc(' in x])
- if free and not alloc:
- func_name = '__' + func_name
-- ri.cw.write_func('static inline void', func_name, body=code,
-+ ri.cw.write_func('static inline void', func_name, local_vars=local_vars,
-+ body=code,
- args=[f'{type_name(ri, direction, deref=deref)} *{var}'] + self.arg_member(ri))
-
-
-@@ -477,8 +492,7 @@ class TypeString(Type):
- ['unsigned int len;']
-
- def _setter_lines(self, ri, member, presence):
-- return [f"free({member});",
-- f"{presence}_len = strlen({self.c_name});",
-+ return [f"{presence}_len = strlen({self.c_name});",
- f"{member} = malloc({presence}_len + 1);",
- f'memcpy({member}, {self.c_name}, {presence}_len);',
- f'{member}[{presence}_len] = 0;']
-@@ -531,8 +545,7 @@ class TypeBinary(Type):
- ['unsigned int len;']
-
- def _setter_lines(self, ri, member, presence):
-- return [f"free({member});",
-- f"{presence}_len = len;",
-+ return [f"{presence}_len = len;",
- f"{member} = malloc({presence}_len);",
- f'memcpy({member}, {self.c_name}, {presence}_len);']
-
-@@ -569,12 +582,14 @@ class TypeNest(Type):
- def _complex_member_type(self, ri):
- return self.nested_struct_type
-
-- def free(self, ri, var, ref):
-+ def _free_lines(self, ri, var, ref):
-+ lines = []
- at = '&'
- if self.is_recursive_for_op(ri):
- at = ''
-- ri.cw.p(f'if ({var}->{ref}{self.c_name})')
-- ri.cw.p(f'{self.nested_render_name}_free({at}{var}->{ref}{self.c_name});')
-+ lines += [f'if ({var}->{ref}{self.c_name})']
-+ lines += [f'{self.nested_render_name}_free({at}{var}->{ref}{self.c_name});']
-+ return lines
-
- def _attr_typol(self):
- return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
-@@ -627,15 +642,19 @@ class TypeMultiAttr(Type):
- def free_needs_iter(self):
- return 'type' not in self.attr or self.attr['type'] == 'nest'
-
-- def free(self, ri, var, ref):
-+ def _free_lines(self, ri, var, ref):
-+ lines = []
- if self.attr['type'] in scalars:
-- ri.cw.p(f"free({var}->{ref}{self.c_name});")
-+ lines += [f"free({var}->{ref}{self.c_name});"]
- elif 'type' not in self.attr or self.attr['type'] == 'nest':
-- ri.cw.p(f"for (i = 0; i < {var}->{ref}n_{self.c_name}; i++)")
-- ri.cw.p(f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name}[i]);')
-- ri.cw.p(f"free({var}->{ref}{self.c_name});")
-+ lines += [
-+ f"for (i = 0; i < {var}->{ref}n_{self.c_name}; i++)",
-+ f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name}[i]);',
-+ f"free({var}->{ref}{self.c_name});",
-+ ]
- else:
- raise Exception(f"Free of MultiAttr sub-type {self.attr['type']} not supported yet")
-+ return lines
-
- def _attr_policy(self, policy):
- return self.base_type._attr_policy(policy)
-@@ -661,8 +680,7 @@ class TypeMultiAttr(Type):
- def _setter_lines(self, ri, member, presence):
- # For multi-attr we have a count, not presence, hack up the presence
- presence = presence[:-(len('_present.') + len(self.c_name))] + "n_" + self.c_name
-- return [f"free({member});",
-- f"{member} = {self.c_name};",
-+ return [f"{member} = {self.c_name};",
- f"{presence} = n_{self.c_name};"]
-
-
-@@ -747,6 +765,7 @@ class Struct:
- self.request = False
- self.reply = False
- self.recursive = False
-+ self.in_multi_val = False # used by a MultiAttr or and legacy arrays
-
- self.attr_list = []
- self.attrs = dict()
-@@ -1114,6 +1133,10 @@ class Family(SpecFamily):
- if attr in rs_members['reply']:
- self.pure_nested_structs[nested].reply = True
-
-+ if spec.is_multi_val():
-+ child = self.pure_nested_structs.get(nested)
-+ child.in_multi_val = True
-+
- self._sort_pure_types()
-
- # Propagate the request / reply / recursive
-@@ -1128,6 +1151,8 @@ class Family(SpecFamily):
- struct.child_nests.update(child.child_nests)
- child.request |= struct.request
- child.reply |= struct.reply
-+ if spec.is_multi_val():
-+ child.in_multi_val = True
- if attr_set in struct.child_nests:
- struct.recursive = True
-
-@@ -2921,6 +2946,9 @@ def main():
- for attr_set, struct in parsed.pure_nested_structs.items():
- ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
- print_type_full(ri, struct)
-+ if struct.request and struct.in_multi_val:
-+ free_rsp_nested_prototype(ri)
-+ cw.nl()
-
- for op_name, op in parsed.ops.items():
- cw.p(f"/* ============== {op.enum_name} ============== */")
---
-2.39.5
-
+++ /dev/null
-From c136cc6535a7454c9e1a66fc0c934aae20134cbe Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Mon, 14 Apr 2025 14:18:47 -0700
-Subject: tools: ynl-gen: make sure we validate subtype of array-nest
-
-From: Jakub Kicinski <kuba@kernel.org>
-
-[ Upstream commit 57e7dedf2b8c72caa6f04b9e08b19e4f370562fa ]
-
-ArrayNest AKA indexed-array support currently skips inner type
-validation. We count the attributes and then we parse them,
-make sure we call validate, too. Otherwise buggy / unexpected
-kernel response may lead to crashes.
-
-Fixes: be5bea1cc0bf ("net: add basic C code generators for Netlink")
-Reviewed-by: Donald Hunter <donald.hunter@gmail.com>
-Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
-Link: https://patch.msgid.link/20250414211851.602096-5-kuba@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- tools/net/ynl/pyynl/ynl_gen_c.py | 7 +++++--
- 1 file changed, 5 insertions(+), 2 deletions(-)
-
-diff --git a/tools/net/ynl/pyynl/ynl_gen_c.py b/tools/net/ynl/pyynl/ynl_gen_c.py
-index 9c9a62a93afe7..f36af0e1da78e 100755
---- a/tools/net/ynl/pyynl/ynl_gen_c.py
-+++ b/tools/net/ynl/pyynl/ynl_gen_c.py
-@@ -706,8 +706,11 @@ class TypeArrayNest(Type):
- def _attr_get(self, ri, var):
- local_vars = ['const struct nlattr *attr2;']
- get_lines = [f'attr_{self.c_name} = attr;',
-- 'ynl_attr_for_each_nested(attr2, attr)',
-- f'\t{var}->n_{self.c_name}++;']
-+ 'ynl_attr_for_each_nested(attr2, attr) {',
-+ '\tif (ynl_attr_validate(yarg, attr2))',
-+ '\t\treturn YNL_PARSE_CB_ERROR;',
-+ f'\t{var}->n_{self.c_name}++;',
-+ '}']
- return get_lines, None, local_vars
-
-
---
-2.39.5
-
net-ethernet-ti-am65-cpsw-fix-port_np-reference-coun.patch
ata-libata-sata-save-all-fields-from-sense-data-desc.patch
cxgb4-fix-memory-leak-in-cxgb4_init_ethtool_filters-.patch
-tools-ynl-gen-track-attribute-use.patch
-tools-ynl-gen-record-information-about-recursive-nes.patch
-tools-ynl-gen-re-sort-ignoring-recursive-nests.patch
-tools-ynl-gen-store-recursive-nests-by-a-pointer.patch
-tools-ynl-gen-individually-free-previous-values-on-d.patch
netlink-specs-rt-link-add-an-attr-layer-around-alt-i.patch
netlink-specs-rt-link-adjust-mctp-attribute-naming.patch
net-b53-enable-bpdu-reception-for-management-port.patch
+++ /dev/null
-From 5604949695e5b874289ce8143d8a4c1e1fe97374 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Mon, 14 Apr 2025 14:18:46 -0700
-Subject: tools: ynl-gen: individually free previous values on double set
-
-From: Jakub Kicinski <kuba@kernel.org>
-
-[ Upstream commit ce6cb8113c842b94e77364b247c4f85c7b34e0c2 ]
-
-When user calls request_attrA_set() multiple times (for the same
-attribute), and attrA is of type which allocates memory -
-we try to free the previously associated values. For array
-types (including multi-attr) we have only freed the array,
-but the array may have contained pointers.
-
-Refactor the code generation for free attr and reuse the generated
-lines in setters to flush out the previous state. Since setters
-are static inlines in the header we need to add forward declarations
-for the free helpers of pure nested structs. Track which types get
-used by arrays and include the right forwad declarations.
-
-At least ethtool string set and bit set would not be freed without
-this. Tho, admittedly, overriding already set attribute twice is likely
-a very very rare thing to do.
-
-Fixes: be5bea1cc0bf ("net: add basic C code generators for Netlink")
-Reviewed-by: Donald Hunter <donald.hunter@gmail.com>
-Reviewed-by: Jacob Keller <jacob.e.keller@intel.com>
-Link: https://patch.msgid.link/20250414211851.602096-4-kuba@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- tools/net/ynl/ynl-gen-c.py | 62 +++++++++++++++++++++++++++-----------
- 1 file changed, 45 insertions(+), 17 deletions(-)
-
-diff --git a/tools/net/ynl/ynl-gen-c.py b/tools/net/ynl/ynl-gen-c.py
-index 9e37d14d76bf9..53c86d3897e13 100755
---- a/tools/net/ynl/ynl-gen-c.py
-+++ b/tools/net/ynl/ynl-gen-c.py
-@@ -107,9 +107,15 @@ class Type(SpecAttr):
- def free_needs_iter(self):
- return False
-
-- def free(self, ri, var, ref):
-+ def _free_lines(self, ri, var, ref):
- if self.is_multi_val() or self.presence_type() == 'len':
-- ri.cw.p(f'free({var}->{ref}{self.c_name});')
-+ return [f'free({var}->{ref}{self.c_name});']
-+ return []
-+
-+ def free(self, ri, var, ref):
-+ lines = self._free_lines(ri, var, ref)
-+ for line in lines:
-+ ri.cw.p(line)
-
- def arg_member(self, ri):
- member = self._complex_member_type(ri)
-@@ -205,6 +211,10 @@ class Type(SpecAttr):
- var = "req"
- member = f"{var}->{'.'.join(ref)}"
-
-+ local_vars = []
-+ if self.free_needs_iter():
-+ local_vars += ['unsigned int i;']
-+
- code = []
- presence = ''
- for i in range(0, len(ref)):
-@@ -214,6 +224,10 @@ class Type(SpecAttr):
- if i == len(ref) - 1 and self.presence_type() != 'bit':
- continue
- code.append(presence + ' = 1;')
-+ ref_path = '.'.join(ref[:-1])
-+ if ref_path:
-+ ref_path += '.'
-+ code += self._free_lines(ri, var, ref_path)
- code += self._setter_lines(ri, member, presence)
-
- func_name = f"{op_prefix(ri, direction, deref=deref)}_set_{'_'.join(ref)}"
-@@ -221,7 +235,8 @@ class Type(SpecAttr):
- alloc = bool([x for x in code if 'alloc(' in x])
- if free and not alloc:
- func_name = '__' + func_name
-- ri.cw.write_func('static inline void', func_name, body=code,
-+ ri.cw.write_func('static inline void', func_name, local_vars=local_vars,
-+ body=code,
- args=[f'{type_name(ri, direction, deref=deref)} *{var}'] + self.arg_member(ri))
-
-
-@@ -397,8 +412,7 @@ class TypeString(Type):
- ['unsigned int len;']
-
- def _setter_lines(self, ri, member, presence):
-- return [f"free({member});",
-- f"{presence}_len = strlen({self.c_name});",
-+ return [f"{presence}_len = strlen({self.c_name});",
- f"{member} = malloc({presence}_len + 1);",
- f'memcpy({member}, {self.c_name}, {presence}_len);',
- f'{member}[{presence}_len] = 0;']
-@@ -441,8 +455,7 @@ class TypeBinary(Type):
- ['unsigned int len;']
-
- def _setter_lines(self, ri, member, presence):
-- return [f"free({member});",
-- f"{presence}_len = len;",
-+ return [f"{presence}_len = len;",
- f"{member} = malloc({presence}_len);",
- f'memcpy({member}, {self.c_name}, {presence}_len);']
-
-@@ -454,12 +467,14 @@ class TypeNest(Type):
- def _complex_member_type(self, ri):
- return self.nested_struct_type
-
-- def free(self, ri, var, ref):
-+ def _free_lines(self, ri, var, ref):
-+ lines = []
- at = '&'
- if self.is_recursive_for_op(ri):
- at = ''
-- ri.cw.p(f'if ({var}->{ref}{self.c_name})')
-- ri.cw.p(f'{self.nested_render_name}_free({at}{var}->{ref}{self.c_name});')
-+ lines += [f'if ({var}->{ref}{self.c_name})']
-+ lines += [f'{self.nested_render_name}_free({at}{var}->{ref}{self.c_name});']
-+ return lines
-
- def _attr_typol(self):
- return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
-@@ -519,15 +534,19 @@ class TypeMultiAttr(Type):
- def free_needs_iter(self):
- return 'type' not in self.attr or self.attr['type'] == 'nest'
-
-- def free(self, ri, var, ref):
-+ def _free_lines(self, ri, var, ref):
-+ lines = []
- if self.attr['type'] in scalars:
-- ri.cw.p(f"free({var}->{ref}{self.c_name});")
-+ lines += [f"free({var}->{ref}{self.c_name});"]
- elif 'type' not in self.attr or self.attr['type'] == 'nest':
-- ri.cw.p(f"for (i = 0; i < {var}->{ref}n_{self.c_name}; i++)")
-- ri.cw.p(f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name}[i]);')
-- ri.cw.p(f"free({var}->{ref}{self.c_name});")
-+ lines += [
-+ f"for (i = 0; i < {var}->{ref}n_{self.c_name}; i++)",
-+ f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name}[i]);',
-+ f"free({var}->{ref}{self.c_name});",
-+ ]
- else:
- raise Exception(f"Free of MultiAttr sub-type {self.attr['type']} not supported yet")
-+ return lines
-
- def _attr_policy(self, policy):
- return self.base_type._attr_policy(policy)
-@@ -553,8 +572,7 @@ class TypeMultiAttr(Type):
- def _setter_lines(self, ri, member, presence):
- # For multi-attr we have a count, not presence, hack up the presence
- presence = presence[:-(len('_present.') + len(self.c_name))] + "n_" + self.c_name
-- return [f"free({member});",
-- f"{member} = {self.c_name};",
-+ return [f"{member} = {self.c_name};",
- f"{presence} = n_{self.c_name};"]
-
-
-@@ -639,6 +657,7 @@ class Struct:
- self.request = False
- self.reply = False
- self.recursive = False
-+ self.in_multi_val = False # used by a MultiAttr or and legacy arrays
-
- self.attr_list = []
- self.attrs = dict()
-@@ -987,6 +1006,10 @@ class Family(SpecFamily):
- if attr in rs_members['reply']:
- self.pure_nested_structs[nested].reply = True
-
-+ if spec.is_multi_val():
-+ child = self.pure_nested_structs.get(nested)
-+ child.in_multi_val = True
-+
- self._sort_pure_types()
-
- # Propagate the request / reply / recursive
-@@ -1001,6 +1024,8 @@ class Family(SpecFamily):
- struct.child_nests.update(child.child_nests)
- child.request |= struct.request
- child.reply |= struct.reply
-+ if spec.is_multi_val():
-+ child.in_multi_val = True
- if attr_set in struct.child_nests:
- struct.recursive = True
-
-@@ -2552,6 +2577,9 @@ def main():
- for attr_set, struct in parsed.pure_nested_structs.items():
- ri = RenderInfo(cw, parsed, args.mode, "", "", attr_set)
- print_type_full(ri, struct)
-+ if struct.request and struct.in_multi_val:
-+ free_rsp_nested_prototype(ri)
-+ cw.nl()
-
- for op_name, op in parsed.ops.items():
- cw.p(f"/* ============== {op.enum_name} ============== */")
---
-2.39.5
-
+++ /dev/null
-From 81bea2ea7c3b5153dd4f58b111ee44f9bca2cb46 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Wed, 13 Dec 2023 15:14:30 -0800
-Subject: tools: ynl-gen: re-sort ignoring recursive nests
-
-From: Jakub Kicinski <kuba@kernel.org>
-
-[ Upstream commit aa75783b95a1e7fc09129f5364476e6effe47392 ]
-
-We try to keep the structures and helpers "topologically sorted",
-to avoid forward declarations. When recursive nests are at play
-we need to sort twice, because structs which end up being marked
-as recursive will get a full set of forward declarations, so we
-should ignore them for the purpose of sorting.
-
-Reviewed-by: Donald Hunter <donald.hunter@gmail.com>
-Link: https://lore.kernel.org/r/20231213231432.2944749-7-kuba@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Stable-dep-of: ce6cb8113c84 ("tools: ynl-gen: individually free previous values on double set")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- tools/net/ynl/ynl-gen-c.py | 52 +++++++++++++++++++++++---------------
- 1 file changed, 31 insertions(+), 21 deletions(-)
-
-diff --git a/tools/net/ynl/ynl-gen-c.py b/tools/net/ynl/ynl-gen-c.py
-index e5a3e0bb5a39d..502d03b8a758a 100755
---- a/tools/net/ynl/ynl-gen-c.py
-+++ b/tools/net/ynl/ynl-gen-c.py
-@@ -909,6 +909,33 @@ class Family(SpecFamily):
- self.root_sets[op['attribute-set']]['request'].update(req_attrs)
- self.root_sets[op['attribute-set']]['reply'].update(rsp_attrs)
-
-+ def _sort_pure_types(self):
-+ # Try to reorder according to dependencies
-+ pns_key_list = list(self.pure_nested_structs.keys())
-+ pns_key_seen = set()
-+ rounds = len(pns_key_list) ** 2 # it's basically bubble sort
-+ for _ in range(rounds):
-+ if len(pns_key_list) == 0:
-+ break
-+ name = pns_key_list.pop(0)
-+ finished = True
-+ for _, spec in self.attr_sets[name].items():
-+ if 'nested-attributes' in spec:
-+ nested = spec['nested-attributes']
-+ # If the unknown nest we hit is recursive it's fine, it'll be a pointer
-+ if self.pure_nested_structs[nested].recursive:
-+ continue
-+ if nested not in pns_key_seen:
-+ # Dicts are sorted, this will make struct last
-+ struct = self.pure_nested_structs.pop(name)
-+ self.pure_nested_structs[name] = struct
-+ finished = False
-+ break
-+ if finished:
-+ pns_key_seen.add(name)
-+ else:
-+ pns_key_list.append(name)
-+
- def _load_nested_sets(self):
- attr_set_queue = list(self.root_sets.keys())
- attr_set_seen = set(self.root_sets.keys())
-@@ -948,27 +975,8 @@ class Family(SpecFamily):
- if attr in rs_members['reply']:
- self.pure_nested_structs[nested].reply = True
-
-- # Try to reorder according to dependencies
-- pns_key_list = list(self.pure_nested_structs.keys())
-- pns_key_seen = set()
-- rounds = len(pns_key_list)**2 # it's basically bubble sort
-- for _ in range(rounds):
-- if len(pns_key_list) == 0:
-- break
-- name = pns_key_list.pop(0)
-- finished = True
-- for _, spec in self.attr_sets[name].items():
-- if 'nested-attributes' in spec:
-- if spec['nested-attributes'] not in pns_key_seen:
-- # Dicts are sorted, this will make struct last
-- struct = self.pure_nested_structs.pop(name)
-- self.pure_nested_structs[name] = struct
-- finished = False
-- break
-- if finished:
-- pns_key_seen.add(name)
-- else:
-- pns_key_list.append(name)
-+ self._sort_pure_types()
-+
- # Propagate the request / reply / recursive
- for attr_set, struct in reversed(self.pure_nested_structs.items()):
- for _, spec in self.attr_sets[attr_set].items():
-@@ -984,6 +992,8 @@ class Family(SpecFamily):
- if attr_set in struct.child_nests:
- struct.recursive = True
-
-+ self._sort_pure_types()
-+
- def _load_attr_use(self):
- for _, struct in self.pure_nested_structs.items():
- if struct.request:
---
-2.39.5
-
+++ /dev/null
-From e81c87860379f30670ad0505ebad1fcc30330348 Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Wed, 13 Dec 2023 15:14:29 -0800
-Subject: tools: ynl-gen: record information about recursive nests
-
-From: Jakub Kicinski <kuba@kernel.org>
-
-[ Upstream commit 38329fcfb757b8215c07a77b6657721cc7e9530e ]
-
-Track which nests are recursive. Non-recursive nesting gets
-rendered in C as directly nested structs. For recursive
-ones we need to put a pointer in, rather than full struct.
-
-Track this information, no change to generated code, yet.
-
-Reviewed-by: Donald Hunter <donald.hunter@gmail.com>
-Link: https://lore.kernel.org/r/20231213231432.2944749-6-kuba@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Stable-dep-of: ce6cb8113c84 ("tools: ynl-gen: individually free previous values on double set")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- tools/net/ynl/ynl-gen-c.py | 19 +++++++++++++++++--
- 1 file changed, 17 insertions(+), 2 deletions(-)
-
-diff --git a/tools/net/ynl/ynl-gen-c.py b/tools/net/ynl/ynl-gen-c.py
-index 4c86fe0cd1179..e5a3e0bb5a39d 100755
---- a/tools/net/ynl/ynl-gen-c.py
-+++ b/tools/net/ynl/ynl-gen-c.py
-@@ -80,6 +80,9 @@ class Type(SpecAttr):
- def is_scalar(self):
- return self.type in {'u8', 'u16', 'u32', 'u64', 's32', 's64'}
-
-+ def is_recursive(self):
-+ return False
-+
- def presence_type(self):
- return 'bit'
-
-@@ -440,6 +443,9 @@ class TypeBinary(Type):
-
-
- class TypeNest(Type):
-+ def is_recursive(self):
-+ return self.family.pure_nested_structs[self.nested_attrs].recursive
-+
- def _complex_member_type(self, ri):
- return self.nested_struct_type
-
-@@ -615,9 +621,12 @@ class Struct:
- if self.nested and space_name in family.consts:
- self.struct_name += '_'
- self.ptr_name = self.struct_name + ' *'
-+ # All attr sets this one contains, directly or multiple levels down
-+ self.child_nests = set()
-
- self.request = False
- self.reply = False
-+ self.recursive = False
-
- self.attr_list = []
- self.attrs = dict()
-@@ -960,14 +969,20 @@ class Family(SpecFamily):
- pns_key_seen.add(name)
- else:
- pns_key_list.append(name)
-- # Propagate the request / reply
-+ # Propagate the request / reply / recursive
- for attr_set, struct in reversed(self.pure_nested_structs.items()):
- for _, spec in self.attr_sets[attr_set].items():
- if 'nested-attributes' in spec:
-- child = self.pure_nested_structs.get(spec['nested-attributes'])
-+ child_name = spec['nested-attributes']
-+ struct.child_nests.add(child_name)
-+ child = self.pure_nested_structs.get(child_name)
- if child:
-+ if not child.recursive:
-+ struct.child_nests.update(child.child_nests)
- child.request |= struct.request
- child.reply |= struct.reply
-+ if attr_set in struct.child_nests:
-+ struct.recursive = True
-
- def _load_attr_use(self):
- for _, struct in self.pure_nested_structs.items():
---
-2.39.5
-
+++ /dev/null
-From 0558ef104f9aecdb8be03b564438e99cad3b597e Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Wed, 13 Dec 2023 15:14:31 -0800
-Subject: tools: ynl-gen: store recursive nests by a pointer
-
-From: Jakub Kicinski <kuba@kernel.org>
-
-[ Upstream commit 461f25a2e4334767d3e306b08dbda054da1aaa30 ]
-
-To avoid infinite nesting store recursive structs by pointer.
-If recursive struct is placed in the op directly - the first
-instance can be stored by value. That makes the code much
-less of a pain for majority of practical uses.
-
-Reviewed-by: Donald Hunter <donald.hunter@gmail.com>
-Link: https://lore.kernel.org/r/20231213231432.2944749-8-kuba@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Stable-dep-of: ce6cb8113c84 ("tools: ynl-gen: individually free previous values on double set")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- tools/net/ynl/ynl-gen-c.py | 16 ++++++++++++++--
- 1 file changed, 14 insertions(+), 2 deletions(-)
-
-diff --git a/tools/net/ynl/ynl-gen-c.py b/tools/net/ynl/ynl-gen-c.py
-index 502d03b8a758a..9e37d14d76bf9 100755
---- a/tools/net/ynl/ynl-gen-c.py
-+++ b/tools/net/ynl/ynl-gen-c.py
-@@ -83,6 +83,9 @@ class Type(SpecAttr):
- def is_recursive(self):
- return False
-
-+ def is_recursive_for_op(self, ri):
-+ return self.is_recursive() and not ri.op
-+
- def presence_type(self):
- return 'bit'
-
-@@ -123,6 +126,8 @@ class Type(SpecAttr):
- member = self._complex_member_type(ri)
- if member:
- ptr = '*' if self.is_multi_val() else ''
-+ if self.is_recursive_for_op(ri):
-+ ptr = '*'
- ri.cw.p(f"{member} {ptr}{self.c_name};")
- return
- members = self.arg_member(ri)
-@@ -450,7 +455,11 @@ class TypeNest(Type):
- return self.nested_struct_type
-
- def free(self, ri, var, ref):
-- ri.cw.p(f'{self.nested_render_name}_free(&{var}->{ref}{self.c_name});')
-+ at = '&'
-+ if self.is_recursive_for_op(ri):
-+ at = ''
-+ ri.cw.p(f'if ({var}->{ref}{self.c_name})')
-+ ri.cw.p(f'{self.nested_render_name}_free({at}{var}->{ref}{self.c_name});')
-
- def _attr_typol(self):
- return f'.type = YNL_PT_NEST, .nest = &{self.nested_render_name}_nest, '
-@@ -459,8 +468,9 @@ class TypeNest(Type):
- return 'NLA_POLICY_NESTED(' + self.nested_render_name + '_nl_policy)'
-
- def attr_put(self, ri, var):
-+ at = '' if self.is_recursive_for_op(ri) else '&'
- self._attr_put_line(ri, var, f"{self.nested_render_name}_put(nlh, " +
-- f"{self.enum_name}, &{var}->{self.c_name})")
-+ f"{self.enum_name}, {at}{var}->{self.c_name})")
-
- def _attr_get(self, ri, var):
- get_lines = [f"if ({self.nested_render_name}_parse(&parg, attr))",
-@@ -473,6 +483,8 @@ class TypeNest(Type):
- ref = (ref if ref else []) + [self.c_name]
-
- for _, attr in ri.family.pure_nested_structs[self.nested_attrs].member_list():
-+ if attr.is_recursive():
-+ continue
- attr.setter(ri, self.nested_attrs, direction, deref=deref, ref=ref)
-
-
---
-2.39.5
-
+++ /dev/null
-From 178fdfff69b384c6f6ee2119bdfc4f3b945c162c Mon Sep 17 00:00:00 2001
-From: Sasha Levin <sashal@kernel.org>
-Date: Wed, 18 Oct 2023 09:39:15 -0700
-Subject: tools: ynl-gen: track attribute use
-
-From: Jakub Kicinski <kuba@kernel.org>
-
-[ Upstream commit ee0a4cfcbdcc6a7b2b35dba475e68187ebdafbf1 ]
-
-For range validation we'll need to know if any individual
-attribute is used on input (i.e. whether we will generate
-a policy for it). Track this information.
-
-Link: https://lore.kernel.org/r/20231018163917.2514503-2-kuba@kernel.org
-Signed-off-by: Jakub Kicinski <kuba@kernel.org>
-Stable-dep-of: ce6cb8113c84 ("tools: ynl-gen: individually free previous values on double set")
-Signed-off-by: Sasha Levin <sashal@kernel.org>
----
- tools/net/ynl/ynl-gen-c.py | 20 ++++++++++++++++++++
- 1 file changed, 20 insertions(+)
-
-diff --git a/tools/net/ynl/ynl-gen-c.py b/tools/net/ynl/ynl-gen-c.py
-index 575b7e248e521..4c86fe0cd1179 100755
---- a/tools/net/ynl/ynl-gen-c.py
-+++ b/tools/net/ynl/ynl-gen-c.py
-@@ -42,6 +42,9 @@ class Type(SpecAttr):
- self.type = attr['type']
- self.checks = attr.get('checks', {})
-
-+ self.request = False
-+ self.reply = False
-+
- if 'len' in attr:
- self.len = attr['len']
- if 'nested-attributes' in attr:
-@@ -845,6 +848,7 @@ class Family(SpecFamily):
-
- self._load_root_sets()
- self._load_nested_sets()
-+ self._load_attr_use()
- self._load_hooks()
-
- self.kernel_policy = self.yaml.get('kernel-policy', 'split')
-@@ -965,6 +969,22 @@ class Family(SpecFamily):
- child.request |= struct.request
- child.reply |= struct.reply
-
-+ def _load_attr_use(self):
-+ for _, struct in self.pure_nested_structs.items():
-+ if struct.request:
-+ for _, arg in struct.member_list():
-+ arg.request = True
-+ if struct.reply:
-+ for _, arg in struct.member_list():
-+ arg.reply = True
-+
-+ for root_set, rs_members in self.root_sets.items():
-+ for attr, spec in self.attr_sets[root_set].items():
-+ if attr in rs_members['request']:
-+ spec.request = True
-+ if attr in rs_members['reply']:
-+ spec.reply = True
-+
- def _load_global_policy(self):
- global_set = set()
- attr_set_name = None
---
-2.39.5
-