From 27ad33b6b349c8c76fdef3bf0f707158ce7c275e Mon Sep 17 00:00:00 2001 From: "Matthew Wilcox (Oracle)" Date: Fri, 6 Jun 2025 15:15:42 +0100 Subject: kernel-doc: Fix symbol matching for dropped suffixes The support for dropping "_noprof" missed dropping the suffix from exported symbols. That meant that using the :export: feature would look for kernel-doc for (eg) krealloc_noprof() and not find the kernel-doc for krealloc(). Fixes: 51a7bf0238c2 (scripts/kernel-doc: drop "_noprof" on function prototypes) Signed-off-by: Matthew Wilcox (Oracle) Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250606141543.1285671-1-willy@infradead.org --- scripts/lib/kdoc/kdoc_parser.py | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 062453eefc7a..2c6143f7ca0f 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1171,16 +1171,24 @@ class KernelDoc: with a staticmethod decorator. """ + # We support documenting some exported symbols with different + # names. A horrible hack. + suffixes = [ '_noprof' ] + # Note: it accepts only one EXPORT_SYMBOL* per line, as having # multiple export lines would violate Kernel coding style. if export_symbol.search(line): symbol = export_symbol.group(2) + for suffix in suffixes: + symbol = symbol.removesuffix(suffix) function_set.add(symbol) return if export_symbol_ns.search(line): symbol = export_symbol_ns.group(2) + for suffix in suffixes: + symbol = symbol.removesuffix(suffix) function_set.add(symbol) def process_normal(self, ln, line): -- cgit v1.2.3 From e8f0303e8b8dce911536963c89eaf0a5ccb62d6a Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 6 Jun 2025 10:34:30 -0600 Subject: docs: kdoc: simplify the PROTO continuation logic Remove the unneeded "cont" variable and tighten up the code slightly. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250606163438.229916-2-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 2c6143f7ca0f..899d5446f95c 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1688,7 +1688,6 @@ class KernelDoc: Besides parsing kernel-doc tags, it also parses export symbols. """ - cont = False prev = "" prev_ln = None export_table = set() @@ -1704,18 +1703,14 @@ class KernelDoc: if self.state == state.PROTO: if line.endswith("\\"): prev += line.rstrip("\\") - cont = True - if not prev_ln: prev_ln = ln - continue - if cont: + if prev: ln = prev_ln line = prev + line prev = "" - cont = False prev_ln = None self.config.log.debug("%d %s%s: %s", -- cgit v1.2.3 From cef8c781ca71ddd0777d639775e66f8630359342 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 6 Jun 2025 10:34:31 -0600 Subject: docs: kdoc: move the core dispatch into a state table Since all of the handlers already nicely have the same prototype, put them into a table and call them from there and take out the extended if-then-else series. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250606163438.229916-3-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 899d5446f95c..1a6c6865b2c5 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1678,6 +1678,21 @@ class KernelDoc: return export_table + # + # The state/action table telling us which function to invoke in + # each state. + # + state_actions = { + state.NORMAL: process_normal, + state.NAME: process_name, + state.BODY: process_body, + state.BODY_MAYBE: process_body, + state.BODY_WITH_BLANK_LINE: process_body, + state.INLINE: process_inline, + state.PROTO: process_proto, + state.DOCBLOCK: process_docblock, + } + def parse_kdoc(self): """ Open and process each line of a C source file. @@ -1729,19 +1744,8 @@ class KernelDoc: self.process_export(export_table, line) # Hand this line to the appropriate state handler - if self.state == state.NORMAL: - self.process_normal(ln, line) - elif self.state == state.NAME: - self.process_name(ln, line) - elif self.state in [state.BODY, state.BODY_MAYBE, - state.BODY_WITH_BLANK_LINE]: - self.process_body(ln, line) - elif self.state == state.INLINE: # scanning for inline parameters - self.process_inline(ln, line) - elif self.state == state.PROTO: - self.process_proto(ln, line) - elif self.state == state.DOCBLOCK: - self.process_docblock(ln, line) + self.state_actions[self.state](self, ln, line) + except OSError: self.config.log.error(f"Error: Cannot open file {self.fname}") -- cgit v1.2.3 From 42592bd46dded5fab5af1d5e04c9b17cbb4bca6d Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 6 Jun 2025 10:34:32 -0600 Subject: docs: kdoc: remove the section_intro variable It is only used in one place, so just put the constant string "Introduction" there so people don't have to go looking for it. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250606163438.229916-4-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 1a6c6865b2c5..f8871f6a2638 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -203,7 +203,6 @@ class KernelDoc: # Section names - section_intro = "Introduction" section_context = "Context" section_return = "Return" @@ -1215,7 +1214,7 @@ class KernelDoc: self.entry.new_start_line = ln if not doc_block.group(1): - self.entry.section = self.section_intro + self.entry.section = "Introduction" else: self.entry.section = doc_block.group(1) -- cgit v1.2.3 From e76a1d2b2623e9f10e2ffd295ae2615bf3228561 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 6 Jun 2025 10:34:33 -0600 Subject: docs: kdoc: simplify the kerneldoc recognition code process_name() looks for the first line of a kerneldoc comment. It contains two nearly identical regular expressions, the second of which only catches six cases in the kernel, all of the form: define SOME_MACRO_NAME - description Simply put the "define" into the regex and discard it, eliminating the loop and the code to remove it specially. Note that this still treats these defines as if they were functions, but that's a separate issue. There is no change in the generated output. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250606163438.229916-5-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index f8871f6a2638..72919a5d71b2 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1238,26 +1238,18 @@ class KernelDoc: # Test for data declaration r = KernRe(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)") + r2 = KernRe(fr"^{decl_start}{fn_type}(?:define\s+)?(\w+)\s*{parenthesis}\s*{decl_end}?$") if r.search(line): self.entry.decl_type = r.group(1) self.entry.identifier = r.group(2) self.entry.is_kernel_comment = True - else: - # Look for foo() or static void foo() - description; - # or misspelt identifier - - r1 = KernRe(fr"^{decl_start}{fn_type}(\w+)\s*{parenthesis}\s*{decl_end}?$") - r2 = KernRe(fr"^{decl_start}{fn_type}(\w+[^-:]*){parenthesis}\s*{decl_end}$") - - for r in [r1, r2]: - if r.search(line): - self.entry.identifier = r.group(1) - self.entry.decl_type = "function" - - r = KernRe(r"define\s+") - self.entry.identifier = r.sub("", self.entry.identifier) - self.entry.is_kernel_comment = True - break + # + # Look for a function description + # + elif r2.search(line): + self.entry.identifier = r2.group(1) + self.entry.decl_type = "function" + self.entry.is_kernel_comment = True self.entry.identifier = self.entry.identifier.strip(" ") -- cgit v1.2.3 From 8f4650fe1a74e68f5c6715413a5a26aa1564780d Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 6 Jun 2025 10:34:34 -0600 Subject: docs: kdoc: remove the KernelEntry::is_kernel_comment member entry::is_kernel_comment never had anything to do with the entry itself; it is a bit of local state in one branch of process_name(). It can, in fact, be removed entirely; rework the code slightly so that it is no longer needed. No change in the rendered output. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250606163438.229916-6-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 72919a5d71b2..dffa3055adc1 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1224,7 +1224,6 @@ class KernelDoc: if doc_decl.search(line): self.entry.identifier = doc_decl.group(1) - self.entry.is_kernel_comment = False decl_start = str(doc_com) # comment block asterisk fn_type = r"(?:\w+\s*\*\s*)?" # type (for non-functions) @@ -1242,14 +1241,20 @@ class KernelDoc: if r.search(line): self.entry.decl_type = r.group(1) self.entry.identifier = r.group(2) - self.entry.is_kernel_comment = True # # Look for a function description # elif r2.search(line): self.entry.identifier = r2.group(1) self.entry.decl_type = "function" - self.entry.is_kernel_comment = True + # + # We struck out. + # + else: + self.emit_msg(ln, + f"This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n{line}") + self.state = state.NORMAL + return self.entry.identifier = self.entry.identifier.strip(" ") @@ -1271,11 +1276,6 @@ class KernelDoc: else: self.entry.declaration_purpose = "" - if not self.entry.is_kernel_comment: - self.emit_msg(ln, - f"This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n{line}") - self.state = state.NORMAL - if not self.entry.declaration_purpose and self.config.wshort_desc: self.emit_msg(ln, f"missing initial short description on line:\n{line}") -- cgit v1.2.3 From f9b4cf2e8518387d4c512d934137dc6968759ec4 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 6 Jun 2025 10:34:35 -0600 Subject: docs: kdoc: remove the KernelEntry::descr pseudo member The entry.descr value used in process_name() is not actually a member of the KernelEntry class; it is a bit of local state. So just manage it locally. A trim_whitespace() helper was added to clean up the code slightly. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250606163438.229916-7-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index dffa3055adc1..2d8a046499c7 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -60,6 +60,13 @@ export_symbol_ns = KernRe(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+" type_param = KernRe(r"\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False) +# +# A little helper to get rid of excess white space +# +multi_space = KernRe(r'\s\s+') +def trim_whitespace(s): + return multi_space.sub(' ', s.strip()) + class state: """ State machine enums @@ -1266,12 +1273,7 @@ class KernelDoc: r = KernRe("[-:](.*)") if r.search(line): - # strip leading/trailing/multiple spaces - self.entry.descr = r.group(1).strip(" ") - - r = KernRe(r"\s+") - self.entry.descr = r.sub(" ", self.entry.descr) - self.entry.declaration_purpose = self.entry.descr + self.entry.declaration_purpose = trim_whitespace(r.group(1)) self.state = state.BODY_MAYBE else: self.entry.declaration_purpose = "" -- cgit v1.2.3 From b23c71080b6cb0c12d4962321e5266814f980da1 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 6 Jun 2025 10:34:36 -0600 Subject: docs: kdoc: remove some ineffective code The code testing for a pointer declaration in process_name() has no actual effect on subsequent actions; remove it. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250606163438.229916-8-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 5 ----- 1 file changed, 5 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 2d8a046499c7..575817387a32 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1237,11 +1237,6 @@ class KernelDoc: parenthesis = r"(?:\(\w*\))?" # optional parenthesis on function decl_end = r"(?:[-:].*)" # end of the name part - # test for pointer declaration type, foo * bar() - desc - r = KernRe(fr"^{decl_start}([\w\s]+?){parenthesis}?\s*{decl_end}?$") - if r.search(line): - self.entry.identifier = r.group(1) - # Test for data declaration r = KernRe(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)") r2 = KernRe(fr"^{decl_start}{fn_type}(?:define\s+)?(\w+)\s*{parenthesis}\s*{decl_end}?$") -- cgit v1.2.3 From 0682bde2c7f44320c621b765f31a0cf24e01b23f Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 6 Jun 2025 10:34:37 -0600 Subject: docs: kdoc: move the declaration regexes out of process_name() Move two complex regexes up with the other patterns, decluttering this function and allowing the compilation to be done once rather than for every kerneldoc comment. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250606163438.229916-9-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 575817387a32..d814e48f9f38 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -47,7 +47,6 @@ doc_sect = doc_com + \ flags=re.I, cache=False) doc_content = doc_com_body + KernRe(r'(.*)', cache=False) -doc_block = doc_com + KernRe(r'DOC:\s*(.*)?', cache=False) doc_inline_start = KernRe(r'^\s*/\*\*\s*$', cache=False) doc_inline_sect = KernRe(r'\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)', cache=False) doc_inline_end = KernRe(r'^\s*\*/\s*$', cache=False) @@ -60,6 +59,18 @@ export_symbol_ns = KernRe(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+" type_param = KernRe(r"\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False) +# +# Tests for the beginning of a kerneldoc block in its various forms. +# +doc_block = doc_com + KernRe(r'DOC:\s*(.*)?', cache=False) +doc_begin_data = KernRe(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)", cache = False) +doc_begin_func = KernRe(str(doc_com) + # initial " * ' + r"(?:\w+\s*\*\s*)?" + # type (not captured) + r'(?:define\s+)?' + # possible "define" (not captured) + r'(\w+)\s*(?:\(\w*\))?\s*' + # name and optional "(...)" + r'(?:[-:].*)?$', # description (not captured) + cache = False) + # # A little helper to get rid of excess white space # @@ -1232,22 +1243,15 @@ class KernelDoc: if doc_decl.search(line): self.entry.identifier = doc_decl.group(1) - decl_start = str(doc_com) # comment block asterisk - fn_type = r"(?:\w+\s*\*\s*)?" # type (for non-functions) - parenthesis = r"(?:\(\w*\))?" # optional parenthesis on function - decl_end = r"(?:[-:].*)" # end of the name part - # Test for data declaration - r = KernRe(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)") - r2 = KernRe(fr"^{decl_start}{fn_type}(?:define\s+)?(\w+)\s*{parenthesis}\s*{decl_end}?$") - if r.search(line): - self.entry.decl_type = r.group(1) - self.entry.identifier = r.group(2) + if doc_begin_data.search(line): + self.entry.decl_type = doc_begin_data.group(1) + self.entry.identifier = doc_begin_data.group(2) # # Look for a function description # - elif r2.search(line): - self.entry.identifier = r2.group(1) + elif doc_begin_func.search(line): + self.entry.identifier = doc_begin_func.group(1) self.entry.decl_type = "function" # # We struck out. -- cgit v1.2.3 From 8666a352dc1738f6302382d9d64611a44978d369 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 6 Jun 2025 10:34:38 -0600 Subject: docs: kdoc: some final touches for process_name() Add some comments to process_name() to cover its broad phases of operation, and slightly restructure the if/then/else structure to remove some early returns. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250606163438.229916-10-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 33 ++++++++++++++++++++------------- 1 file changed, 20 insertions(+), 13 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index d814e48f9f38..42b2e0936b72 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1227,7 +1227,9 @@ class KernelDoc: """ STATE_NAME: Looking for the "name - description" line """ - + # + # Check for a DOC: block and handle them specially. + # if doc_block.search(line): self.entry.new_start_line = ln @@ -1238,9 +1240,10 @@ class KernelDoc: self.entry.identifier = self.entry.section self.state = state.DOCBLOCK - return - - if doc_decl.search(line): + # + # Otherwise we're looking for a normal kerneldoc declaration line. + # + elif doc_decl.search(line): self.entry.identifier = doc_decl.group(1) # Test for data declaration @@ -1261,15 +1264,19 @@ class KernelDoc: f"This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n{line}") self.state = state.NORMAL return - - self.entry.identifier = self.entry.identifier.strip(" ") - + # + # OK, set up for a new kerneldoc entry. + # self.state = state.BODY - + self.entry.identifier = self.entry.identifier.strip(" ") # if there's no @param blocks need to set up default section here self.entry.section = SECTION_DEFAULT self.entry.new_start_line = ln + 1 - + # + # Find the description portion, which *should* be there but + # isn't always. + # (We should be able to capture this from the previous parsing - someday) + # r = KernRe("[-:](.*)") if r.search(line): self.entry.declaration_purpose = trim_whitespace(r.group(1)) @@ -1290,11 +1297,11 @@ class KernelDoc: self.emit_msg(ln, f"Scanning doc for {self.entry.decl_type} {self.entry.identifier}", warning=False) - - return - + # # Failed to find an identifier. Emit a warning - self.emit_msg(ln, f"Cannot find identifier on line:\n{line}") + # + else: + self.emit_msg(ln, f"Cannot find identifier on line:\n{line}") def process_body(self, ln, line): """ -- cgit v1.2.3 From b143745249c00fc1807a35bdac4a7cfe01d3e351 Mon Sep 17 00:00:00 2001 From: Collin Funk Date: Sat, 7 Jun 2025 20:12:39 -0700 Subject: ver_linux: Remove checks for reiserfsprogs. The reiserfsprogs package is no longer needed since ReiserFS was removed in Linux 6.13. Signed-off-by: Collin Funk Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/4d9808b5e3a87eab41d5d0417d453800faad98b1.1749352106.git.collin.funk1@gmail.com --- scripts/ver_linux | 2 -- 1 file changed, 2 deletions(-) (limited to 'scripts') diff --git a/scripts/ver_linux b/scripts/ver_linux index 1a8ee4ff0e32..d6f2362d3792 100755 --- a/scripts/ver_linux +++ b/scripts/ver_linux @@ -25,8 +25,6 @@ BEGIN { printversion("Module-init-tools", version("depmod -V")) printversion("E2fsprogs", version("tune2fs")) printversion("Jfsutils", version("fsck.jfs -V")) - printversion("Reiserfsprogs", version("reiserfsck -V")) - printversion("Reiser4fsprogs", version("fsck.reiser4 -V")) printversion("Xfsprogs", version("xfs_db -V")) printversion("Pcmciautils", version("pccardctl -V")) printversion("Pcmcia-cs", version("cardmgr -V")) -- cgit v1.2.3 From 2841ef8d9630fb9735bd219f15f33cd1c70cb0d1 Mon Sep 17 00:00:00 2001 From: Thomas Weißschuh Date: Fri, 30 May 2025 05:54:35 +0200 Subject: const_structs.checkpatch: add bin_attribute MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now that the sysfs core can handle "const struct bin_attribute", make sure that new usages of the struct already enter the tree as const. Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20250530-sysfs-const-bin_attr-final-v3-1-724bfcf05b99@weissschuh.net Signed-off-by: Greg Kroah-Hartman --- scripts/const_structs.checkpatch | 1 + 1 file changed, 1 insertion(+) (limited to 'scripts') diff --git a/scripts/const_structs.checkpatch b/scripts/const_structs.checkpatch index e8609a03c3d8..6eb94fddc338 100644 --- a/scripts/const_structs.checkpatch +++ b/scripts/const_structs.checkpatch @@ -1,6 +1,7 @@ acpi_dock_ops address_space_operations backlight_ops +bin_attribute block_device_operations bus_type clk_ops -- cgit v1.2.3 From 0a1eab129fedb4281e65c845b04be02b53c99f9c Mon Sep 17 00:00:00 2001 From: Thomas Meyer Date: Tue, 24 Sep 2024 20:47:22 +0200 Subject: kconfig: use memcmp instead of deprecated bcmp bcmp() was removed in POSIX.1-2008. This commit replaces bcmp() with memcmp(). This allows Kconfig to link with C libraries that do not provide bcmp(), such as Android bionic libc. Signed-off-by: Thomas Meyer Reviewed-by: Miguel Ojeda Reported-by: Abhigyan Ghosh [masahiro: update commit description] Signed-off-by: Masahiro Yamada --- scripts/kconfig/confdata.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index ac95661a1c9d..9599a0408862 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c @@ -77,7 +77,7 @@ static bool is_same(const char *file1, const char *file2) if (map2 == MAP_FAILED) goto close2; - if (bcmp(map1, map2, st1.st_size)) + if (memcmp(map1, map2, st1.st_size)) goto close2; ret = true; -- cgit v1.2.3 From 694174f94ebeeb5ec5cc0e9de9b40c82057e1d95 Mon Sep 17 00:00:00 2001 From: "Yann E. MORIN" Date: Thu, 14 Nov 2013 00:53:32 +0100 Subject: kconfig: lxdialog: fix 'space' to (de)select options In case a menu has comment without letters/numbers (eg. characters matching the regexp '^[^[:alpha:][:digit:]]+$', for example - or *), hitting space will cycle through those comments, rather than selecting/deselecting the currently-highlighted option. This is the behaviour of hitting any letter/digit: jump to the next option which prompt starts with that letter. The only letters that do not behave as such are 'y' 'm' and 'n'. Prompts that start with one of those three letters are instead matched on the first letter that is not 'y', 'm' or 'n'. Fix that by treating 'space' as we treat y/m/n, ie. as an action key, not as shortcut to jump to prompt. Signed-off-by: Yann E. MORIN Signed-off-by: Peter Korsgaard Signed-off-by: Cherniaev Andrei [masahiro: took from Buildroot, adjusted the commit subject] Signed-off-by: Masahiro Yamada --- scripts/kconfig/lxdialog/menubox.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/lxdialog/menubox.c b/scripts/kconfig/lxdialog/menubox.c index 6e6244df0c56..d4c19b7beebb 100644 --- a/scripts/kconfig/lxdialog/menubox.c +++ b/scripts/kconfig/lxdialog/menubox.c @@ -264,7 +264,7 @@ do_resize: if (key < 256 && isalpha(key)) key = tolower(key); - if (strchr("ynmh", key)) + if (strchr("ynmh ", key)) i = max_choice; else { for (i = choice + 1; i < max_choice; i++) { -- cgit v1.2.3 From 823d6f956605cb2f009f75de138622fcd7e03817 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Sat, 21 Jun 2025 14:35:04 -0600 Subject: docs: kdoc: Make body_with_blank_line parsing more flexible The regex in the BODY_WITH_BLANK_LINE case was looking for lines starting with " * ", where exactly one space was allowed before the following text. There are many kerneldoc comments where the authors have put multiple spaces instead, leading to mis-formatting of the documentation. Specifically, in this case, the description portion is associated with the last of the parameters. Allow multiple spaces in this context. See, for example, synchronize_hardirq() and how its documentation is formatted before and after the change. Acked-by: Mauro Carvalho Chehab Tested-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250621203512.223189-2-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 42b2e0936b72..c46e1b6a7d4b 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1309,7 +1309,7 @@ class KernelDoc: """ if self.state == state.BODY_WITH_BLANK_LINE: - r = KernRe(r"\s*\*\s?\S") + r = KernRe(r"\s*\*\s*\S") if r.match(line): self.dump_section() self.entry.section = SECTION_DEFAULT -- cgit v1.2.3 From df2755269456d9ed02ad689aa8eaa50f7ac4217e Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Sat, 21 Jun 2025 14:35:05 -0600 Subject: docs: kdoc: consolidate the "begin section" logic Pull the repeated "begin a section" logic into a single place and hide it within the KernelEntry class. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250621203512.223189-3-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index c46e1b6a7d4b..d29a61a06f6d 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -169,6 +169,15 @@ class KernelEntry: self.warnings.append(log_msg) return + # + # Begin a new section. + # + def begin_section(self, line_no, title = SECTION_DEFAULT, dump = False): + if dump: + self.dump_section(start_new = True) + self.section = title + self.new_start_line = line_no + def dump_section(self, start_new=True): """ Dumps section contents to arrays/hashes intended for that purpose. @@ -1231,12 +1240,11 @@ class KernelDoc: # Check for a DOC: block and handle them specially. # if doc_block.search(line): - self.entry.new_start_line = ln if not doc_block.group(1): - self.entry.section = "Introduction" + self.entry.begin_section(ln, "Introduction") else: - self.entry.section = doc_block.group(1) + self.entry.begin_section(ln, doc_block.group(1)) self.entry.identifier = self.entry.section self.state = state.DOCBLOCK @@ -1270,8 +1278,7 @@ class KernelDoc: self.state = state.BODY self.entry.identifier = self.entry.identifier.strip(" ") # if there's no @param blocks need to set up default section here - self.entry.section = SECTION_DEFAULT - self.entry.new_start_line = ln + 1 + self.entry.begin_section(ln + 1) # # Find the description portion, which *should* be there but # isn't always. @@ -1312,8 +1319,7 @@ class KernelDoc: r = KernRe(r"\s*\*\s*\S") if r.match(line): self.dump_section() - self.entry.section = SECTION_DEFAULT - self.entry.new_start_line = ln + self.entry.begin_section(ln) self.entry.contents = "" if doc_sect.search(line): @@ -1340,8 +1346,7 @@ class KernelDoc: if self.entry.contents.strip("\n"): self.dump_section() - self.entry.new_start_line = ln - self.entry.section = newsection + self.entry.begin_section(ln, newsection) self.entry.leading_space = None self.entry.contents = newcontents.lstrip() @@ -1370,9 +1375,7 @@ class KernelDoc: if cont == "": if self.entry.section == self.section_context: - self.dump_section() - - self.entry.new_start_line = ln + self.entry.begin_section(ln, dump = True) self.state = state.BODY else: if self.entry.section != SECTION_DEFAULT: @@ -1427,8 +1430,7 @@ class KernelDoc: if self.inline_doc_state == state.INLINE_NAME and \ doc_inline_sect.search(line): - self.entry.section = doc_inline_sect.group(1) - self.entry.new_start_line = ln + self.entry.begin_section(ln, doc_inline_sect.group(1)) self.entry.contents = doc_inline_sect.group(2).lstrip() if self.entry.contents != "": @@ -1627,7 +1629,7 @@ class KernelDoc: """STATE_PROTO: reading a function/whatever prototype.""" if doc_inline_oneline.search(line): - self.entry.section = doc_inline_oneline.group(1) + self.entry.begin_section(ln, doc_inline_oneline.group(1)) self.entry.contents = doc_inline_oneline.group(2) if self.entry.contents != "": -- cgit v1.2.3 From e4153a2255b1a0f3398360895e79e7709a0600b2 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Sat, 21 Jun 2025 14:35:06 -0600 Subject: docs: kdoc: separate out the handling of the declaration phase The BODY_MAYBE state really describes the "we are in a declaration" state. Rename it accordingly, and split the handling of this state out from that of the other BODY* states. This change introduces a fair amount of duplicated code that will be coalesced in a later patch. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250621203512.223189-4-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 93 ++++++++++++++++++++++++++++++++++------- 1 file changed, 78 insertions(+), 15 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index d29a61a06f6d..f1491f8c88e7 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -86,7 +86,7 @@ class state: # Parser states NORMAL = 0 # normal code NAME = 1 # looking for function name - BODY_MAYBE = 2 # body - or maybe more description + DECLARATION = 2 # We have seen a declaration which might not be done BODY = 3 # the body of the comment BODY_WITH_BLANK_LINE = 4 # the body which has a blank line PROTO = 5 # scanning prototype @@ -96,7 +96,7 @@ class state: name = [ "NORMAL", "NAME", - "BODY_MAYBE", + "DECLARATION", "BODY", "BODY_WITH_BLANK_LINE", "PROTO", @@ -1287,7 +1287,7 @@ class KernelDoc: r = KernRe("[-:](.*)") if r.search(line): self.entry.declaration_purpose = trim_whitespace(r.group(1)) - self.state = state.BODY_MAYBE + self.state = state.DECLARATION else: self.entry.declaration_purpose = "" @@ -1310,9 +1310,82 @@ class KernelDoc: else: self.emit_msg(ln, f"Cannot find identifier on line:\n{line}") + def process_decl(self, ln, line): + """ + STATE_DECLARATION: We've seen the beginning of a declaration + """ + if doc_sect.search(line): + self.entry.in_doc_sect = True + newsection = doc_sect.group(1) + + if newsection.lower() in ["description", "context"]: + newsection = newsection.title() + + # Special case: @return is a section, not a param description + if newsection.lower() in ["@return", "@returns", + "return", "returns"]: + newsection = "Return" + + # Perl kernel-doc has a check here for contents before sections. + # the logic there is always false, as in_doc_sect variable is + # always true. So, just don't implement Wcontents_before_sections + + # .title() + newcontents = doc_sect.group(2) + if not newcontents: + newcontents = "" + + if self.entry.contents.strip("\n"): + self.dump_section() + + self.entry.begin_section(ln, newsection) + self.entry.leading_space = None + + self.entry.contents = newcontents.lstrip() + if self.entry.contents: + self.entry.contents += "\n" + + self.state = state.BODY + return + + if doc_end.search(line): + self.dump_section() + + # Look for doc_com + + doc_end: + r = KernRe(r'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') + if r.match(line): + self.emit_msg(ln, f"suspicious ending line: {line}") + + self.entry.prototype = "" + self.entry.new_start_line = ln + 1 + + self.state = state.PROTO + return + + if doc_content.search(line): + cont = doc_content.group(1) + + if cont == "": + self.state = state.BODY + self.entry.contents += "\n" # needed? + + else: + # Continued declaration purpose + self.entry.declaration_purpose = self.entry.declaration_purpose.rstrip() + self.entry.declaration_purpose += " " + cont + + r = KernRe(r"\s+") + self.entry.declaration_purpose = r.sub(' ', + self.entry.declaration_purpose) + return + + # Unknown line, ignore + self.emit_msg(ln, f"bad line: {line}") + + def process_body(self, ln, line): """ - STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment. + STATE_BODY: the bulk of a kerneldoc comment. """ if self.state == state.BODY_WITH_BLANK_LINE: @@ -1385,16 +1458,6 @@ class KernelDoc: self.entry.contents += "\n" - elif self.state == state.BODY_MAYBE: - - # Continued declaration purpose - self.entry.declaration_purpose = self.entry.declaration_purpose.rstrip() - self.entry.declaration_purpose += " " + cont - - r = KernRe(r"\s+") - self.entry.declaration_purpose = r.sub(' ', - self.entry.declaration_purpose) - else: if self.entry.section.startswith('@') or \ self.entry.section == self.section_context: @@ -1687,7 +1750,7 @@ class KernelDoc: state.NORMAL: process_normal, state.NAME: process_name, state.BODY: process_body, - state.BODY_MAYBE: process_body, + state.DECLARATION: process_decl, state.BODY_WITH_BLANK_LINE: process_body, state.INLINE: process_inline, state.PROTO: process_proto, -- cgit v1.2.3 From 74cee0dfc2fc50e0d53629c289dc9b2954d31b1c Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Sat, 21 Jun 2025 14:35:07 -0600 Subject: docs: kdoc: split out the special-section state The state known as BODY_WITH_BLANK_LINE really, in a convoluted way, indicates a "special section" that is terminated by a blank line or the beginning of a new section. That is either "@param: desc" sections, or the weird "context" section that plays by the same rules. Rename the state to SPECIAL_SECTION and split its processing into a separate function; no real changes to the logic yet. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250621203512.223189-5-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index f1491f8c88e7..185ffe4e1469 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -88,7 +88,7 @@ class state: NAME = 1 # looking for function name DECLARATION = 2 # We have seen a declaration which might not be done BODY = 3 # the body of the comment - BODY_WITH_BLANK_LINE = 4 # the body which has a blank line + SPECIAL_SECTION = 4 # doc section ending with a blank line PROTO = 5 # scanning prototype DOCBLOCK = 6 # documentation block INLINE = 7 # gathering doc outside main block @@ -98,7 +98,7 @@ class state: "NAME", "DECLARATION", "BODY", - "BODY_WITH_BLANK_LINE", + "SPECIAL_SECTION", "PROTO", "DOCBLOCK", "INLINE", @@ -1383,18 +1383,18 @@ class KernelDoc: self.emit_msg(ln, f"bad line: {line}") + def process_special(self, ln, line): + """ + STATE_SPECIAL_SECTION: a section ending with a blank line + """ + if KernRe(r"\s*\*\s*\S").match(line): + self.entry.begin_section(ln, dump = True) + self.process_body(ln, line) + def process_body(self, ln, line): """ STATE_BODY: the bulk of a kerneldoc comment. """ - - if self.state == state.BODY_WITH_BLANK_LINE: - r = KernRe(r"\s*\*\s*\S") - if r.match(line): - self.dump_section() - self.entry.begin_section(ln) - self.entry.contents = "" - if doc_sect.search(line): self.entry.in_doc_sect = True newsection = doc_sect.group(1) @@ -1452,7 +1452,7 @@ class KernelDoc: self.state = state.BODY else: if self.entry.section != SECTION_DEFAULT: - self.state = state.BODY_WITH_BLANK_LINE + self.state = state.SPECIAL_SECTION else: self.state = state.BODY @@ -1751,7 +1751,7 @@ class KernelDoc: state.NAME: process_name, state.BODY: process_body, state.DECLARATION: process_decl, - state.BODY_WITH_BLANK_LINE: process_body, + state.SPECIAL_SECTION: process_special, state.INLINE: process_inline, state.PROTO: process_proto, state.DOCBLOCK: process_docblock, -- cgit v1.2.3 From 99327067e1974e83cd8a60cf8445ce49086de46e Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Sat, 21 Jun 2025 14:35:08 -0600 Subject: docs: kdoc: coalesce the new-section handling Merge the duplicated code back into a single implementation. Code movement only, no logic changes. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250621203512.223189-6-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 49 +++++++++++------------------------------ 1 file changed, 13 insertions(+), 36 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 185ffe4e1469..a336d543e72b 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1310,10 +1310,10 @@ class KernelDoc: else: self.emit_msg(ln, f"Cannot find identifier on line:\n{line}") - def process_decl(self, ln, line): - """ - STATE_DECLARATION: We've seen the beginning of a declaration - """ + # + # Helper function to determine if a new section is being started. + # + def is_new_section(self, ln, line): if doc_sect.search(line): self.entry.in_doc_sect = True newsection = doc_sect.group(1) @@ -1346,6 +1346,14 @@ class KernelDoc: self.entry.contents += "\n" self.state = state.BODY + return True + return False + + def process_decl(self, ln, line): + """ + STATE_DECLARATION: We've seen the beginning of a declaration + """ + if self.is_new_section(ln, line): return if doc_end.search(line): @@ -1395,38 +1403,7 @@ class KernelDoc: """ STATE_BODY: the bulk of a kerneldoc comment. """ - if doc_sect.search(line): - self.entry.in_doc_sect = True - newsection = doc_sect.group(1) - - if newsection.lower() in ["description", "context"]: - newsection = newsection.title() - - # Special case: @return is a section, not a param description - if newsection.lower() in ["@return", "@returns", - "return", "returns"]: - newsection = "Return" - - # Perl kernel-doc has a check here for contents before sections. - # the logic there is always false, as in_doc_sect variable is - # always true. So, just don't implement Wcontents_before_sections - - # .title() - newcontents = doc_sect.group(2) - if not newcontents: - newcontents = "" - - if self.entry.contents.strip("\n"): - self.dump_section() - - self.entry.begin_section(ln, newsection) - self.entry.leading_space = None - - self.entry.contents = newcontents.lstrip() - if self.entry.contents: - self.entry.contents += "\n" - - self.state = state.BODY + if self.is_new_section(ln, line): return if doc_end.search(line): -- cgit v1.2.3 From e65d54e19149601b96a19790ee9ba9ed04c59abe Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Sat, 21 Jun 2025 14:35:09 -0600 Subject: docs: kdoc: rework the handling of SPECIAL_SECTION Move the recognition of this state to when we enter it, rather than when we exit, eliminating some twisty logic along the way. Some changes in output do result from this shift, generally for kerneldoc comments that do not quite fit the format. See, for example, struct irqdomain. As far as I can tell, the new behavior is more correct in each case. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250621203512.223189-7-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 48 +++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 28 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index a336d543e72b..5998b02ca3a0 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1316,21 +1316,25 @@ class KernelDoc: def is_new_section(self, ln, line): if doc_sect.search(line): self.entry.in_doc_sect = True + self.state = state.BODY + # + # Pick out the name of our new section, tweaking it if need be. + # newsection = doc_sect.group(1) - - if newsection.lower() in ["description", "context"]: - newsection = newsection.title() - - # Special case: @return is a section, not a param description - if newsection.lower() in ["@return", "@returns", - "return", "returns"]: + if newsection.lower() == 'description': + newsection = 'Description' + elif newsection.lower() == 'context': + newsection = 'Context' + self.state = state.SPECIAL_SECTION + elif newsection.lower() in ["@return", "@returns", + "return", "returns"]: newsection = "Return" - - # Perl kernel-doc has a check here for contents before sections. - # the logic there is always false, as in_doc_sect variable is - # always true. So, just don't implement Wcontents_before_sections - - # .title() + self.state = state.SPECIAL_SECTION + elif newsection[0] == '@': + self.state = state.SPECIAL_SECTION + # + # Initialize the contents, and get the new section going. + # newcontents = doc_sect.group(2) if not newcontents: newcontents = "" @@ -1344,8 +1348,6 @@ class KernelDoc: self.entry.contents = newcontents.lstrip() if self.entry.contents: self.entry.contents += "\n" - - self.state = state.BODY return True return False @@ -1395,8 +1397,9 @@ class KernelDoc: """ STATE_SPECIAL_SECTION: a section ending with a blank line """ - if KernRe(r"\s*\*\s*\S").match(line): + if KernRe(r"\s*\*\s*$").match(line): self.entry.begin_section(ln, dump = True) + self.state = state.BODY self.process_body(ln, line) def process_body(self, ln, line): @@ -1424,20 +1427,9 @@ class KernelDoc: cont = doc_content.group(1) if cont == "": - if self.entry.section == self.section_context: - self.entry.begin_section(ln, dump = True) - self.state = state.BODY - else: - if self.entry.section != SECTION_DEFAULT: - self.state = state.SPECIAL_SECTION - else: - self.state = state.BODY - self.entry.contents += "\n" - else: - if self.entry.section.startswith('@') or \ - self.entry.section == self.section_context: + if self.state == state.SPECIAL_SECTION: if self.entry.leading_space is None: r = KernRe(r'^(\s+)') if r.match(cont): -- cgit v1.2.3 From 2ad02b94914ab47b3b94274856e1b56cd94d3e31 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Sat, 21 Jun 2025 14:35:10 -0600 Subject: docs: kdoc: coalesce the end-of-comment processing Separate out the end-of-comment logic into its own helper and remove the duplicated code introduced earlier. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250621203512.223189-8-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 5998b02ca3a0..f7a5b85a8ed7 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1351,13 +1351,10 @@ class KernelDoc: return True return False - def process_decl(self, ln, line): - """ - STATE_DECLARATION: We've seen the beginning of a declaration - """ - if self.is_new_section(ln, line): - return - + # + # Helper function to detect (and effect) the end of a kerneldoc comment. + # + def is_comment_end(self, ln, line): if doc_end.search(line): self.dump_section() @@ -1370,6 +1367,15 @@ class KernelDoc: self.entry.new_start_line = ln + 1 self.state = state.PROTO + return True + return False + + + def process_decl(self, ln, line): + """ + STATE_DECLARATION: We've seen the beginning of a declaration + """ + if self.is_new_section(ln, line) or self.is_comment_end(ln, line): return if doc_content.search(line): @@ -1406,21 +1412,7 @@ class KernelDoc: """ STATE_BODY: the bulk of a kerneldoc comment. """ - if self.is_new_section(ln, line): - return - - if doc_end.search(line): - self.dump_section() - - # Look for doc_com + + doc_end: - r = KernRe(r'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') - if r.match(line): - self.emit_msg(ln, f"suspicious ending line: {line}") - - self.entry.prototype = "" - self.entry.new_start_line = ln + 1 - - self.state = state.PROTO + if self.is_new_section(ln, line) or self.is_comment_end(ln, line): return if doc_content.search(line): -- cgit v1.2.3 From ccad65a494657e899f9139174fcc74c64316c10a Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Sat, 21 Jun 2025 14:35:11 -0600 Subject: docs: kdoc: Add some comments to process_decl() Now that the function can actually fit into a human brain, add a few comments. While I was at it, I switched to the trim_whitespace() helper rather than open-coding it. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250621203512.223189-9-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index f7a5b85a8ed7..a6ee8bac378d 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1377,26 +1377,28 @@ class KernelDoc: """ if self.is_new_section(ln, line) or self.is_comment_end(ln, line): return - + # + # Look for anything with the " * " line beginning. + # if doc_content.search(line): cont = doc_content.group(1) - + # + # A blank line means that we have moved out of the declaration + # part of the comment (without any "special section" parameter + # descriptions). + # if cont == "": self.state = state.BODY self.entry.contents += "\n" # needed? - + # + # Otherwise we have more of the declaration section to soak up. + # else: - # Continued declaration purpose - self.entry.declaration_purpose = self.entry.declaration_purpose.rstrip() - self.entry.declaration_purpose += " " + cont - - r = KernRe(r"\s+") - self.entry.declaration_purpose = r.sub(' ', - self.entry.declaration_purpose) - return - - # Unknown line, ignore - self.emit_msg(ln, f"bad line: {line}") + self.entry.declaration_purpose = \ + trim_whitespace(self.entry.declaration_purpose + ' ' + cont) + else: + # Unknown line, ignore + self.emit_msg(ln, f"bad line: {line}") def process_special(self, ln, line): -- cgit v1.2.3 From 07e04d8e7dceae9822377abcb2dd07aae5747e7d Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Sat, 21 Jun 2025 14:35:12 -0600 Subject: docs: kdoc: finish disentangling the BODY and SPECIAL_SECTION states Move the last SPECIAL_SECTION special case into the proper handler function, getting rid of more if/then/else logic. The leading-space tracking was tightened up a bit in the move. Add some comments describing what is going on. No changes to the generated output. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250621203512.223189-10-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 80 ++++++++++++++++++++++++----------------- 1 file changed, 48 insertions(+), 32 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index a6ee8bac378d..3557c512c85a 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1405,10 +1405,53 @@ class KernelDoc: """ STATE_SPECIAL_SECTION: a section ending with a blank line """ + # + # If we have hit a blank line (only the " * " marker), then this + # section is done. + # if KernRe(r"\s*\*\s*$").match(line): self.entry.begin_section(ln, dump = True) + self.entry.contents += '\n' self.state = state.BODY - self.process_body(ln, line) + return + # + # Not a blank line, look for the other ways to end the section. + # + if self.is_new_section(ln, line) or self.is_comment_end(ln, line): + return + # + # OK, we should have a continuation of the text for this section. + # + if doc_content.search(line): + cont = doc_content.group(1) + # + # If the lines of text after the first in a special section have + # leading white space, we need to trim it out or Sphinx will get + # confused. For the second line (the None case), see what we + # find there and remember it. + # + if self.entry.leading_space is None: + r = KernRe(r'^(\s+)') + if r.match(cont): + self.entry.leading_space = len(r.group(1)) + else: + self.entry.leading_space = 0 + # + # Otherwise, before trimming any leading chars, be *sure* + # that they are white space. We should maybe warn if this + # isn't the case. + # + for i in range(0, self.entry.leading_space): + if cont[i] != " ": + self.entry.leading_space = i + break + # + # Add the trimmed result to the section and we're done. + # + self.entry.contents += cont[self.entry.leading_space:] + '\n' + else: + # Unknown line, ignore + self.emit_msg(ln, f"bad line: {line}") def process_body(self, ln, line): """ @@ -1419,37 +1462,10 @@ class KernelDoc: if doc_content.search(line): cont = doc_content.group(1) - - if cont == "": - self.entry.contents += "\n" - else: - if self.state == state.SPECIAL_SECTION: - if self.entry.leading_space is None: - r = KernRe(r'^(\s+)') - if r.match(cont): - self.entry.leading_space = len(r.group(1)) - else: - self.entry.leading_space = 0 - - # Double-check if leading space are realy spaces - pos = 0 - for i in range(0, self.entry.leading_space): - if cont[i] != " ": - break - pos += 1 - - cont = cont[pos:] - - # NEW LOGIC: - # In case it is different, update it - if self.entry.leading_space != pos: - self.entry.leading_space = pos - - self.entry.contents += cont + "\n" - return - - # Unknown line, ignore - self.emit_msg(ln, f"bad line: {line}") + self.entry.contents += cont + "\n" + else: + # Unknown line, ignore + self.emit_msg(ln, f"bad line: {line}") def process_inline(self, ln, line): """STATE_INLINE: docbook comments within a prototype.""" -- cgit v1.2.3 From 54c147f4c76c7891afe99ad2bdeba82143fd4ca6 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 22 Jun 2025 08:02:32 +0200 Subject: scripts: scripts/test_doc_build.py: add script to test doc build Testing Sphinx backward-compatibility is hard, as per version minimal Python dependency requirements can be a nightmare. Add a script to help automate such checks. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/93faf6c35ec865566246ca094868a8e6d85dde39.1750571906.git.mchehab+huawei@kernel.org --- scripts/test_doc_build.py | 241 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 241 insertions(+) create mode 100755 scripts/test_doc_build.py (limited to 'scripts') diff --git a/scripts/test_doc_build.py b/scripts/test_doc_build.py new file mode 100755 index 000000000000..482716fbe91d --- /dev/null +++ b/scripts/test_doc_build.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0 +# Copyright(c) 2025: Mauro Carvalho Chehab +# +# pylint: disable=C0103,R1715 + +""" +Install minimal supported requirements for different Sphinx versions +and optionally test the build. +""" + +import argparse +import os.path +import sys +import time + +from subprocess import run + +# Minimal python version supported by the building system +python_bin = "python3.9" + +# Starting from 8.0.2, Python 3.9 becomes too old +python_changes = {(8, 0, 2): "python3"} + +# Sphinx versions to be installed and their incremental requirements +sphinx_requirements = { + (3, 4, 3): { + "alabaster": "0.7.13", + "babel": "2.17.0", + "certifi": "2025.6.15", + "charset-normalizer": "3.4.2", + "docutils": "0.15", + "idna": "3.10", + "imagesize": "1.4.1", + "Jinja2": "3.0.3", + "MarkupSafe": "2.0", + "packaging": "25.0", + "Pygments": "2.19.1", + "PyYAML": "5.1", + "requests": "2.32.4", + "snowballstemmer": "3.0.1", + "sphinxcontrib-applehelp": "1.0.4", + "sphinxcontrib-devhelp": "1.0.2", + "sphinxcontrib-htmlhelp": "2.0.1", + "sphinxcontrib-jsmath": "1.0.1", + "sphinxcontrib-qthelp": "1.0.3", + "sphinxcontrib-serializinghtml": "1.1.5", + "urllib3": "2.4.0", + }, + (3, 5, 4): {}, + (4, 0, 3): { + "docutils": "0.17.1", + "PyYAML": "5.1", + }, + (4, 1, 2): {}, + (4, 3, 2): {}, + (4, 4, 0): {}, + (4, 5, 0): {}, + (5, 0, 2): {}, + (5, 1, 1): {}, + (5, 2, 3): { + "Jinja2": "3.1.2", + "MarkupSafe": "2.0", + "PyYAML": "5.3.1", + }, + (5, 3, 0): { + "docutils": "0.18.1", + "PyYAML": "5.3.1", + }, + (6, 0, 1): {}, + (6, 1, 3): {}, + (6, 2, 1): { + "PyYAML": "5.4.1", + }, + (7, 0, 1): {}, + (7, 1, 2): {}, + (7, 2, 3): { + "PyYAML": "6.0.1", + "sphinxcontrib-serializinghtml": "1.1.9", + }, + (7, 3, 7): { + "alabaster": "0.7.14", + "PyYAML": "6.0.1", + }, + (7, 4, 7): { + "docutils": "0.20", + "PyYAML": "6.0.1", + }, + (8, 0, 2): {}, + (8, 1, 3): { + "PyYAML": "6.0.1", + "sphinxcontrib-applehelp": "1.0.7", + "sphinxcontrib-devhelp": "1.0.6", + "sphinxcontrib-htmlhelp": "2.0.6", + "sphinxcontrib-qthelp": "1.0.6", + }, + (8, 2, 3): { + "PyYAML": "6.0.1", + "sphinxcontrib-serializinghtml": "1.1.9", + }, +} + + +def parse_version(ver_str): + """Convert a version string into a tuple.""" + + return tuple(map(int, ver_str.split("."))) + + +parser = argparse.ArgumentParser(description="Build docs for different sphinx_versions.") + +parser.add_argument('-v', '--version', help='Sphinx single version', + type=parse_version) +parser.add_argument('--min-version', "--min", help='Sphinx minimal version', + type=parse_version) +parser.add_argument('--max-version', "--max", help='Sphinx maximum version', + type=parse_version) +parser.add_argument('-a', '--make_args', + help='extra arguments for make htmldocs, like SPHINXDIRS=netlink/specs', + nargs="*") +parser.add_argument('-w', '--write', help='write a requirements.txt file', + action='store_true') +parser.add_argument('-m', '--make', + help='Make documentation', + action='store_true') +parser.add_argument('-i', '--wait-input', + help='Wait for an enter before going to the next version', + action='store_true') + +args = parser.parse_args() + +if not args.make_args: + args.make_args = [] + +if args.version: + if args.min_version or args.max_version: + sys.exit("Use either --version or --min-version/--max-version") + else: + args.min_version = args.version + args.max_version = args.version + +sphinx_versions = sorted(list(sphinx_requirements.keys())) + +if not args.min_version: + args.min_version = sphinx_versions[0] + +if not args.max_version: + args.max_version = sphinx_versions[-1] + +first_run = True +cur_requirements = {} +built_time = {} + +for cur_ver, new_reqs in sphinx_requirements.items(): + cur_requirements.update(new_reqs) + + if cur_ver in python_changes: + python_bin = python_changes[cur_ver] + + ver = ".".join(map(str, cur_ver)) + + if args.min_version: + if cur_ver < args.min_version: + continue + + if args.max_version: + if cur_ver > args.max_version: + break + + if not first_run and args.wait_input and args.make: + ret = input("Press Enter to continue or 'a' to abort: ").strip().lower() + if ret == "a": + print("Aborted.") + sys.exit() + else: + first_run = False + + venv_dir = f"Sphinx_{ver}" + req_file = f"requirements_{ver}.txt" + + print(f"\nSphinx {ver} with {python_bin}") + + # Create venv + run([python_bin, "-m", "venv", venv_dir], check=True) + pip = os.path.join(venv_dir, "bin/pip") + + # Create install list + reqs = [] + for pkg, verstr in cur_requirements.items(): + reqs.append(f"{pkg}=={verstr}") + + reqs.append(f"Sphinx=={ver}") + + run([pip, "install"] + reqs, check=True) + + # Freeze environment + result = run([pip, "freeze"], capture_output=True, text=True, check=True) + + # Pip install succeeded. Write requirements file + if args.write: + with open(req_file, "w", encoding="utf-8") as fp: + fp.write(result.stdout) + + if args.make: + start_time = time.time() + + # Prepare a venv environment + env = os.environ.copy() + bin_dir = os.path.join(venv_dir, "bin") + env["PATH"] = bin_dir + ":" + env["PATH"] + env["VIRTUAL_ENV"] = venv_dir + if "PYTHONHOME" in env: + del env["PYTHONHOME"] + + # Test doc build + run(["make", "cleandocs"], env=env, check=True) + make = ["make"] + args.make_args + ["htmldocs"] + + print(f". {bin_dir}/activate") + print(" ".join(make)) + print("deactivate") + run(make, env=env, check=True) + + end_time = time.time() + elapsed_time = end_time - start_time + hours, minutes = divmod(elapsed_time, 3600) + minutes, seconds = divmod(minutes, 60) + + hours = int(hours) + minutes = int(minutes) + seconds = int(seconds) + + built_time[ver] = f"{hours:02d}:{minutes:02d}:{seconds:02d}" + + print(f"Finished doc build for Sphinx {ver}. Elapsed time: {built_time[ver]}") + +if args.make: + print() + print("Summary:") + for ver, elapsed_time in sorted(built_time.items()): + print(f"\tSphinx {ver} elapsed time: {elapsed_time}") -- cgit v1.2.3 From 7649db7de2932c8e80e4bcf54027b3ad6388d95c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 22 Jun 2025 08:02:33 +0200 Subject: scripts: test_doc_build.py: make capture assynchronous Prepare the tool to allow writing the output into log files. For such purpose, receive stdin/stdout messages asynchronously. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/9b0a60b5047137b5ba764701268da992767b128c.1750571906.git.mchehab+huawei@kernel.org --- scripts/test_doc_build.py | 343 +++++++++++++++++++++++++++++++--------------- 1 file changed, 230 insertions(+), 113 deletions(-) (limited to 'scripts') diff --git a/scripts/test_doc_build.py b/scripts/test_doc_build.py index 482716fbe91d..94f2f2d8c3b7 100755 --- a/scripts/test_doc_build.py +++ b/scripts/test_doc_build.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: GPL-2.0 # Copyright(c) 2025: Mauro Carvalho Chehab # -# pylint: disable=C0103,R1715 +# pylint: disable=R0903,R0913,R0914,R0917 """ Install minimal supported requirements for different Sphinx versions @@ -10,20 +10,20 @@ and optionally test the build. """ import argparse +import asyncio import os.path import sys import time - -from subprocess import run +import subprocess # Minimal python version supported by the building system -python_bin = "python3.9" +MINIMAL_PYTHON_VERSION = "python3.9" # Starting from 8.0.2, Python 3.9 becomes too old -python_changes = {(8, 0, 2): "python3"} +PYTHON_VER_CHANGES = {(8, 0, 2): "python3"} # Sphinx versions to be installed and their incremental requirements -sphinx_requirements = { +SPHINX_REQUIREMENTS = { (3, 4, 3): { "alabaster": "0.7.13", "babel": "2.17.0", @@ -101,141 +101,258 @@ sphinx_requirements = { } -def parse_version(ver_str): - """Convert a version string into a tuple.""" +class AsyncCommands: + """Excecute command synchronously""" + + stdout = None + stderr = None + output = None + + async def _read(self, stream, verbose, is_info): + """Ancillary routine to capture while displaying""" + + while stream is not None: + line = await stream.readline() + if line: + out = line.decode("utf-8", errors="backslashreplace") + self.output += out + if is_info: + if verbose: + print(out.rstrip("\n")) + + self.stdout += out + else: + if verbose: + print(out.rstrip("\n"), file=sys.stderr) + + self.stderr += out + else: + break + + async def run(self, cmd, capture_output=False, check=False, + env=None, verbose=True): + + """ + Execute an arbitrary command, handling errors. + + Please notice that this class is not thread safe + """ + + self.stdout = "" + self.stderr = "" + self.output = "" + + if verbose: + print("$ ", " ".join(cmd)) + + proc = await asyncio.create_subprocess_exec(cmd[0], + *cmd[1:], + env=env, + stdout=asyncio.subprocess.PIPE, + stderr=asyncio.subprocess.PIPE) + + # Handle input and output in realtime + await asyncio.gather( + self._read(proc.stdout, verbose, True), + self._read(proc.stderr, verbose, False), + ) + + await proc.wait() + + if check and proc.returncode > 0: + raise subprocess.CalledProcessError(returncode=proc.returncode, + cmd=" ".join(cmd), + output=self.stdout, + stderr=self.stderr) + + if capture_output: + if proc.returncode > 0: + print("Error {proc.returncode}", file=sys.stderr) + return "" + + return self.output + + ret = subprocess.CompletedProcess(args=cmd, + returncode=proc.returncode, + stdout=self.stdout, + stderr=self.stderr) + + return ret - return tuple(map(int, ver_str.split("."))) +class SphinxVenv: + """ + Installs Sphinx on one virtual env per Sphinx version with a minimal + set of dependencies, adjusting them to each specific version. + """ -parser = argparse.ArgumentParser(description="Build docs for different sphinx_versions.") + def __init__(self): + """Initialize instance variables""" -parser.add_argument('-v', '--version', help='Sphinx single version', - type=parse_version) -parser.add_argument('--min-version', "--min", help='Sphinx minimal version', - type=parse_version) -parser.add_argument('--max-version', "--max", help='Sphinx maximum version', - type=parse_version) -parser.add_argument('-a', '--make_args', - help='extra arguments for make htmldocs, like SPHINXDIRS=netlink/specs', - nargs="*") -parser.add_argument('-w', '--write', help='write a requirements.txt file', - action='store_true') -parser.add_argument('-m', '--make', - help='Make documentation', - action='store_true') -parser.add_argument('-i', '--wait-input', - help='Wait for an enter before going to the next version', - action='store_true') + self.built_time = {} + self.first_run = True -args = parser.parse_args() - -if not args.make_args: - args.make_args = [] + async def _handle_version(self, args, cur_ver, cur_requirements, python_bin): + """Handle a single Sphinx version""" -if args.version: - if args.min_version or args.max_version: - sys.exit("Use either --version or --min-version/--max-version") - else: - args.min_version = args.version - args.max_version = args.version + cmd = AsyncCommands() -sphinx_versions = sorted(list(sphinx_requirements.keys())) + ver = ".".join(map(str, cur_ver)) -if not args.min_version: - args.min_version = sphinx_versions[0] + if not self.first_run and args.wait_input and args.make: + ret = input("Press Enter to continue or 'a' to abort: ").strip().lower() + if ret == "a": + print("Aborted.") + sys.exit() + else: + self.first_run = False -if not args.max_version: - args.max_version = sphinx_versions[-1] + venv_dir = f"Sphinx_{ver}" + req_file = f"requirements_{ver}.txt" -first_run = True -cur_requirements = {} -built_time = {} + print(f"\nSphinx {ver} with {python_bin}") -for cur_ver, new_reqs in sphinx_requirements.items(): - cur_requirements.update(new_reqs) + # Create venv + await cmd.run([python_bin, "-m", "venv", venv_dir], check=True) + pip = os.path.join(venv_dir, "bin/pip") - if cur_ver in python_changes: - python_bin = python_changes[cur_ver] + # Create install list + reqs = [] + for pkg, verstr in cur_requirements.items(): + reqs.append(f"{pkg}=={verstr}") - ver = ".".join(map(str, cur_ver)) + reqs.append(f"Sphinx=={ver}") - if args.min_version: - if cur_ver < args.min_version: - continue + await cmd.run([pip, "install"] + reqs, check=True, verbose=True) - if args.max_version: - if cur_ver > args.max_version: - break + # Freeze environment + result = await cmd.run([pip, "freeze"], verbose=False, check=True) - if not first_run and args.wait_input and args.make: - ret = input("Press Enter to continue or 'a' to abort: ").strip().lower() - if ret == "a": - print("Aborted.") - sys.exit() - else: - first_run = False + # Pip install succeeded. Write requirements file + if args.write: + with open(req_file, "w", encoding="utf-8") as fp: + fp.write(result.stdout) - venv_dir = f"Sphinx_{ver}" - req_file = f"requirements_{ver}.txt" + if args.make: + start_time = time.time() - print(f"\nSphinx {ver} with {python_bin}") + # Prepare a venv environment + env = os.environ.copy() + bin_dir = os.path.join(venv_dir, "bin") + env["PATH"] = bin_dir + ":" + env["PATH"] + env["VIRTUAL_ENV"] = venv_dir + if "PYTHONHOME" in env: + del env["PYTHONHOME"] + + # Test doc build + await cmd.run(["make", "cleandocs"], env=env, check=True) + make = ["make"] + args.make_args + ["htmldocs"] + + print(f". {bin_dir}/activate") + print(" ".join(make)) + print("deactivate") + await cmd.run(make, env=env, check=True) + + end_time = time.time() + elapsed_time = end_time - start_time + hours, minutes = divmod(elapsed_time, 3600) + minutes, seconds = divmod(minutes, 60) + + hours = int(hours) + minutes = int(minutes) + seconds = int(seconds) + + self.built_time[ver] = f"{hours:02d}:{minutes:02d}:{seconds:02d}" + + print(f"Finished doc build for Sphinx {ver}. Elapsed time: {self.built_time[ver]}") + + async def run(self, args): + """ + Navigate though multiple Sphinx versions, handling each of them + on a loop. + """ + + cur_requirements = {} + python_bin = MINIMAL_PYTHON_VERSION + + for cur_ver, new_reqs in SPHINX_REQUIREMENTS.items(): + cur_requirements.update(new_reqs) + + if cur_ver in PYTHON_VER_CHANGES: # pylint: disable=R1715 + + python_bin = PYTHON_VER_CHANGES[cur_ver] + + if args.min_version: + if cur_ver < args.min_version: + continue + + if args.max_version: + if cur_ver > args.max_version: + break + + await self._handle_version(args, cur_ver, cur_requirements, + python_bin) + + if args.make: + print() + print("Summary:") + for ver, elapsed_time in sorted(self.built_time.items()): + print(f"\tSphinx {ver} elapsed time: {elapsed_time}") + + +def parse_version(ver_str): + """Convert a version string into a tuple.""" + + return tuple(map(int, ver_str.split("."))) - # Create venv - run([python_bin, "-m", "venv", venv_dir], check=True) - pip = os.path.join(venv_dir, "bin/pip") - # Create install list - reqs = [] - for pkg, verstr in cur_requirements.items(): - reqs.append(f"{pkg}=={verstr}") +async def main(): + """Main program""" - reqs.append(f"Sphinx=={ver}") + parser = argparse.ArgumentParser(description="Build docs for different sphinx_versions.") - run([pip, "install"] + reqs, check=True) + parser.add_argument('-v', '--version', help='Sphinx single version', + type=parse_version) + parser.add_argument('--min-version', "--min", help='Sphinx minimal version', + type=parse_version) + parser.add_argument('--max-version', "--max", help='Sphinx maximum version', + type=parse_version) + parser.add_argument('-a', '--make_args', + help='extra arguments for make htmldocs, like SPHINXDIRS=netlink/specs', + nargs="*") + parser.add_argument('-w', '--write', help='write a requirements.txt file', + action='store_true') + parser.add_argument('-m', '--make', + help='Make documentation', + action='store_true') + parser.add_argument('-i', '--wait-input', + help='Wait for an enter before going to the next version', + action='store_true') - # Freeze environment - result = run([pip, "freeze"], capture_output=True, text=True, check=True) + args = parser.parse_args() - # Pip install succeeded. Write requirements file - if args.write: - with open(req_file, "w", encoding="utf-8") as fp: - fp.write(result.stdout) + if not args.make_args: + args.make_args = [] - if args.make: - start_time = time.time() + if args.version: + if args.min_version or args.max_version: + sys.exit("Use either --version or --min-version/--max-version") + else: + args.min_version = args.version + args.max_version = args.version - # Prepare a venv environment - env = os.environ.copy() - bin_dir = os.path.join(venv_dir, "bin") - env["PATH"] = bin_dir + ":" + env["PATH"] - env["VIRTUAL_ENV"] = venv_dir - if "PYTHONHOME" in env: - del env["PYTHONHOME"] - - # Test doc build - run(["make", "cleandocs"], env=env, check=True) - make = ["make"] + args.make_args + ["htmldocs"] - - print(f". {bin_dir}/activate") - print(" ".join(make)) - print("deactivate") - run(make, env=env, check=True) + sphinx_versions = sorted(list(SPHINX_REQUIREMENTS.keys())) - end_time = time.time() - elapsed_time = end_time - start_time - hours, minutes = divmod(elapsed_time, 3600) - minutes, seconds = divmod(minutes, 60) + if not args.min_version: + args.min_version = sphinx_versions[0] - hours = int(hours) - minutes = int(minutes) - seconds = int(seconds) + if not args.max_version: + args.max_version = sphinx_versions[-1] - built_time[ver] = f"{hours:02d}:{minutes:02d}:{seconds:02d}" + venv = SphinxVenv() + await venv.run(args) - print(f"Finished doc build for Sphinx {ver}. Elapsed time: {built_time[ver]}") -if args.make: - print() - print("Summary:") - for ver, elapsed_time in sorted(built_time.items()): - print(f"\tSphinx {ver} elapsed time: {elapsed_time}") +# Call main method +if __name__ == "__main__": + asyncio.run(main()) -- cgit v1.2.3 From fb1e8d1265a555ddd1cbfd0307477b4c4b048cc9 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 22 Jun 2025 08:02:34 +0200 Subject: scripts: test_doc_build.py: better control its output Now that asyncio is supported, allow userspace to adjust verbosity level and direct the script output to a file. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/76c3a64a87a7493ae607d5c7784b3b829affcaf0.1750571906.git.mchehab+huawei@kernel.org --- scripts/test_doc_build.py | 78 +++++++++++++++++++++++++++++++---------------- 1 file changed, 51 insertions(+), 27 deletions(-) (limited to 'scripts') diff --git a/scripts/test_doc_build.py b/scripts/test_doc_build.py index 94f2f2d8c3b7..5b9eb2c0bf01 100755 --- a/scripts/test_doc_build.py +++ b/scripts/test_doc_build.py @@ -2,7 +2,7 @@ # SPDX-License-Identifier: GPL-2.0 # Copyright(c) 2025: Mauro Carvalho Chehab # -# pylint: disable=R0903,R0913,R0914,R0917 +# pylint: disable=R0903,R0912,R0913,R0914,R0917,C0301 """ Install minimal supported requirements for different Sphinx versions @@ -104,9 +104,22 @@ SPHINX_REQUIREMENTS = { class AsyncCommands: """Excecute command synchronously""" - stdout = None - stderr = None - output = None + def __init__(self, fp=None): + + self.stdout = None + self.stderr = None + self.output = None + self.fp = fp + + def log(self, out, verbose, is_info=True): + if verbose: + if is_info: + print(out.rstrip("\n")) + else: + print(out.rstrip("\n"), file=sys.stderr) + + if self.fp: + self.fp.write(out.rstrip("\n") + "\n") async def _read(self, stream, verbose, is_info): """Ancillary routine to capture while displaying""" @@ -115,16 +128,10 @@ class AsyncCommands: line = await stream.readline() if line: out = line.decode("utf-8", errors="backslashreplace") - self.output += out + self.log(out, verbose, is_info) if is_info: - if verbose: - print(out.rstrip("\n")) - self.stdout += out else: - if verbose: - print(out.rstrip("\n"), file=sys.stderr) - self.stderr += out else: break @@ -140,10 +147,8 @@ class AsyncCommands: self.stdout = "" self.stderr = "" - self.output = "" - if verbose: - print("$ ", " ".join(cmd)) + self.log("$ " + " ".join(cmd), verbose) proc = await asyncio.create_subprocess_exec(cmd[0], *cmd[1:], @@ -167,7 +172,7 @@ class AsyncCommands: if capture_output: if proc.returncode > 0: - print("Error {proc.returncode}", file=sys.stderr) + self.log(f"Error {proc.returncode}", verbose=True, is_info=False) return "" return self.output @@ -192,10 +197,11 @@ class SphinxVenv: self.built_time = {} self.first_run = True - async def _handle_version(self, args, cur_ver, cur_requirements, python_bin): + async def _handle_version(self, args, fp, + cur_ver, cur_requirements, python_bin): """Handle a single Sphinx version""" - cmd = AsyncCommands() + cmd = AsyncCommands(fp) ver = ".".join(map(str, cur_ver)) @@ -210,10 +216,11 @@ class SphinxVenv: venv_dir = f"Sphinx_{ver}" req_file = f"requirements_{ver}.txt" - print(f"\nSphinx {ver} with {python_bin}") + cmd.log(f"\nSphinx {ver} with {python_bin}", verbose=True) # Create venv - await cmd.run([python_bin, "-m", "venv", venv_dir], check=True) + await cmd.run([python_bin, "-m", "venv", venv_dir], + verbose=args.verbose, check=True) pip = os.path.join(venv_dir, "bin/pip") # Create install list @@ -223,7 +230,7 @@ class SphinxVenv: reqs.append(f"Sphinx=={ver}") - await cmd.run([pip, "install"] + reqs, check=True, verbose=True) + await cmd.run([pip, "install"] + reqs, check=True, verbose=args.verbose) # Freeze environment result = await cmd.run([pip, "freeze"], verbose=False, check=True) @@ -248,10 +255,11 @@ class SphinxVenv: await cmd.run(["make", "cleandocs"], env=env, check=True) make = ["make"] + args.make_args + ["htmldocs"] - print(f". {bin_dir}/activate") - print(" ".join(make)) - print("deactivate") - await cmd.run(make, env=env, check=True) + if args.verbose: + print(f". {bin_dir}/activate") + await cmd.run(make, env=env, check=True, verbose=True) + if args.verbose: + print("deactivate") end_time = time.time() elapsed_time = end_time - start_time @@ -264,7 +272,7 @@ class SphinxVenv: self.built_time[ver] = f"{hours:02d}:{minutes:02d}:{seconds:02d}" - print(f"Finished doc build for Sphinx {ver}. Elapsed time: {self.built_time[ver]}") + cmd.log(f"Finished doc build for Sphinx {ver}. Elapsed time: {self.built_time[ver]}", verbose=True) async def run(self, args): """ @@ -272,6 +280,15 @@ class SphinxVenv: on a loop. """ + if args.log: + fp = open(args.log, "w", encoding="utf-8") + if not args.verbose: + args.verbose = False + else: + fp = None + if not args.verbose: + args.verbose = True + cur_requirements = {} python_bin = MINIMAL_PYTHON_VERSION @@ -290,7 +307,7 @@ class SphinxVenv: if cur_ver > args.max_version: break - await self._handle_version(args, cur_ver, cur_requirements, + await self._handle_version(args, fp, cur_ver, cur_requirements, python_bin) if args.make: @@ -299,6 +316,8 @@ class SphinxVenv: for ver, elapsed_time in sorted(self.built_time.items()): print(f"\tSphinx {ver} elapsed time: {elapsed_time}") + if fp: + fp.close() def parse_version(ver_str): """Convert a version string into a tuple.""" @@ -311,7 +330,7 @@ async def main(): parser = argparse.ArgumentParser(description="Build docs for different sphinx_versions.") - parser.add_argument('-v', '--version', help='Sphinx single version', + parser.add_argument('-V', '--version', help='Sphinx single version', type=parse_version) parser.add_argument('--min-version', "--min", help='Sphinx minimal version', type=parse_version) @@ -328,6 +347,11 @@ async def main(): parser.add_argument('-i', '--wait-input', help='Wait for an enter before going to the next version', action='store_true') + parser.add_argument('-v', '--verbose', + help='Verbose all commands', + action='store_true') + parser.add_argument('-l', '--log', + help='Log command output on a file') args = parser.parse_args() -- cgit v1.2.3 From 3fa60d281130064808751f6d48224330f0879ce1 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 22 Jun 2025 08:02:35 +0200 Subject: scripts: test_doc_build.py: better adjust to python version Very old versions of Sphinx require older versions of python. The original script assumes that a python3.9 exec exists, but this may not be the case. Relax python requirements. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/32cb41c543293bbbab5fcb15f8a0aefac040e3a9.1750571906.git.mchehab+huawei@kernel.org --- scripts/test_doc_build.py | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/test_doc_build.py b/scripts/test_doc_build.py index 5b9eb2c0bf01..129c2862065c 100755 --- a/scripts/test_doc_build.py +++ b/scripts/test_doc_build.py @@ -12,15 +12,28 @@ and optionally test the build. import argparse import asyncio import os.path +import shutil import sys import time import subprocess -# Minimal python version supported by the building system -MINIMAL_PYTHON_VERSION = "python3.9" +# Minimal python version supported by the building system. -# Starting from 8.0.2, Python 3.9 becomes too old -PYTHON_VER_CHANGES = {(8, 0, 2): "python3"} +PYTHON = os.path.basename(sys.executable) + +min_python_bin = None + +for i in range(9, 13): + p = f"python3.{i}" + if shutil.which(p): + min_python_bin = p + break + +if not min_python_bin: + min_python_bin = PYTHON + +# Starting from 8.0, Python 3.9 is not supported anymore. +PYTHON_VER_CHANGES = {(8, 0, 2): PYTHON} # Sphinx versions to be installed and their incremental requirements SPHINX_REQUIREMENTS = { @@ -290,7 +303,7 @@ class SphinxVenv: args.verbose = True cur_requirements = {} - python_bin = MINIMAL_PYTHON_VERSION + python_bin = min_python_bin for cur_ver, new_reqs in SPHINX_REQUIREMENTS.items(): cur_requirements.update(new_reqs) -- cgit v1.2.3 From 792bf0194ce88606bc08243b78ec2dac2ea10ee5 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 22 Jun 2025 08:02:36 +0200 Subject: scripts: test_doc_build.py: improve dependency list Change the dependency list to ensure that: - all docutils versions are covered; - provide an explanation about the dependencies; - set a better minimal requirement for 3.4.3. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/433aeefb4ac9edbd62494334ac07bc1307387d40.1750571906.git.mchehab+huawei@kernel.org --- scripts/test_doc_build.py | 68 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 50 insertions(+), 18 deletions(-) (limited to 'scripts') diff --git a/scripts/test_doc_build.py b/scripts/test_doc_build.py index 129c2862065c..087b8c476f05 100755 --- a/scripts/test_doc_build.py +++ b/scripts/test_doc_build.py @@ -37,70 +37,102 @@ PYTHON_VER_CHANGES = {(8, 0, 2): PYTHON} # Sphinx versions to be installed and their incremental requirements SPHINX_REQUIREMENTS = { + # Oldest versions we support for each package required by Sphinx 3.4.3 (3, 4, 3): { + "docutils": "0.16", + "alabaster": "0.7.12", + "babel": "2.8.0", + "certifi": "2020.6.20", + "docutils": "0.16", + "idna": "2.10", + "imagesize": "1.2.0", + "Jinja2": "2.11.2", + "MarkupSafe": "1.1.1", + "packaging": "20.4", + "Pygments": "2.6.1", + "PyYAML": "5.1", + "requests": "2.24.0", + "snowballstemmer": "2.0.0", + "sphinxcontrib-applehelp": "1.0.2", + "sphinxcontrib-devhelp": "1.0.2", + "sphinxcontrib-htmlhelp": "1.0.3", + "sphinxcontrib-jsmath": "1.0.1", + "sphinxcontrib-qthelp": "1.0.3", + "sphinxcontrib-serializinghtml": "1.1.4", + "urllib3": "1.25.9", + }, + + # Update package dependencies to a more modern base. The goal here + # is to avoid to many incremental changes for the next entries + (3, 5, 4): { "alabaster": "0.7.13", "babel": "2.17.0", "certifi": "2025.6.15", - "charset-normalizer": "3.4.2", - "docutils": "0.15", "idna": "3.10", "imagesize": "1.4.1", "Jinja2": "3.0.3", "MarkupSafe": "2.0", "packaging": "25.0", "Pygments": "2.19.1", - "PyYAML": "5.1", "requests": "2.32.4", "snowballstemmer": "3.0.1", "sphinxcontrib-applehelp": "1.0.4", - "sphinxcontrib-devhelp": "1.0.2", "sphinxcontrib-htmlhelp": "2.0.1", - "sphinxcontrib-jsmath": "1.0.1", - "sphinxcontrib-qthelp": "1.0.3", "sphinxcontrib-serializinghtml": "1.1.5", - "urllib3": "2.4.0", }, - (3, 5, 4): {}, + + # Starting from here, ensure all docutils versions are covered with + # supported Sphinx versions. Other packages are upgraded only when + # required by pip (4, 0, 3): { - "docutils": "0.17.1", + "docutils": "0.17", "PyYAML": "5.1", }, - (4, 1, 2): {}, - (4, 3, 2): {}, + (4, 1, 2): { + }, + (4, 3, 2): { + }, (4, 4, 0): {}, (4, 5, 0): {}, (5, 0, 2): {}, (5, 1, 1): {}, (5, 2, 3): { + "docutils": "0.17.1", "Jinja2": "3.1.2", "MarkupSafe": "2.0", "PyYAML": "5.3.1", }, - (5, 3, 0): { - "docutils": "0.18.1", - "PyYAML": "5.3.1", + (5, 3, 0): {}, + (6, 0, 1): { + "docutils": "0.18", }, - (6, 0, 1): {}, (6, 1, 3): {}, (6, 2, 1): { + "docutils": "0.18.1", "PyYAML": "5.4.1", }, - (7, 0, 1): {}, + (7, 0, 1): { + }, (7, 1, 2): {}, (7, 2, 3): { + "docutils": "0.19", "PyYAML": "6.0.1", "sphinxcontrib-serializinghtml": "1.1.9", }, (7, 3, 7): { + "docutils": "0.20", "alabaster": "0.7.14", "PyYAML": "6.0.1", }, (7, 4, 7): { - "docutils": "0.20", + "docutils": "0.21", "PyYAML": "6.0.1", }, - (8, 0, 2): {}, + (8, 0, 2): { + "docutils": "0.21.1", + }, (8, 1, 3): { + "docutils": "0.21.2", "PyYAML": "6.0.1", "sphinxcontrib-applehelp": "1.0.7", "sphinxcontrib-devhelp": "1.0.6", -- cgit v1.2.3 From 0e93f1244db7000ef752e749d202d016663c3f1c Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 22 Jun 2025 08:02:37 +0200 Subject: scripts: test_doc_build.py: improve cmd.log logic Simplify the logic which handles with new lines. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/2436f37ab7945673f26bcfc94c10e6e76b93c2d8.1750571906.git.mchehab+huawei@kernel.org --- scripts/test_doc_build.py | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'scripts') diff --git a/scripts/test_doc_build.py b/scripts/test_doc_build.py index 087b8c476f05..7ea6add48f9a 100755 --- a/scripts/test_doc_build.py +++ b/scripts/test_doc_build.py @@ -157,14 +157,16 @@ class AsyncCommands: self.fp = fp def log(self, out, verbose, is_info=True): + out = out.removesuffix('\n') + if verbose: if is_info: - print(out.rstrip("\n")) + print(out) else: - print(out.rstrip("\n"), file=sys.stderr) + print(out, file=sys.stderr) if self.fp: - self.fp.write(out.rstrip("\n") + "\n") + self.fp.write(out + "\n") async def _read(self, stream, verbose, is_info): """Ancillary routine to capture while displaying""" @@ -301,10 +303,10 @@ class SphinxVenv: make = ["make"] + args.make_args + ["htmldocs"] if args.verbose: - print(f". {bin_dir}/activate") + cmd.log(f". {bin_dir}/activate", verbose=True) await cmd.run(make, env=env, check=True, verbose=True) if args.verbose: - print("deactivate") + cmd.log("deactivate", verbose=True) end_time = time.time() elapsed_time = end_time - start_time @@ -356,10 +358,11 @@ class SphinxVenv: python_bin) if args.make: - print() - print("Summary:") + cmd = AsyncCommands(fp) + cmd.log("\nSummary:", verbose=True) for ver, elapsed_time in sorted(self.built_time.items()): - print(f"\tSphinx {ver} elapsed time: {elapsed_time}") + cmd.log(f"\tSphinx {ver} elapsed time: {elapsed_time}", + verbose=True) if fp: fp.close() -- cgit v1.2.3 From 791b9b0333742760107e8719416ced49971ec4dd Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 22 Jun 2025 08:02:38 +0200 Subject: scripts: test_doc_build.py: make the script smarter Most of the time, testing the full range of supported Sphinx version is a waste of time and resources. Instead, the best is to focus at the versions that are actually shipped by major distros. For it to work properly, we need to adjust the requirements for them to start from first patch for each distro after the minimal supported one. The requirements were re-adjusted to avoid build breakages related to version incompatibilities. Such builds were tested with: ./scripts/test_doc_build.py -m -a "SPHINXOPTS=-j8" "SPHINXDIRS=networking netlink/specs" --full Change the logic to pick by default only such versions, adding another parameter to do a comprehensive test. While here, improve the script documentation to make it easier to be used. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/a2b9b7775a185766643ea4b82b558de25b61d6c7.1750571906.git.mchehab+huawei@kernel.org --- scripts/test_doc_build.py | 154 +++++++++++++++++++++++++++++++--------------- 1 file changed, 105 insertions(+), 49 deletions(-) (limited to 'scripts') diff --git a/scripts/test_doc_build.py b/scripts/test_doc_build.py index 7ea6add48f9a..5e905a350bd0 100755 --- a/scripts/test_doc_build.py +++ b/scripts/test_doc_build.py @@ -33,7 +33,19 @@ if not min_python_bin: min_python_bin = PYTHON # Starting from 8.0, Python 3.9 is not supported anymore. -PYTHON_VER_CHANGES = {(8, 0, 2): PYTHON} +PYTHON_VER_CHANGES = {(8, 0, 0): PYTHON} + +DEFAULT_VERSIONS_TO_TEST = [ + (3, 4, 3), # Minimal supported version + (5, 3, 0), # CentOS Stream 9 / AlmaLinux 9 + (6, 1, 1), # Debian 12 + (7, 2, 1), # openSUSE Leap 15.6 + (7, 2, 6), # Ubuntu 24.04 LTS + (7, 4, 7), # Ubuntu 24.10 + (7, 3, 0), # openSUSE Tumbleweed + (8, 1, 3), # Fedora 42 + (8, 2, 3) # Latest version - covers rolling distros +] # Sphinx versions to be installed and their incremental requirements SPHINX_REQUIREMENTS = { @@ -64,82 +76,87 @@ SPHINX_REQUIREMENTS = { # Update package dependencies to a more modern base. The goal here # is to avoid to many incremental changes for the next entries - (3, 5, 4): { + (3, 5, 0): { "alabaster": "0.7.13", "babel": "2.17.0", "certifi": "2025.6.15", "idna": "3.10", "imagesize": "1.4.1", - "Jinja2": "3.0.3", - "MarkupSafe": "2.0", "packaging": "25.0", - "Pygments": "2.19.1", + "Pygments": "2.8.1", "requests": "2.32.4", "snowballstemmer": "3.0.1", "sphinxcontrib-applehelp": "1.0.4", "sphinxcontrib-htmlhelp": "2.0.1", "sphinxcontrib-serializinghtml": "1.1.5", + "urllib3": "2.0.0", }, # Starting from here, ensure all docutils versions are covered with # supported Sphinx versions. Other packages are upgraded only when # required by pip - (4, 0, 3): { - "docutils": "0.17", + (4, 0, 0): { "PyYAML": "5.1", }, - (4, 1, 2): { - }, - (4, 3, 2): { + (4, 1, 0): { + "docutils": "0.17", + "Pygments": "2.19.1", + "Jinja2": "3.0.3", + "MarkupSafe": "2.0", }, + (4, 3, 0): {}, (4, 4, 0): {}, - (4, 5, 0): {}, - (5, 0, 2): {}, - (5, 1, 1): {}, - (5, 2, 3): { + (4, 5, 0): { "docutils": "0.17.1", + }, + (5, 0, 0): {}, + (5, 1, 0): {}, + (5, 2, 0): { + "docutils": "0.18", "Jinja2": "3.1.2", "MarkupSafe": "2.0", "PyYAML": "5.3.1", }, - (5, 3, 0): {}, - (6, 0, 1): { - "docutils": "0.18", - }, - (6, 1, 3): {}, - (6, 2, 1): { + (5, 3, 0): { "docutils": "0.18.1", - "PyYAML": "5.4.1", }, - (7, 0, 1): { + (6, 0, 0): {}, + (6, 1, 0): {}, + (6, 2, 0): { + "PyYAML": "5.4.1", }, - (7, 1, 2): {}, - (7, 2, 3): { + (7, 0, 0): {}, + (7, 1, 0): {}, + (7, 2, 0): { "docutils": "0.19", "PyYAML": "6.0.1", "sphinxcontrib-serializinghtml": "1.1.9", }, - (7, 3, 7): { + (7, 2, 6): { "docutils": "0.20", + }, + (7, 3, 0): { "alabaster": "0.7.14", "PyYAML": "6.0.1", + "tomli": "2.0.1", }, - (7, 4, 7): { - "docutils": "0.21", + (7, 4, 0): { + "docutils": "0.20.1", "PyYAML": "6.0.1", }, - (8, 0, 2): { - "docutils": "0.21.1", + (8, 0, 0): { + "docutils": "0.21", }, - (8, 1, 3): { - "docutils": "0.21.2", + (8, 1, 0): { + "docutils": "0.21.1", "PyYAML": "6.0.1", "sphinxcontrib-applehelp": "1.0.7", "sphinxcontrib-devhelp": "1.0.6", "sphinxcontrib-htmlhelp": "2.0.6", "sphinxcontrib-qthelp": "1.0.6", }, - (8, 2, 3): { + (8, 2, 0): { + "docutils": "0.21.2", "PyYAML": "6.0.1", "sphinxcontrib-serializinghtml": "1.1.9", }, @@ -339,13 +356,19 @@ class SphinxVenv: cur_requirements = {} python_bin = min_python_bin - for cur_ver, new_reqs in SPHINX_REQUIREMENTS.items(): - cur_requirements.update(new_reqs) + vers = set(SPHINX_REQUIREMENTS.keys()) | set(args.versions) - if cur_ver in PYTHON_VER_CHANGES: # pylint: disable=R1715 + for cur_ver in sorted(vers): + if cur_ver in SPHINX_REQUIREMENTS: + new_reqs = SPHINX_REQUIREMENTS[cur_ver] + cur_requirements.update(new_reqs) + if cur_ver in PYTHON_VER_CHANGES: # pylint: disable=R1715 python_bin = PYTHON_VER_CHANGES[cur_ver] + if cur_ver not in args.versions: + continue + if args.min_version: if cur_ver < args.min_version: continue @@ -373,12 +396,52 @@ def parse_version(ver_str): return tuple(map(int, ver_str.split("."))) +DEFAULT_VERS = " - " +DEFAULT_VERS += "\n - ".join(map(lambda v: f"{v[0]}.{v[1]}.{v[2]}", + DEFAULT_VERSIONS_TO_TEST)) + +SCRIPT = os.path.relpath(__file__) + +DESCRIPTION = f""" +This tool allows creating Python virtual environments for different +Sphinx versions that are supported by the Linux Kernel build system. + +Besides creating the virtual environment, it can also test building +the documentation using "make htmldocs". + +If called without "--versions" argument, it covers the versions shipped +on major distros, plus the lowest supported version: + +{DEFAULT_VERS} + +A typical usage is to run: + + {SCRIPT} -m -l sphinx_builds.log + +This will create one virtual env for the default version set and do a +full htmldocs build for each version, creating a log file with the +excecuted commands on it. + +NOTE: The build time can be very long, specially on old versions. Also, there +is a known bug with Sphinx version 6.0.x: each subprocess uses a lot of +memory. That, together with "-jauto" may cause OOM killer to cause +failures at the doc generation. To minimize the risk, you may use the +"-a" command line parameter to constrain the built directories and/or +reduce the number of threads from "-jauto" to, for instance, "-j4": + + {SCRIPT} -m -V 6.0.1 -a "SPHINXDIRS=process" "SPHINXOPTS='-j4'" + +""" + + async def main(): """Main program""" - parser = argparse.ArgumentParser(description="Build docs for different sphinx_versions.") + parser = argparse.ArgumentParser(description=DESCRIPTION, + formatter_class=argparse.RawDescriptionHelpFormatter) - parser.add_argument('-V', '--version', help='Sphinx single version', + parser.add_argument('-V', '--versions', help='Sphinx versions to test', + nargs="*", default=DEFAULT_VERSIONS_TO_TEST, type=parse_version) parser.add_argument('--min-version', "--min", help='Sphinx minimal version', type=parse_version) @@ -392,6 +455,9 @@ async def main(): parser.add_argument('-m', '--make', help='Make documentation', action='store_true') + parser.add_argument('-f', '--full', + help='Add all (major,minor,latest_patch) version to the version list', + action='store_true') parser.add_argument('-i', '--wait-input', help='Wait for an enter before going to the next version', action='store_true') @@ -406,20 +472,10 @@ async def main(): if not args.make_args: args.make_args = [] - if args.version: - if args.min_version or args.max_version: - sys.exit("Use either --version or --min-version/--max-version") - else: - args.min_version = args.version - args.max_version = args.version - sphinx_versions = sorted(list(SPHINX_REQUIREMENTS.keys())) - if not args.min_version: - args.min_version = sphinx_versions[0] - - if not args.max_version: - args.max_version = sphinx_versions[-1] + if args.full: + args.versions += list(SPHINX_REQUIREMENTS.keys()) venv = SphinxVenv() await venv.run(args) -- cgit v1.2.3 From c6b5b1559c0c7bcb5fe9b390d1785adf9e3bc83f Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 22 Jun 2025 08:02:39 +0200 Subject: scripts: sphinx-pre-install: properly handle SPHINXBUILD Currently, the script ignores SPHINXBUILD, making it useless. As we're about to use on another script, fix support for it. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/b0217df871a5e563646d386327bdd7a393c58ac2.1750571906.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install index ad9945ccb0cf..2a311ed00179 100755 --- a/scripts/sphinx-pre-install +++ b/scripts/sphinx-pre-install @@ -245,6 +245,10 @@ sub check_missing_tex($) sub get_sphinx_fname() { + if ($ENV{'SPHINXBUILD'}) { + return $ENV{'SPHINXBUILD'}; + } + my $fname = "sphinx-build"; return $fname if findprog($fname); -- cgit v1.2.3 From 61aeda1e5c0d6ae128736ec4d6a082ee4dd9f04e Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 22 Jun 2025 08:02:40 +0200 Subject: scripts: sphinx-pre-install: fix release detection for Fedora Fedora distros are now identified as: Fedora Linux 42 Fix the way script detects it. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/c2a34860bd986cc5f81fc25554ed91629736e995.1750571906.git.mchehab+huawei@kernel.org --- scripts/sphinx-pre-install | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install index 2a311ed00179..3f8d6925e896 100755 --- a/scripts/sphinx-pre-install +++ b/scripts/sphinx-pre-install @@ -413,7 +413,7 @@ sub give_redhat_hints() my $old = 0; my $rel; my $noto_sans_redhat = "google-noto-sans-cjk-ttc-fonts"; - $rel = $1 if ($system_release =~ /release\s+(\d+)/); + $rel = $1 if ($system_release =~ /(release|Linux)\s+(\d+)/); if (!($system_release =~ /Fedora/)) { $map{"virtualenv"} = "python-virtualenv"; -- cgit v1.2.3 From bb4c5c50aeeca230428e77f57b60f3a9ef85ca9b Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Sun, 22 Jun 2025 08:02:41 +0200 Subject: scripts: test_doc_build.py: regroup and rename arguments The script now have lots or arguments. Better organize and name them, for it to be a little bit more intuitive. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/acf5e1db38ca6a713c44ceca9db5cdd7d3079c92.1750571906.git.mchehab+huawei@kernel.org --- scripts/test_doc_build.py | 95 ++++++++++++++++++++++++++++++----------------- 1 file changed, 61 insertions(+), 34 deletions(-) (limited to 'scripts') diff --git a/scripts/test_doc_build.py b/scripts/test_doc_build.py index 5e905a350bd0..47b4606569f9 100755 --- a/scripts/test_doc_build.py +++ b/scripts/test_doc_build.py @@ -269,7 +269,7 @@ class SphinxVenv: ver = ".".join(map(str, cur_ver)) - if not self.first_run and args.wait_input and args.make: + if not self.first_run and args.wait_input and args.build: ret = input("Press Enter to continue or 'a' to abort: ").strip().lower() if ret == "a": print("Aborted.") @@ -300,11 +300,11 @@ class SphinxVenv: result = await cmd.run([pip, "freeze"], verbose=False, check=True) # Pip install succeeded. Write requirements file - if args.write: + if args.req_file: with open(req_file, "w", encoding="utf-8") as fp: fp.write(result.stdout) - if args.make: + if args.build: start_time = time.time() # Prepare a venv environment @@ -317,7 +317,16 @@ class SphinxVenv: # Test doc build await cmd.run(["make", "cleandocs"], env=env, check=True) - make = ["make"] + args.make_args + ["htmldocs"] + make = ["make"] + + if args.output: + sphinx_build = os.path.realpath(f"{bin_dir}/sphinx-build") + make += [f"O={args.output}", f"SPHINXBUILD={sphinx_build}"] + + if args.make_args: + make += args.make_args + + make += args.targets if args.verbose: cmd.log(f". {bin_dir}/activate", verbose=True) @@ -380,7 +389,7 @@ class SphinxVenv: await self._handle_version(args, fp, cur_ver, cur_requirements, python_bin) - if args.make: + if args.build: cmd = AsyncCommands(fp) cmd.log("\nSummary:", verbose=True) for ver, elapsed_time in sorted(self.built_time.items()): @@ -407,7 +416,7 @@ This tool allows creating Python virtual environments for different Sphinx versions that are supported by the Linux Kernel build system. Besides creating the virtual environment, it can also test building -the documentation using "make htmldocs". +the documentation using "make htmldocs" (and/or other doc targets). If called without "--versions" argument, it covers the versions shipped on major distros, plus the lowest supported version: @@ -418,8 +427,8 @@ A typical usage is to run: {SCRIPT} -m -l sphinx_builds.log -This will create one virtual env for the default version set and do a -full htmldocs build for each version, creating a log file with the +This will create one virtual env for the default version set and run +"make htmldocs" for each version, creating a log file with the excecuted commands on it. NOTE: The build time can be very long, specially on old versions. Also, there @@ -433,6 +442,15 @@ reduce the number of threads from "-jauto" to, for instance, "-j4": """ +MAKE_TARGETS = [ + "htmldocs", + "texinfodocs", + "infodocs", + "latexdocs", + "pdfdocs", + "epubdocs", + "xmldocs", +] async def main(): """Main program""" @@ -440,32 +458,41 @@ async def main(): parser = argparse.ArgumentParser(description=DESCRIPTION, formatter_class=argparse.RawDescriptionHelpFormatter) - parser.add_argument('-V', '--versions', help='Sphinx versions to test', - nargs="*", default=DEFAULT_VERSIONS_TO_TEST, - type=parse_version) - parser.add_argument('--min-version', "--min", help='Sphinx minimal version', - type=parse_version) - parser.add_argument('--max-version', "--max", help='Sphinx maximum version', - type=parse_version) - parser.add_argument('-a', '--make_args', - help='extra arguments for make htmldocs, like SPHINXDIRS=netlink/specs', - nargs="*") - parser.add_argument('-w', '--write', help='write a requirements.txt file', - action='store_true') - parser.add_argument('-m', '--make', - help='Make documentation', - action='store_true') - parser.add_argument('-f', '--full', - help='Add all (major,minor,latest_patch) version to the version list', - action='store_true') - parser.add_argument('-i', '--wait-input', - help='Wait for an enter before going to the next version', - action='store_true') - parser.add_argument('-v', '--verbose', - help='Verbose all commands', - action='store_true') - parser.add_argument('-l', '--log', - help='Log command output on a file') + ver_group = parser.add_argument_group("Version range options") + + ver_group.add_argument('-V', '--versions', nargs="*", + default=DEFAULT_VERSIONS_TO_TEST,type=parse_version, + help='Sphinx versions to test') + ver_group.add_argument('--min-version', "--min", type=parse_version, + help='Sphinx minimal version') + ver_group.add_argument('--max-version', "--max", type=parse_version, + help='Sphinx maximum version') + ver_group.add_argument('-f', '--full', action='store_true', + help='Add all Sphinx (major,minor) supported versions to the version range') + + build_group = parser.add_argument_group("Build options") + + build_group.add_argument('-b', '--build', action='store_true', + help='Build documentation') + build_group.add_argument('-a', '--make-args', nargs="*", + help='extra arguments for make, like SPHINXDIRS=netlink/specs', + ) + build_group.add_argument('-t', '--targets', nargs="+", choices=MAKE_TARGETS, + default=[MAKE_TARGETS[0]], + help="make build targets. Default: htmldocs.") + build_group.add_argument("-o", '--output', + help="output directory for the make O=OUTPUT") + + other_group = parser.add_argument_group("Other options") + + other_group.add_argument('-r', '--req-file', action='store_true', + help='write a requirements.txt file') + other_group.add_argument('-l', '--log', + help='Log command output on a file') + other_group.add_argument('-v', '--verbose', action='store_true', + help='Verbose all commands') + other_group.add_argument('-i', '--wait-input', action='store_true', + help='Wait for an enter before going to the next version') args = parser.parse_args() -- cgit v1.2.3 From d982828d08b63c2c56f83c09b33cb71929fd4c22 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Wed, 25 Jun 2025 14:08:40 -0600 Subject: docs: kdoc: remove KernelEntry::in_doc_sect This field is not used for anything, just get rid of it. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 3557c512c85a..f3970ffbf402 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -151,8 +151,6 @@ class KernelEntry: # State flags self.brcount = 0 - - self.in_doc_sect = False self.declaration_start_line = ln + 1 # TODO: rename to emit_message after removal of kernel-doc.pl @@ -1227,7 +1225,6 @@ class KernelDoc: # start a new entry self.reset_state(ln) - self.entry.in_doc_sect = False # next line is always the function name self.state = state.NAME @@ -1315,7 +1312,6 @@ class KernelDoc: # def is_new_section(self, ln, line): if doc_sect.search(line): - self.entry.in_doc_sect = True self.state = state.BODY # # Pick out the name of our new section, tweaking it if need be. -- cgit v1.2.3 From d6699d5f601670176bd03f95d1680914bd65b2a9 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Wed, 25 Jun 2025 14:51:11 -0600 Subject: docs: kdoc: Move content handling into KernelEntry Rather than having other code mucking around with this bit of internal state, encapsulate it internally. Accumulate the description as a list of strings, joining them at the end, which is a more efficient way of building the text. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 62 ++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 32 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index f3970ffbf402..f87355b63c19 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -128,7 +128,7 @@ class KernelEntry: def __init__(self, config, ln): self.config = config - self.contents = "" + self._contents = [] self.function = "" self.sectcheck = "" self.struct_actual = "" @@ -153,6 +153,15 @@ class KernelEntry: self.brcount = 0 self.declaration_start_line = ln + 1 + # + # Management of section contents + # + def add_text(self, text): + self._contents.append(text) + + def contents(self): + return '\n'.join(self._contents) + '\n' + # TODO: rename to emit_message after removal of kernel-doc.pl def emit_msg(self, log_msg, warning=True): """Emit a message""" @@ -180,9 +189,14 @@ class KernelEntry: """ Dumps section contents to arrays/hashes intended for that purpose. """ - + # + # If we have accumulated no contents in the default ("description") + # section, don't bother. + # + if self.section == SECTION_DEFAULT and not self._contents: + return name = self.section - contents = self.contents + contents = self.contents() if type_param.match(name): name = type_param.group(1) @@ -206,7 +220,8 @@ class KernelEntry: if name != SECTION_DEFAULT: self.emit_msg(self.new_start_line, f"duplicate section name '{name}'\n") - self.sections[name] += contents + # Treat as a new paragraph - add a blank line + self.sections[name] += '\n' + contents else: self.sections[name] = contents self.sectionlist.append(name) @@ -217,7 +232,7 @@ class KernelEntry: if start_new: self.section = SECTION_DEFAULT - self.contents = "" + self._contents = [] class KernelDoc: @@ -1334,16 +1349,11 @@ class KernelDoc: newcontents = doc_sect.group(2) if not newcontents: newcontents = "" - - if self.entry.contents.strip("\n"): - self.dump_section() - + self.dump_section() self.entry.begin_section(ln, newsection) self.entry.leading_space = None - self.entry.contents = newcontents.lstrip() - if self.entry.contents: - self.entry.contents += "\n" + self.entry.add_text(newcontents.lstrip()) return True return False @@ -1385,7 +1395,6 @@ class KernelDoc: # if cont == "": self.state = state.BODY - self.entry.contents += "\n" # needed? # # Otherwise we have more of the declaration section to soak up. # @@ -1407,7 +1416,6 @@ class KernelDoc: # if KernRe(r"\s*\*\s*$").match(line): self.entry.begin_section(ln, dump = True) - self.entry.contents += '\n' self.state = state.BODY return # @@ -1444,7 +1452,7 @@ class KernelDoc: # # Add the trimmed result to the section and we're done. # - self.entry.contents += cont[self.entry.leading_space:] + '\n' + self.entry.add_text(cont[self.entry.leading_space:]) else: # Unknown line, ignore self.emit_msg(ln, f"bad line: {line}") @@ -1458,7 +1466,7 @@ class KernelDoc: if doc_content.search(line): cont = doc_content.group(1) - self.entry.contents += cont + "\n" + self.entry.add_text(cont) else: # Unknown line, ignore self.emit_msg(ln, f"bad line: {line}") @@ -1470,27 +1478,20 @@ class KernelDoc: doc_inline_sect.search(line): self.entry.begin_section(ln, doc_inline_sect.group(1)) - self.entry.contents = doc_inline_sect.group(2).lstrip() - if self.entry.contents != "": - self.entry.contents += "\n" - + self.entry.add_text(doc_inline_sect.group(2).lstrip()) self.inline_doc_state = state.INLINE_TEXT # Documentation block end */ return if doc_inline_end.search(line): - if self.entry.contents not in ["", "\n"]: - self.dump_section() - + self.dump_section() self.state = state.PROTO self.inline_doc_state = state.INLINE_NA return if doc_content.search(line): if self.inline_doc_state == state.INLINE_TEXT: - self.entry.contents += doc_content.group(1) + "\n" - if not self.entry.contents.strip(" ").rstrip("\n"): - self.entry.contents = "" + self.entry.add_text(doc_content.group(1)) elif self.inline_doc_state == state.INLINE_NAME: self.emit_msg(ln, @@ -1668,11 +1669,8 @@ class KernelDoc: if doc_inline_oneline.search(line): self.entry.begin_section(ln, doc_inline_oneline.group(1)) - self.entry.contents = doc_inline_oneline.group(2) - - if self.entry.contents != "": - self.entry.contents += "\n" - self.dump_section(start_new=False) + self.entry.add_text(doc_inline_oneline.group(2)) + self.dump_section() elif doc_inline_start.search(line): self.state = state.INLINE @@ -1696,7 +1694,7 @@ class KernelDoc: self.reset_state(ln) elif doc_content.search(line): - self.entry.contents += doc_content.group(1) + "\n" + self.entry.add_text(doc_content.group(1)) def parse_export(self): """ -- cgit v1.2.3 From 1550a409e778673a63a6957718b802050f98359a Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Wed, 25 Jun 2025 15:43:37 -0600 Subject: docs: kdoc: remove a bit of dead code The type_param regex matches "@..." just fine, so the special-case branch for that in dump_section() is never executed. Just remove it. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 7 ------- 1 file changed, 7 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index f87355b63c19..9e46cfa20978 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -207,13 +207,6 @@ class KernelEntry: self.sectcheck += name + " " self.new_start_line = 0 - elif name == "@...": - name = "..." - self.parameterdescs[name] = contents - self.sectcheck += name + " " - self.parameterdesc_start_lines[name] = self.new_start_line - self.new_start_line = 0 - else: if name in self.sections and self.sections[name] != "": # Only warn on user-specified duplicate section names -- cgit v1.2.3 From 79300ac805b672a84b64d80d4cbc374d83411599 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Thu, 19 Jun 2025 15:51:05 -0700 Subject: scripts/gdb: fix dentry_name() lookup The "d_iname" member was replaced with "d_shortname.string" in the commit referenced in the Fixes tag. This prevented the GDB script "lx-mount" command to properly function: (gdb) lx-mounts mount super_block devname pathname fstype options 0xff11000002d21180 0xff11000002d24800 rootfs / rootfs rw 0 0 0xff11000002e18a80 0xff11000003713000 /dev/root / ext4 rw,relatime 0 0 Python Exception : There is no member named d_iname. Error occurred in Python: There is no member named d_iname. Link: https://lkml.kernel.org/r/20250619225105.320729-1-florian.fainelli@broadcom.com Fixes: 58cf9c383c5c ("dcache: back inline names with a struct-wrapped array of unsigned long") Signed-off-by: Florian Fainelli Cc: Al Viro Cc: Jan Kara Cc: Jan Kiszka Cc: Jeff Layton Cc: Kieran Bingham Cc: Signed-off-by: Andrew Morton --- scripts/gdb/linux/vfs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/gdb/linux/vfs.py b/scripts/gdb/linux/vfs.py index c77b9ce75f6d..b5fbb18ccb77 100644 --- a/scripts/gdb/linux/vfs.py +++ b/scripts/gdb/linux/vfs.py @@ -22,7 +22,7 @@ def dentry_name(d): if parent == d or parent == 0: return "" p = dentry_name(d['d_parent']) + "/" - return p + d['d_iname'].string() + return p + d['d_shortname']['string'].string() class DentryName(gdb.Function): """Return string of the full path of a dentry. -- cgit v1.2.3 From f61e404f5b6124905025dbda58afa1fd3171100f Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Wed, 25 Jun 2025 16:58:55 -0600 Subject: docs: kdoc: remove KernelEntry::function This member is unused, to take it out. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 1 - 1 file changed, 1 deletion(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 9e46cfa20978..224dea5f7c2e 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -129,7 +129,6 @@ class KernelEntry: self.config = config self._contents = [] - self.function = "" self.sectcheck = "" self.struct_actual = "" self.prototype = "" -- cgit v1.2.3 From 473734e086ccdd50af9d0abf81c0b70085dcf625 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Wed, 25 Jun 2025 17:19:40 -0600 Subject: docs: kdoc: rework process_export() slightly Reorganize process_export() to eliminate duplicated code, don't look for exports in states where we don't expect them, and don't bother with normal state-machine processing if an export declaration has been found. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 224dea5f7c2e..734b908579c3 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1211,16 +1211,17 @@ class KernelDoc: if export_symbol.search(line): symbol = export_symbol.group(2) - for suffix in suffixes: - symbol = symbol.removesuffix(suffix) - function_set.add(symbol) - return - - if export_symbol_ns.search(line): + elif export_symbol_ns.search(line): symbol = export_symbol_ns.group(2) - for suffix in suffixes: - symbol = symbol.removesuffix(suffix) - function_set.add(symbol) + else: + return False + # + # Found an export, trim out any special suffixes + # + for suffix in suffixes: + symbol = symbol.removesuffix(suffix) + function_set.add(symbol) + return True def process_normal(self, ln, line): """ @@ -1767,13 +1768,10 @@ class KernelDoc: # it was read twice. Here, we use the already-existing # loop to parse exported symbols as well. # - # TODO: It should be noticed that not all states are - # needed here. On a future cleanup, process export only - # at the states that aren't handling comment markups. - self.process_export(export_table, line) - - # Hand this line to the appropriate state handler - self.state_actions[self.state](self, ln, line) + if (self.state != state.NORMAL) or \ + not self.process_export(export_table, line): + # Hand this line to the appropriate state handler + self.state_actions[self.state](self, ln, line) except OSError: self.config.log.error(f"Error: Cannot open file {self.fname}") -- cgit v1.2.3 From e02adac7c84bf2883ce5d5a828a03871c0c1d4f9 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Tue, 24 Jun 2025 12:53:34 +0200 Subject: checkpatch: check for comment explaining rgmii(|-rxid|-txid) PHY modes Historically, the RGMII PHY modes specified in Device Trees have been used inconsistently, often referring to the usage of delays on the PHY side rather than describing the board; many drivers still implement this incorrectly. Require a comment in Devices Trees using these modes (usually mentioning that the delay is realized on the PCB), so we can avoid adding more incorrect uses (or will at least notice which drivers still need to be fixed). Suggested-by: Andrew Lunn Signed-off-by: Matthias Schiffer Reviewed-by: Andrew Lunn Link: https://patch.msgid.link/bc112b8aa510cf9df9ab33178d122f234d0aebf7.1750756583.git.matthias.schiffer@ew.tq-group.com Signed-off-by: Paolo Abeni --- Documentation/dev-tools/checkpatch.rst | 9 +++++++++ scripts/checkpatch.pl | 12 ++++++++++++ 2 files changed, 21 insertions(+) (limited to 'scripts') diff --git a/Documentation/dev-tools/checkpatch.rst b/Documentation/dev-tools/checkpatch.rst index 76bd0ddb0041..d5c47e560324 100644 --- a/Documentation/dev-tools/checkpatch.rst +++ b/Documentation/dev-tools/checkpatch.rst @@ -495,6 +495,15 @@ Comments See: https://lore.kernel.org/lkml/20131006222342.GT19510@leaf/ + **UNCOMMENTED_RGMII_MODE** + Historically, the RGMII PHY modes specified in Device Trees have been + used inconsistently, often referring to the usage of delays on the PHY + side rather than describing the board. + + PHY modes "rgmii", "rgmii-rxid" and "rgmii-txid" modes require the clock + signal to be delayed on the PCB; this unusual configuration should be + described in a comment. If they are not (meaning that the delay is realized + internally in the MAC or PHY), "rgmii-id" is the correct PHY mode. Commit message -------------- diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 664f7b7a622c..f597734d83cc 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3741,6 +3741,18 @@ sub process { } } +# Check for RGMII phy-mode with delay on PCB + if ($realfile =~ /\.(dts|dtsi|dtso)$/ && + $line =~ /^\+\s*(phy-mode|phy-connection-type)\s*=\s*"/ && + !ctx_has_comment($first_line, $linenr)) { + my $prop = $1; + my $mode = get_quoted_string($line, $rawline); + if ($mode =~ /^"rgmii(?:|-rxid|-txid)"$/) { + WARN("UNCOMMENTED_RGMII_MODE", + "$prop $mode without comment -- delays on the PCB should be described, otherwise use \"rgmii-id\"\n" . $herecurr); + } + } + # check for using SPDX license tag at beginning of files if ($realline == $checklicenseline) { if ($rawline =~ /^[ \+]\s*\#\!\s*\//) { -- cgit v1.2.3 From dd49aae52b5e03bc151c65f0e8ee1731fdd73c0a Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 26 Jun 2025 13:38:05 -0600 Subject: docs: kdoc: remove the INLINE_END state It is never used, so just get rid of it. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 734b908579c3..03a0e44707a7 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -108,8 +108,7 @@ class state: INLINE_NA = 0 # not applicable ($state != INLINE) INLINE_NAME = 1 # looking for member name (@foo:) INLINE_TEXT = 2 # looking for member documentation - INLINE_END = 3 # done - INLINE_ERROR = 4 # error - Comment without header was found. + INLINE_ERROR = 3 # error - Comment without header was found. # Spit a warning as it's not # proper kernel-doc and ignore the rest. @@ -117,7 +116,6 @@ class state: "", "_NAME", "_TEXT", - "_END", "_ERROR", ] -- cgit v1.2.3 From b04c7e88bcf5ddcd15e2c620b802c28848f437bb Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:49 +0900 Subject: kconfig: set MENU_CHANGED to choice when the selected member is changed In gconf, choice entries display the selected symbol in the 'Value' column, but it is not updated when the selected symbol is changed. Set the MENU_CHANGED flag, so it is updated. Signed-off-by: Masahiro Yamada --- scripts/kconfig/symbol.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'scripts') diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index d57f8cbba291..26ab10c0fd76 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c @@ -195,6 +195,10 @@ static void sym_set_changed(struct symbol *sym) list_for_each_entry(menu, &sym->menus, link) menu->flags |= MENU_CHANGED; + + menu = sym_get_choice_menu(sym); + if (menu) + menu->flags |= MENU_CHANGED; } static void sym_set_all_changed(void) -- cgit v1.2.3 From 096f73ab01b95aaeaa7f678c56257d2e4c8490d3 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 27 Jun 2025 11:33:18 -0600 Subject: docs: kdoc: remove the inline states-within-a-state The processing of inline kerneldoc comments is a state like the rest, but it was implemented as a set of separate substates. Just remove the substate logic and make the inline states normal ones like the rest. INLINE_ERROR was never actually used for anything, so just take it out. No changes to the generated output. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 43 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 30 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 03a0e44707a7..a931c1471fa8 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -91,7 +91,8 @@ class state: SPECIAL_SECTION = 4 # doc section ending with a blank line PROTO = 5 # scanning prototype DOCBLOCK = 6 # documentation block - INLINE = 7 # gathering doc outside main block + INLINE_NAME = 7 # gathering doc outside main block + INLINE_TEXT = 8 # reading the body of inline docs name = [ "NORMAL", @@ -101,23 +102,10 @@ class state: "SPECIAL_SECTION", "PROTO", "DOCBLOCK", - "INLINE", + "INLINE_NAME", + "INLINE_TEXT", ] - # Inline documentation state - INLINE_NA = 0 # not applicable ($state != INLINE) - INLINE_NAME = 1 # looking for member name (@foo:) - INLINE_TEXT = 2 # looking for member documentation - INLINE_ERROR = 3 # error - Comment without header was found. - # Spit a warning as it's not - # proper kernel-doc and ignore the rest. - - inline_name = [ - "", - "_NAME", - "_TEXT", - "_ERROR", - ] SECTION_DEFAULT = "Description" # default section @@ -246,7 +234,6 @@ class KernelDoc: # Initial state for the state machines self.state = state.NORMAL - self.inline_doc_state = state.INLINE_NA # Store entry currently being processed self.entry = None @@ -323,7 +310,6 @@ class KernelDoc: # State flags self.state = state.NORMAL - self.inline_doc_state = state.INLINE_NA def push_parameter(self, ln, decl_type, param, dtype, org_arg, declaration_name): @@ -1465,30 +1451,28 @@ class KernelDoc: def process_inline(self, ln, line): """STATE_INLINE: docbook comments within a prototype.""" - if self.inline_doc_state == state.INLINE_NAME and \ + if self.state == state.INLINE_NAME and \ doc_inline_sect.search(line): self.entry.begin_section(ln, doc_inline_sect.group(1)) self.entry.add_text(doc_inline_sect.group(2).lstrip()) - self.inline_doc_state = state.INLINE_TEXT + self.state = state.INLINE_TEXT # Documentation block end */ return if doc_inline_end.search(line): self.dump_section() self.state = state.PROTO - self.inline_doc_state = state.INLINE_NA return if doc_content.search(line): - if self.inline_doc_state == state.INLINE_TEXT: + if self.state == state.INLINE_TEXT: self.entry.add_text(doc_content.group(1)) - elif self.inline_doc_state == state.INLINE_NAME: + elif self.state == state.INLINE_NAME: self.emit_msg(ln, f"Incorrect use of kernel-doc format: {line}") - - self.inline_doc_state = state.INLINE_ERROR + self.state = state.PROTO def syscall_munge(self, ln, proto): # pylint: disable=W0613 """ @@ -1664,8 +1648,7 @@ class KernelDoc: self.dump_section() elif doc_inline_start.search(line): - self.state = state.INLINE - self.inline_doc_state = state.INLINE_NAME + self.state = state.INLINE_NAME elif self.entry.decl_type == 'function': self.process_proto_function(ln, line) @@ -1716,7 +1699,8 @@ class KernelDoc: state.BODY: process_body, state.DECLARATION: process_decl, state.SPECIAL_SECTION: process_special, - state.INLINE: process_inline, + state.INLINE_NAME: process_inline, + state.INLINE_TEXT: process_inline, state.PROTO: process_proto, state.DOCBLOCK: process_docblock, } @@ -1756,9 +1740,8 @@ class KernelDoc: prev = "" prev_ln = None - self.config.log.debug("%d %s%s: %s", + self.config.log.debug("%d %s: %s", ln, state.name[self.state], - state.inline_name[self.inline_doc_state], line) # This is an optimization over the original script. -- cgit v1.2.3 From c7eedb09417e4372183bf1843676d2008da340d5 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 27 Jun 2025 12:23:05 -0600 Subject: docs: kdoc: split the processing of the two remaining inline states Now that "inline_*" are just ordinary parser states, split them into two separate functions, getting rid of some nested conditional logic. The original process_inline() would simply ignore lines that didn't match any of the regexes (those lacking the initial " * " marker). I have preserved that behavior, but we should perhaps emit a warning instead. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index a931c1471fa8..93938155fce2 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1448,31 +1448,30 @@ class KernelDoc: # Unknown line, ignore self.emit_msg(ln, f"bad line: {line}") - def process_inline(self, ln, line): - """STATE_INLINE: docbook comments within a prototype.""" + def process_inline_name(self, ln, line): + """STATE_INLINE_NAME: beginning of docbook comments within a prototype.""" - if self.state == state.INLINE_NAME and \ - doc_inline_sect.search(line): + if doc_inline_sect.search(line): self.entry.begin_section(ln, doc_inline_sect.group(1)) - self.entry.add_text(doc_inline_sect.group(2).lstrip()) self.state = state.INLINE_TEXT - # Documentation block end */ - return - - if doc_inline_end.search(line): + elif doc_inline_end.search(line): self.dump_section() self.state = state.PROTO - return + elif doc_content.search(line): + self.emit_msg(ln, f"Incorrect use of kernel-doc format: {line}") + self.state = state.PROTO + # else ... ?? - if doc_content.search(line): - if self.state == state.INLINE_TEXT: - self.entry.add_text(doc_content.group(1)) + def process_inline_text(self, ln, line): + """STATE_INLINE_TEXT: docbook comments within a prototype.""" - elif self.state == state.INLINE_NAME: - self.emit_msg(ln, - f"Incorrect use of kernel-doc format: {line}") - self.state = state.PROTO + if doc_inline_end.search(line): + self.dump_section() + self.state = state.PROTO + elif doc_content.search(line): + self.entry.add_text(doc_content.group(1)) + # else ... ?? def syscall_munge(self, ln, proto): # pylint: disable=W0613 """ @@ -1699,8 +1698,8 @@ class KernelDoc: state.BODY: process_body, state.DECLARATION: process_decl, state.SPECIAL_SECTION: process_special, - state.INLINE_NAME: process_inline, - state.INLINE_TEXT: process_inline, + state.INLINE_NAME: process_inline_name, + state.INLINE_TEXT: process_inline_text, state.PROTO: process_proto, state.DOCBLOCK: process_docblock, } -- cgit v1.2.3 From 362ec251a6aba32c8d950f0278c75aaa8c1b0b10 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 27 Jun 2025 13:08:20 -0600 Subject: docs: kdoc: don't reinvent string.strip() process_proto_type() and process_proto_function() reinventing the strip() string method with a whole series of separate regexes; take all that out and just use strip(). The previous implementation also (in process_proto_type()) removed C++ comments *after* the above dance, leaving trailing whitespace in that case; now we do the stripping afterward. This results in exactly one output change: the removal of a spurious space in the definition of BACKLIGHT_POWER_REDUCED - see https://docs.kernel.org/gpu/backlight.html#c.backlight_properties. I note that we are putting semicolons after #define lines that really shouldn't be there - a task for another day. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 93938155fce2..d9ff2d066160 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1567,17 +1567,9 @@ class KernelDoc: self.entry.prototype += r.group(1) + " " if '{' in line or ';' in line or KernRe(r'\s*#\s*define').match(line): - # strip comments - r = KernRe(r'/\*.*?\*/') - self.entry.prototype = r.sub('', self.entry.prototype) - - # strip newlines/cr's - r = KernRe(r'[\r\n]+') - self.entry.prototype = r.sub(' ', self.entry.prototype) - - # strip leading spaces - r = KernRe(r'^\s+') - self.entry.prototype = r.sub('', self.entry.prototype) + # strip comments and surrounding spaces + r = KernRe(r'/\*.*\*/') + self.entry.prototype = r.sub('', self.entry.prototype).strip() # Handle self.entry.prototypes for function pointers like: # int (*pcs_config)(struct foo) @@ -1600,17 +1592,8 @@ class KernelDoc: def process_proto_type(self, ln, line): """Ancillary routine to process a type""" - # Strip newlines/cr's. - line = KernRe(r'[\r\n]+', re.S).sub(' ', line) - - # Strip leading spaces - line = KernRe(r'^\s+', re.S).sub('', line) - - # Strip trailing spaces - line = KernRe(r'\s+$', re.S).sub('', line) - - # Strip C99-style comments to the end of the line - line = KernRe(r"\/\/.*$", re.S).sub('', line) + # Strip C99-style comments and surrounding whitespace + line = KernRe(r"//.*$", re.S).sub('', line).strip() # To distinguish preprocessor directive from regular declaration later. if line.startswith('#'): -- cgit v1.2.3 From cd5f1534a37e0b05733a8714195ec90474c20e82 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:50 +0900 Subject: kconfig: qconf: do not show checkbox icon for choice When you select "Show All Options" or "Show Prompt Options", choice entries display a check box icon, but this has no point because choice is always y since commit fde192511bdb ("kconfig: remove tristate choice support"). Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/qconf.cc | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index eaa465b0ccf9..546738a5c3b1 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -92,7 +92,6 @@ void ConfigItem::updateMenu(void) { ConfigList* list; struct symbol* sym; - struct property *prop; QString prompt; int type; tristate expr; @@ -105,11 +104,10 @@ void ConfigItem::updateMenu(void) } sym = menu->sym; - prop = menu->prompt; prompt = menu_get_prompt(menu); - if (prop) switch (prop->type) { - case P_MENU: + switch (menu->type) { + case M_MENU: if (list->mode == singleMode) { /* a menuconfig entry is displayed differently * depending whether it's at the view root or a child. @@ -123,10 +121,13 @@ void ConfigItem::updateMenu(void) setIcon(promptColIdx, QIcon()); } goto set_prompt; - case P_COMMENT: + case M_COMMENT: setIcon(promptColIdx, QIcon()); prompt = "*** " + prompt + " ***"; goto set_prompt; + case M_CHOICE: + setIcon(promptColIdx, QIcon()); + goto set_prompt; default: ; } -- cgit v1.2.3 From 604f5b2127fb76e15dcc6dabbd73b541817a2fba Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:51 +0900 Subject: kconfig: qconf: show selected choice in the Value column It is useful to display the selected choice's value in the Value column. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/qconf.cc | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index 546738a5c3b1..68640e507ec4 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -127,6 +127,9 @@ void ConfigItem::updateMenu(void) goto set_prompt; case M_CHOICE: setIcon(promptColIdx, QIcon()); + sym = sym_calc_choice(menu); + if (sym) + setText(dataColIdx, sym->name); goto set_prompt; default: ; @@ -189,7 +192,11 @@ void ConfigItem::testUpdateMenu(void) if (!menu) return; - sym_calc_value(menu->sym); + if (menu->type == M_CHOICE) + sym_calc_choice(menu); + else + sym_calc_value(menu->sym); + if (menu->flags & MENU_CHANGED) { /* the menu entry changed, so update all list items */ menu->flags &= ~MENU_CHANGED; -- cgit v1.2.3 From 3c292cd0047c8758a2db7a44e441314e78b4db00 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:52 +0900 Subject: kconfig: rename menu_get_parent_menu() to menu_get_menu_or_parent_menu() The current menu_get_parent_menu() does not always return the parent menu; if the given argument is itself a menu, it returns that menu. Rename this function to better reflect this behavior. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/conf.c | 2 +- scripts/kconfig/lkc.h | 2 +- scripts/kconfig/menu.c | 8 +++++++- scripts/kconfig/qconf.cc | 6 +++--- 4 files changed, 12 insertions(+), 6 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index 8abe57041955..a7b44cd8ae14 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c @@ -594,7 +594,7 @@ static void check_conf(struct menu *menu) default: if (!conf_cnt++) printf("*\n* Restart config...\n*\n"); - rootEntry = menu_get_parent_menu(menu); + rootEntry = menu_get_menu_or_parent_menu(menu); conf(rootEntry); break; } diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index fbc907f75eac..5cc85c3d4aaa 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -97,7 +97,7 @@ bool menu_is_empty(struct menu *menu); bool menu_is_visible(struct menu *menu); bool menu_has_prompt(const struct menu *menu); const char *menu_get_prompt(const struct menu *menu); -struct menu *menu_get_parent_menu(struct menu *menu); +struct menu *menu_get_menu_or_parent_menu(struct menu *menu); int get_jump_key_char(void); struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head); void menu_get_ext_help(struct menu *menu, struct gstr *help); diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index 7d48a692bd27..ccb690bbf05d 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -575,7 +575,13 @@ const char *menu_get_prompt(const struct menu *menu) return NULL; } -struct menu *menu_get_parent_menu(struct menu *menu) +/** + * menu_get_menu_or_parent_menu - return the parent menu or the menu itself + * @menu: pointer to the menu + * return: the parent menu. If the given argument is already a menu, return + * itself. + */ +struct menu *menu_get_menu_or_parent_menu(struct menu *menu) { enum prop_type type; diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index 68640e507ec4..dc056b0a8fde 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -577,7 +577,7 @@ void ConfigList::setParentMenu(void) oldroot = rootEntry; if (rootEntry == &rootmenu) return; - setRootMenu(menu_get_parent_menu(rootEntry->parent)); + setRootMenu(menu_get_menu_or_parent_menu(rootEntry->parent)); QTreeWidgetItemIterator it(this); while (*it) { @@ -1540,7 +1540,7 @@ void ConfigMainWindow::setMenuLink(struct menu *menu) switch (configList->mode) { case singleMode: list = configList; - parent = menu_get_parent_menu(menu); + parent = menu_get_menu_or_parent_menu(menu); if (!parent) return; list->setRootMenu(parent); @@ -1551,7 +1551,7 @@ void ConfigMainWindow::setMenuLink(struct menu *menu) configList->clearSelection(); list = configList; } else { - parent = menu_get_parent_menu(menu->parent); + parent = menu_get_menu_or_parent_menu(menu->parent); if (!parent) return; -- cgit v1.2.3 From 7d1bfaa457686b1e791de03450a3d49f28bdd022 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:53 +0900 Subject: kconfig: re-add menu_get_parent_menu() that returns parent menu This helper returns the parent menu, or NULL if there is no parent. The main difference from the previous version is that it always returns the parent menu even when the given argument is itself a menu. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/lkc.h | 1 + scripts/kconfig/menu.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+) (limited to 'scripts') diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index 5cc85c3d4aaa..37b606c74bff 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -97,6 +97,7 @@ bool menu_is_empty(struct menu *menu); bool menu_is_visible(struct menu *menu); bool menu_has_prompt(const struct menu *menu); const char *menu_get_prompt(const struct menu *menu); +struct menu *menu_get_parent_menu(struct menu *menu); struct menu *menu_get_menu_or_parent_menu(struct menu *menu); int get_jump_key_char(void); struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head); diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index ccb690bbf05d..a5e5b4fdcd93 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -575,6 +575,20 @@ const char *menu_get_prompt(const struct menu *menu) return NULL; } +/** + * menu_get_parent_menu - return the parent menu or NULL + * @menu: pointer to the menu + * return: the parent menu, or NULL if there is no parent. + */ +struct menu *menu_get_parent_menu(struct menu *menu) +{ + for (menu = menu->parent; menu; menu = menu->parent) + if (menu->type == M_MENU) + return menu; + + return NULL; +} + /** * menu_get_menu_or_parent_menu - return the parent menu or the menu itself * @menu: pointer to the menu -- cgit v1.2.3 From 2f2d60f489f0b2410f33103fa42296f7466673e0 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:54 +0900 Subject: kconfig: gconf: make columns resizable The variable "resizeable" is a typo and always set to FALSE, resulting in dead code in init_right_tree(). It is unclear column resizing should be disabled. Enable it. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/gconf.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index c0f46f189060..a3978d3420d1 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -30,7 +30,6 @@ static gint view_mode = FULL_VIEW; static gboolean show_name = TRUE; static gboolean show_range = TRUE; static gboolean show_value = TRUE; -static gboolean resizeable = FALSE; static int opt_mode = OPT_NORMAL; GtkWidget *main_wnd = NULL; @@ -312,11 +311,9 @@ static void init_right_tree(void) column = gtk_tree_view_get_column(view, COL_VALUE); gtk_tree_view_column_set_visible(column, show_value); - if (resizeable) { - for (i = 0; i < COL_VALUE; i++) { - column = gtk_tree_view_get_column(view, i); - gtk_tree_view_column_set_resizable(column, TRUE); - } + for (i = 0; i < COL_VALUE; i++) { + column = gtk_tree_view_get_column(view, i); + gtk_tree_view_column_set_resizable(column, TRUE); } sel = gtk_tree_view_get_selection(view); -- cgit v1.2.3 From f72ed4c6a375e52a3f4b75615e4a89d29d8acea7 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:55 +0900 Subject: kconfig: gconf: fix potential memory leak in renderer_edited() If gtk_tree_model_get_iter() fails, gtk_tree_path_free() is not called. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap --- scripts/kconfig/gconf.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index a3978d3420d1..769f38307f34 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -745,7 +745,7 @@ static void renderer_edited(GtkCellRendererText * cell, struct symbol *sym; if (!gtk_tree_model_get_iter(model2, &iter, path)) - return; + goto free; gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); sym = menu->sym; @@ -757,6 +757,7 @@ static void renderer_edited(GtkCellRendererText * cell, update_tree(&rootmenu, NULL); +free: gtk_tree_path_free(path); } -- cgit v1.2.3 From 09b9297478a3da4d940af8ff8c5f8e27be4990e8 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Mon, 30 Jun 2025 09:33:23 -0600 Subject: docs: kdoc: micro-optimize KernRe Switch KernRe::add_regex() to a try..except block to avoid looking up each regex twice. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_re.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_re.py b/scripts/lib/kdoc/kdoc_re.py index e81695b273bf..a467cd2f160b 100644 --- a/scripts/lib/kdoc/kdoc_re.py +++ b/scripts/lib/kdoc/kdoc_re.py @@ -29,12 +29,10 @@ class KernRe: """ Adds a new regex or re-use it from the cache. """ - - if string in re_cache: + try: self.regex = re_cache[string] - else: + except KeyError: self.regex = re.compile(string, flags=flags) - if self.cache: re_cache[string] = self.regex -- cgit v1.2.3 From bfa5bb3d104b0f2ffd25daa3b4900d54fe060285 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Mon, 30 Jun 2025 10:03:28 -0600 Subject: docs: kdoc: remove the brcount floor in process_proto_type() Putting the floor under brcount does not change the output in any way, just remove it. Change the termination test from ==0 to <=0 to prevent infinite loops in case somebody does something truly wacko in the code. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index d9ff2d066160..935f2a3c4b47 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1609,9 +1609,7 @@ class KernelDoc: self.entry.brcount += r.group(2).count('{') self.entry.brcount -= r.group(2).count('}') - self.entry.brcount = max(self.entry.brcount, 0) - - if r.group(2) == ';' and self.entry.brcount == 0: + if r.group(2) == ';' and self.entry.brcount <= 0: self.dump_declaration(ln, self.entry.prototype) self.reset_state(ln) break -- cgit v1.2.3 From 02bb13bd6c55bffb53de8da1eae87533d332235d Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:56 +0900 Subject: kconfig: gconf: always destroy dialog in on_window1_delete_event() When gtk_dialog_run() returns GTK_RESPONSE_YES or GTK_RESPONSE_NO, gtk_widget_destroy() is not called, resulting in a memory leak. It is better to always destroy the dialog, even if the application is about to exit. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap --- scripts/kconfig/gconf.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 769f38307f34..52d439a5119b 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -378,6 +378,7 @@ gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event, { GtkWidget *dialog, *label; gint result; + gint ret = FALSE; if (!conf_get_changed()) return FALSE; @@ -404,17 +405,19 @@ gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event, switch (result) { case GTK_RESPONSE_YES: on_save_activate(NULL, NULL); - return FALSE; + break; case GTK_RESPONSE_NO: - return FALSE; + break; case GTK_RESPONSE_CANCEL: case GTK_RESPONSE_DELETE_EVENT: default: - gtk_widget_destroy(dialog); - return TRUE; + ret = TRUE; + break; } - return FALSE; + gtk_widget_destroy(dialog); + + return ret; } -- cgit v1.2.3 From bff576a2a90954c6b242bf02d915c49f52b1e3cb Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:57 +0900 Subject: kconfig: gconf: remove old #ifdef GTK_CHECK_VERSION Remove old code. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 52d439a5119b..b2a0208b0a5f 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -849,16 +849,12 @@ on_treeview2_button_press_event(GtkWidget * widget, struct menu *menu; gint col; -#if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK gint tx = (gint) event->x; gint ty = (gint) event->y; gint cx, cy; gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx, &cy); -#else - gtk_tree_view_get_cursor(view, &path, &column); -#endif if (path == NULL) return FALSE; -- cgit v1.2.3 From ab2924ab5e75380b007fad1fded809b5ba650b76 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:58 +0900 Subject: kconfig: gconf: remove empty if-block This if-block is empty. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap --- scripts/kconfig/gconf.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index b2a0208b0a5f..7960c456e3b9 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -913,8 +913,6 @@ on_treeview2_key_press_event(GtkWidget * widget, gtk_tree_view_expand_row(view, path, FALSE); return TRUE; } - if (event->keyval == GDK_KP_Enter) { - } if (widget == tree1_w) return FALSE; -- cgit v1.2.3 From 5575df3d3a216860db720ed5d3d1dcef33ab4d6d Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:04:59 +0900 Subject: kconfig: gconf: remove meaningless code in init_main_window() The 'widget' variable is set, but not used in later code. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap --- scripts/kconfig/gconf.c | 1 - 1 file changed, 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 7960c456e3b9..4b5befa4f685 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -129,7 +129,6 @@ static void init_main_window(const gchar *glade_file) conf_set_changed_callback(conf_changed); style = gtk_widget_get_style(main_wnd); - widget = glade_xml_get_widget(xml, "toolbar1"); replace_button_icon(xml, main_wnd->window, style, "button4", (gchar **) xpm_single_view); -- cgit v1.2.3 From 08726436886e05c46efcb0655018a74c534ddead Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:00 +0900 Subject: kconfig: gconf: remove unneeded gtk_tree_view_set_headers_visible() calls The headers are visible by default. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 4b5befa4f685..32e5e9054846 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -189,7 +189,6 @@ static void init_left_tree(void) GtkTreeViewColumn *column; gtk_tree_view_set_model(view, model1); - gtk_tree_view_set_headers_visible(view, TRUE); gtk_tree_view_set_rules_hint(view, TRUE); column = gtk_tree_view_column_new(); @@ -232,7 +231,6 @@ static void init_right_tree(void) gint i; gtk_tree_view_set_model(view, model2); - gtk_tree_view_set_headers_visible(view, TRUE); gtk_tree_view_set_rules_hint(view, TRUE); column = gtk_tree_view_column_new(); -- cgit v1.2.3 From ede0a43249d47660ca977c90a279b6cfc9da314a Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:01 +0900 Subject: kconfig: gconf: remove gtk_tree_view_column_set_visible() calls The columns are visible by default. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 32e5e9054846..a027f0f10af9 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -297,17 +297,6 @@ static void init_right_tree(void) g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(renderer_edited), NULL); - column = gtk_tree_view_get_column(view, COL_NAME); - gtk_tree_view_column_set_visible(column, show_name); - column = gtk_tree_view_get_column(view, COL_NO); - gtk_tree_view_column_set_visible(column, show_range); - column = gtk_tree_view_get_column(view, COL_MOD); - gtk_tree_view_column_set_visible(column, show_range); - column = gtk_tree_view_get_column(view, COL_YES); - gtk_tree_view_column_set_visible(column, show_range); - column = gtk_tree_view_get_column(view, COL_VALUE); - gtk_tree_view_column_set_visible(column, show_value); - for (i = 0; i < COL_VALUE; i++) { column = gtk_tree_view_get_column(view, i); gtk_tree_view_column_set_resizable(column, TRUE); -- cgit v1.2.3 From dc1de6c03bc67ff918d904c7f239eaebea34b99b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:02 +0900 Subject: kconfig: gconf: remove gtk_widget_realize() calls This function is primarily used in widget implementations, and isn't very useful otherwise. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index a027f0f10af9..3f9b9957f089 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -215,7 +215,6 @@ static void init_left_tree(void) sel = gtk_tree_view_get_selection(view); gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); - gtk_widget_realize(tree1_w); } static void renderer_edited(GtkCellRendererText * cell, @@ -967,7 +966,6 @@ on_treeview1_button_press_event(GtkWidget * widget, display_tree_part(); } - gtk_widget_realize(tree2_w); gtk_tree_view_set_cursor(view, path, NULL, FALSE); gtk_widget_grab_focus(tree2_w); -- cgit v1.2.3 From ace8bee8369c209b647600c3dc28b529e4f44966 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:03 +0900 Subject: kconfig: gconf: remove gtk_tree_view_set_rules_hint() calls The use of the this function is not recommended, and it has been deprecated since GTK 3.14. [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/3.14.0/gtk/gtktreeview.c#L11891 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 3f9b9957f089..4bbc8f87deb6 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -189,7 +189,6 @@ static void init_left_tree(void) GtkTreeViewColumn *column; gtk_tree_view_set_model(view, model1); - gtk_tree_view_set_rules_hint(view, TRUE); column = gtk_tree_view_column_new(); gtk_tree_view_append_column(view, column); @@ -230,7 +229,6 @@ static void init_right_tree(void) gint i; gtk_tree_view_set_model(view, model2); - gtk_tree_view_set_rules_hint(view, TRUE); column = gtk_tree_view_column_new(); gtk_tree_view_append_column(view, column); -- cgit v1.2.3 From f931a5d37a17f941492392b13f0ad67b1bac8bef Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:04 +0900 Subject: kconfig: gconf: remove unnecessary gtk_set_locale() call gtk_set_locale() has been deprecated since version 2.24, and setlocale() should be used directly. [1] However, gtk_init() automatically does this, so there is typically no point in calling this function. [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/gtk/gtkmain.c#L1152 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 1 - 1 file changed, 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 4bbc8f87deb6..3e632a325c10 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1367,7 +1367,6 @@ int main(int ac, char *av[]) gchar *glade_file; /* GTK stuffs */ - gtk_set_locale(); gtk_init(&ac, &av); glade_init(); -- cgit v1.2.3 From a54b0397d36706ce6f60e0e56709ad94791eda45 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:05 +0900 Subject: kconfig: gconf: remove internal-child="image" nodes from glade These nodes do not appear to serve anything useful. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.glade | 84 --------------------------------------------- 1 file changed, 84 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.glade b/scripts/kconfig/gconf.glade index aa483cb32755..19b80f2ec1ff 100644 --- a/scripts/kconfig/gconf.glade +++ b/scripts/kconfig/gconf.glade @@ -48,18 +48,6 @@ True - - - - True - gtk-open - 1 - 0.5 - 0.5 - 0 - 0 - - @@ -71,18 +59,6 @@ True - - - - True - gtk-save - 1 - 0.5 - 0.5 - 0 - 0 - - @@ -93,18 +69,6 @@ Save _as True - - - - True - gtk-save-as - 1 - 0.5 - 0.5 - 0 - 0 - - @@ -121,18 +85,6 @@ True - - - - True - gtk-quit - 1 - 0.5 - 0.5 - 0 - 0 - - @@ -244,18 +196,6 @@ True - - - - True - gtk-dialog-question - 1 - 0.5 - 0.5 - 0 - 0 - - @@ -266,18 +206,6 @@ True - - - - True - gtk-properties - 1 - 0.5 - 0.5 - 0 - 0 - - @@ -287,18 +215,6 @@ _License True - - - - True - gtk-justify-fill - 1 - 0.5 - 0.5 - 0 - 0 - - -- cgit v1.2.3 From b3841b501c4a58ba20f190afd25aa4b93cd664f9 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:06 +0900 Subject: kconfig: gconf: remove parents[] array and indent variable The parents[] array is used to store the GtkTreeIter of parent nodes, but this can be simplified: we can pass a GtkTreeIter pointer down when _display_tree() recurses. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 49 ++++++++++++++----------------------------------- 1 file changed, 14 insertions(+), 35 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 3e632a325c10..432a467e3250 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -47,8 +47,6 @@ GdkColor color; GtkTreeStore *tree1, *tree2, *tree; GtkTreeModel *model1, *model2; -static GtkTreeIter *parents[256]; -static gint indent; static struct menu *current; // current node for SINGLE view static struct menu *browsed; // browsed node for SPLIT view @@ -153,8 +151,6 @@ static void init_main_window(const gchar *glade_file) static void init_tree_model(void) { - gint i; - tree = tree2 = gtk_tree_store_new(COL_NUMBER, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, @@ -166,9 +162,6 @@ static void init_tree_model(void) G_TYPE_BOOLEAN); model2 = GTK_TREE_MODEL(tree2); - for (parents[0] = NULL, i = 1; i < 256; i++) - parents[i] = (GtkTreeIter *) g_malloc(sizeof(GtkTreeIter)); - tree1 = gtk_tree_store_new(COL_NUMBER, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, @@ -1131,18 +1124,6 @@ static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row) g_object_unref(pix); } - -/* Add a node to the tree */ -static void place_node(struct menu *menu, char **row) -{ - GtkTreeIter *parent = parents[indent - 1]; - GtkTreeIter *node = parents[indent]; - - gtk_tree_store_append(tree, node, parent); - set_node(node, menu, row); -} - - /* Find a node in the GTK+ tree */ static GtkTreeIter found; @@ -1193,9 +1174,6 @@ static void update_tree(struct menu *src, GtkTreeIter * dst) struct symbol *sym; struct menu *menu1, *menu2; - if (src == &rootmenu) - indent = 1; - valid = gtk_tree_model_iter_children(model2, child2, dst); for (child1 = src->list; child1; child1 = child1->next) { @@ -1253,9 +1231,7 @@ static void update_tree(struct menu *src, GtkTreeIter * dst) set_node(child2, menu1, fill_row(menu1)); } - indent++; update_tree(child1, child2); - indent--; valid = gtk_tree_model_iter_next(model2, child2); } @@ -1263,16 +1239,15 @@ static void update_tree(struct menu *src, GtkTreeIter * dst) /* Display the whole tree (single/split/full view) */ -static void display_tree(struct menu *menu) +static void _display_tree(struct menu *menu, GtkTreeIter *parent) { struct property *prop; struct menu *child; enum prop_type ptype; + GtkTreeIter iter; - if (menu == &rootmenu) { - indent = 1; + if (menu == &rootmenu) current = &rootmenu; - } for (child = menu->list; child; child = child->next) { prop = child->prompt; @@ -1290,8 +1265,10 @@ static void display_tree(struct menu *menu) if ((opt_mode == OPT_NORMAL && menu_is_visible(child)) || (opt_mode == OPT_PROMPT && menu_has_prompt(child)) || - (opt_mode == OPT_ALL && menu_get_prompt(child))) - place_node(child, fill_row(child)); + (opt_mode == OPT_ALL && menu_get_prompt(child))) { + gtk_tree_store_append(tree, &iter, parent); + set_node(&iter, child, fill_row(child)); + } if ((view_mode != FULL_VIEW) && (ptype == P_MENU) && (tree == tree2)) @@ -1308,14 +1285,16 @@ static void display_tree(struct menu *menu) if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT)) || (view_mode == FULL_VIEW) - || (view_mode == SPLIT_VIEW)) { - indent++; - display_tree(child); - indent--; - } + || (view_mode == SPLIT_VIEW)) + _display_tree(child, &iter); } } +static void display_tree(struct menu *menu) +{ + _display_tree(menu, NULL); +} + /* Display a part of the tree starting at current node (single/split view) */ static void display_tree_part(void) { -- cgit v1.2.3 From 9b8338fd45fc10961130bc2477aa72e4484e2732 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:07 +0900 Subject: kconfig: gconf: remove unnecessary NULL checks for tree1 and tree2 The tree1 and tree2 variables are initialized earlier in init_tree_model(), so the NULL checks are redundant. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 432a467e3250..2ab000adcced 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -678,8 +678,7 @@ void on_split_clicked(GtkButton * button, gpointer user_data) gtk_widget_show(tree1_w); gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); gtk_paned_set_position(GTK_PANED(hpaned), w / 2); - if (tree2) - gtk_tree_store_clear(tree2); + gtk_tree_store_clear(tree2); display_list(); /* Disable back btn, like in full mode. */ @@ -691,8 +690,7 @@ void on_full_clicked(GtkButton * button, gpointer user_data) { view_mode = FULL_VIEW; gtk_widget_hide(tree1_w); - if (tree2) - gtk_tree_store_clear(tree2); + gtk_tree_store_clear(tree2); display_tree(&rootmenu); gtk_widget_set_sensitive(back_btn, FALSE); } @@ -1298,8 +1296,7 @@ static void display_tree(struct menu *menu) /* Display a part of the tree starting at current node (single/split view) */ static void display_tree_part(void) { - if (tree2) - gtk_tree_store_clear(tree2); + gtk_tree_store_clear(tree2); if (view_mode == SINGLE_VIEW) display_tree(current); else if (view_mode == SPLIT_VIEW) @@ -1312,8 +1309,7 @@ static void display_tree_part(void) /* Display the list in the left frame (split view) */ static void display_list(void) { - if (tree1) - gtk_tree_store_clear(tree1); + gtk_tree_store_clear(tree1); tree = tree1; display_tree(&rootmenu); -- cgit v1.2.3 From 8e3136eb27211eaf0560543dd0ee4698c6eb751f Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:08 +0900 Subject: kconfig: gconf: remove unneeded variable in on_split_clicked() The height of the window is not used here. Passing NULL to gtk_window_get_default_size() is allowed. [1] [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/gtk/gtkwindow.c#L3974 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 2ab000adcced..c78eded5c01b 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -673,10 +673,10 @@ void on_single_clicked(GtkButton * button, gpointer user_data) void on_split_clicked(GtkButton * button, gpointer user_data) { - gint w, h; + gint w; view_mode = SPLIT_VIEW; gtk_widget_show(tree1_w); - gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); + gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, NULL); gtk_paned_set_position(GTK_PANED(hpaned), w / 2); gtk_tree_store_clear(tree2); display_list(); -- cgit v1.2.3 From 57b63d17f73e2d5576e57521fb10307b91439b72 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:09 +0900 Subject: kconfig: gconf: remove unneeded variables in on_treeview*_button_press_event() Not all position parameters are used here. Passing NULL to gtk_tree_view_get_cursor() or gtk_tree_view_get_path_at_pos() is allowed. [1] [2] [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/gtk/gtktreeview.c#L12638 [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/gtk/gtktreeview.c#L12795 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index c78eded5c01b..ab2e0df21037 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -822,13 +822,10 @@ on_treeview2_button_press_event(GtkWidget * widget, GtkTreeIter iter; struct menu *menu; gint col; - gint tx = (gint) event->x; gint ty = (gint) event->y; - gint cx, cy; - gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx, - &cy); + gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, NULL, NULL); if (path == NULL) return FALSE; @@ -871,12 +868,11 @@ on_treeview2_key_press_event(GtkWidget * widget, { GtkTreeView *view = GTK_TREE_VIEW(widget); GtkTreePath *path; - GtkTreeViewColumn *column; GtkTreeIter iter; struct menu *menu; gint col; - gtk_tree_view_get_cursor(view, &path, &column); + gtk_tree_view_get_cursor(view, &path, NULL); if (path == NULL) return FALSE; @@ -930,16 +926,12 @@ on_treeview1_button_press_event(GtkWidget * widget, { GtkTreeView *view = GTK_TREE_VIEW(widget); GtkTreePath *path; - GtkTreeViewColumn *column; GtkTreeIter iter; struct menu *menu; - gint tx = (gint) event->x; gint ty = (gint) event->y; - gint cx, cy; - gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx, - &cy); + gtk_tree_view_get_path_at_pos(view, tx, ty, &path, NULL, NULL, NULL); if (path == NULL) return FALSE; -- cgit v1.2.3 From b4809e25e2bf05de6398830f0990abe5dff49ac5 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:10 +0900 Subject: kconfig: gconf: remove unused 'color' variable This is not used at all. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 1 - 1 file changed, 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index ab2e0df21037..4fff931f34fc 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -43,7 +43,6 @@ GtkWidget *save_btn = NULL; GtkWidget *save_menu_item = NULL; GtkTextTag *tag1, *tag2; -GdkColor color; GtkTreeStore *tree1, *tree2, *tree; GtkTreeModel *model1, *model2; -- cgit v1.2.3 From 290fc035dfeb07fcec57b09d888f837531333af7 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:11 +0900 Subject: kconfig: gconf: add static qualifiers to variables I also removed unnecessary initializers. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 4fff931f34fc..0d5a02706bcb 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -32,20 +32,20 @@ static gboolean show_range = TRUE; static gboolean show_value = TRUE; static int opt_mode = OPT_NORMAL; -GtkWidget *main_wnd = NULL; -GtkWidget *tree1_w = NULL; // left frame -GtkWidget *tree2_w = NULL; // right frame -GtkWidget *text_w = NULL; -GtkWidget *hpaned = NULL; -GtkWidget *vpaned = NULL; -GtkWidget *back_btn = NULL; -GtkWidget *save_btn = NULL; -GtkWidget *save_menu_item = NULL; - -GtkTextTag *tag1, *tag2; - -GtkTreeStore *tree1, *tree2, *tree; -GtkTreeModel *model1, *model2; +static GtkWidget *main_wnd; +static GtkWidget *tree1_w; // left frame +static GtkWidget *tree2_w; // right frame +static GtkWidget *text_w; +static GtkWidget *hpaned; +static GtkWidget *vpaned; +static GtkWidget *back_btn; +static GtkWidget *save_btn; +static GtkWidget *save_menu_item; + +static GtkTextTag *tag1, *tag2; + +static GtkTreeStore *tree1, *tree2, *tree; +static GtkTreeModel *model1, *model2; static struct menu *current; // current node for SINGLE view static struct menu *browsed; // browsed node for SPLIT view -- cgit v1.2.3 From 7ef533938e6cd7d0e33e1c24389c34a21221979b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:12 +0900 Subject: kconfig: gconf: move init_*() functions below This allows removal of the forward declaration of renderer_edited(). Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 450 ++++++++++++++++++++++++------------------------ 1 file changed, 222 insertions(+), 228 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 0d5a02706bcb..9d06c050b270 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -62,240 +62,12 @@ static void display_tree(struct menu *menu); static void display_tree_part(void); static void update_tree(struct menu *src, GtkTreeIter * dst); -static void replace_button_icon(GladeXML *xml, GdkDrawable *window, - GtkStyle *style, gchar *btn_name, gchar **xpm) -{ - GdkPixmap *pixmap; - GdkBitmap *mask; - GtkToolButton *button; - GtkWidget *image; - - pixmap = gdk_pixmap_create_from_xpm_d(window, &mask, - &style->bg[GTK_STATE_NORMAL], - xpm); - - button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name)); - image = gtk_image_new_from_pixmap(pixmap, mask); - gtk_widget_show(image); - gtk_tool_button_set_icon_widget(button, image); -} - static void conf_changed(bool dirty) { gtk_widget_set_sensitive(save_btn, dirty); gtk_widget_set_sensitive(save_menu_item, dirty); } -/* Main Window Initialization */ -static void init_main_window(const gchar *glade_file) -{ - GladeXML *xml; - GtkWidget *widget; - GtkTextBuffer *txtbuf; - GtkStyle *style; - - xml = glade_xml_new(glade_file, "window1", NULL); - if (!xml) - g_error("GUI loading failed !\n"); - glade_xml_signal_autoconnect(xml); - - main_wnd = glade_xml_get_widget(xml, "window1"); - hpaned = glade_xml_get_widget(xml, "hpaned1"); - vpaned = glade_xml_get_widget(xml, "vpaned1"); - tree1_w = glade_xml_get_widget(xml, "treeview1"); - tree2_w = glade_xml_get_widget(xml, "treeview2"); - text_w = glade_xml_get_widget(xml, "textview3"); - - back_btn = glade_xml_get_widget(xml, "button1"); - gtk_widget_set_sensitive(back_btn, FALSE); - - widget = glade_xml_get_widget(xml, "show_name1"); - gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, - show_name); - - widget = glade_xml_get_widget(xml, "show_range1"); - gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, - show_range); - - widget = glade_xml_get_widget(xml, "show_data1"); - gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, - show_value); - - save_btn = glade_xml_get_widget(xml, "button3"); - save_menu_item = glade_xml_get_widget(xml, "save1"); - conf_set_changed_callback(conf_changed); - - style = gtk_widget_get_style(main_wnd); - - replace_button_icon(xml, main_wnd->window, style, - "button4", (gchar **) xpm_single_view); - replace_button_icon(xml, main_wnd->window, style, - "button5", (gchar **) xpm_split_view); - replace_button_icon(xml, main_wnd->window, style, - "button6", (gchar **) xpm_tree_view); - - txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); - tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1", - "foreground", "red", - "weight", PANGO_WEIGHT_BOLD, - NULL); - tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2", - /*"style", PANGO_STYLE_OBLIQUE, */ - NULL); - - gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text); - - gtk_widget_show(main_wnd); -} - -static void init_tree_model(void) -{ - tree = tree2 = gtk_tree_store_new(COL_NUMBER, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_POINTER, GDK_TYPE_COLOR, - G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, - G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN); - model2 = GTK_TREE_MODEL(tree2); - - tree1 = gtk_tree_store_new(COL_NUMBER, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_POINTER, GDK_TYPE_COLOR, - G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, - G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN); - model1 = GTK_TREE_MODEL(tree1); -} - -static void init_left_tree(void) -{ - GtkTreeView *view = GTK_TREE_VIEW(tree1_w); - GtkCellRenderer *renderer; - GtkTreeSelection *sel; - GtkTreeViewColumn *column; - - gtk_tree_view_set_model(view, model1); - - column = gtk_tree_view_column_new(); - gtk_tree_view_append_column(view, column); - gtk_tree_view_column_set_title(column, "Options"); - - renderer = gtk_cell_renderer_toggle_new(); - gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), - renderer, FALSE); - gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), - renderer, - "active", COL_BTNACT, - "inconsistent", COL_BTNINC, - "visible", COL_BTNVIS, - "radio", COL_BTNRAD, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), - renderer, FALSE); - gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), - renderer, - "text", COL_OPTION, - "foreground-gdk", - COL_COLOR, NULL); - - sel = gtk_tree_view_get_selection(view); - gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); -} - -static void renderer_edited(GtkCellRendererText * cell, - const gchar * path_string, - const gchar * new_text, gpointer user_data); - -static void init_right_tree(void) -{ - GtkTreeView *view = GTK_TREE_VIEW(tree2_w); - GtkCellRenderer *renderer; - GtkTreeSelection *sel; - GtkTreeViewColumn *column; - gint i; - - gtk_tree_view_set_model(view, model2); - - column = gtk_tree_view_column_new(); - gtk_tree_view_append_column(view, column); - gtk_tree_view_column_set_title(column, "Options"); - - renderer = gtk_cell_renderer_pixbuf_new(); - gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), - renderer, FALSE); - gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), - renderer, - "pixbuf", COL_PIXBUF, - "visible", COL_PIXVIS, NULL); - renderer = gtk_cell_renderer_toggle_new(); - gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), - renderer, FALSE); - gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), - renderer, - "active", COL_BTNACT, - "inconsistent", COL_BTNINC, - "visible", COL_BTNVIS, - "radio", COL_BTNRAD, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), - renderer, FALSE); - gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), - renderer, - "text", COL_OPTION, - "foreground-gdk", - COL_COLOR, NULL); - - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(view, -1, - "Name", renderer, - "text", COL_NAME, - "foreground-gdk", - COL_COLOR, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(view, -1, - "N", renderer, - "text", COL_NO, - "foreground-gdk", - COL_COLOR, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(view, -1, - "M", renderer, - "text", COL_MOD, - "foreground-gdk", - COL_COLOR, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(view, -1, - "Y", renderer, - "text", COL_YES, - "foreground-gdk", - COL_COLOR, NULL); - renderer = gtk_cell_renderer_text_new(); - gtk_tree_view_insert_column_with_attributes(view, -1, - "Value", renderer, - "text", COL_VALUE, - "editable", - COL_EDIT, - "foreground-gdk", - COL_COLOR, NULL); - g_signal_connect(G_OBJECT(renderer), "edited", - G_CALLBACK(renderer_edited), NULL); - - for (i = 0; i < COL_VALUE; i++) { - column = gtk_tree_view_get_column(view, i); - gtk_tree_view_column_set_resizable(column, TRUE); - } - - sel = gtk_tree_view_get_selection(view); - gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); -} - - /* Utility Functions */ @@ -1324,6 +1096,228 @@ static void fixup_rootmenu(struct menu *menu) } } +/* Main Window Initialization */ +static void replace_button_icon(GladeXML *xml, GdkDrawable *window, + GtkStyle *style, gchar *btn_name, gchar **xpm) +{ + GdkPixmap *pixmap; + GdkBitmap *mask; + GtkToolButton *button; + GtkWidget *image; + + pixmap = gdk_pixmap_create_from_xpm_d(window, &mask, + &style->bg[GTK_STATE_NORMAL], + xpm); + + button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name)); + image = gtk_image_new_from_pixmap(pixmap, mask); + gtk_widget_show(image); + gtk_tool_button_set_icon_widget(button, image); +} + +static void init_main_window(const gchar *glade_file) +{ + GladeXML *xml; + GtkWidget *widget; + GtkTextBuffer *txtbuf; + GtkStyle *style; + + xml = glade_xml_new(glade_file, "window1", NULL); + if (!xml) + g_error("GUI loading failed !\n"); + glade_xml_signal_autoconnect(xml); + + main_wnd = glade_xml_get_widget(xml, "window1"); + hpaned = glade_xml_get_widget(xml, "hpaned1"); + vpaned = glade_xml_get_widget(xml, "vpaned1"); + tree1_w = glade_xml_get_widget(xml, "treeview1"); + tree2_w = glade_xml_get_widget(xml, "treeview2"); + text_w = glade_xml_get_widget(xml, "textview3"); + + back_btn = glade_xml_get_widget(xml, "button1"); + gtk_widget_set_sensitive(back_btn, FALSE); + + widget = glade_xml_get_widget(xml, "show_name1"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, + show_name); + + widget = glade_xml_get_widget(xml, "show_range1"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, + show_range); + + widget = glade_xml_get_widget(xml, "show_data1"); + gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, + show_value); + + save_btn = glade_xml_get_widget(xml, "button3"); + save_menu_item = glade_xml_get_widget(xml, "save1"); + conf_set_changed_callback(conf_changed); + + style = gtk_widget_get_style(main_wnd); + + replace_button_icon(xml, main_wnd->window, style, + "button4", (gchar **) xpm_single_view); + replace_button_icon(xml, main_wnd->window, style, + "button5", (gchar **) xpm_split_view); + replace_button_icon(xml, main_wnd->window, style, + "button6", (gchar **) xpm_tree_view); + + txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); + tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1", + "foreground", "red", + "weight", PANGO_WEIGHT_BOLD, + NULL); + tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2", + /*"style", PANGO_STYLE_OBLIQUE, */ + NULL); + + gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text); + + gtk_widget_show(main_wnd); +} + +static void init_tree_model(void) +{ + tree = tree2 = gtk_tree_store_new(COL_NUMBER, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_POINTER, GDK_TYPE_COLOR, + G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + model2 = GTK_TREE_MODEL(tree2); + + tree1 = gtk_tree_store_new(COL_NUMBER, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_POINTER, GDK_TYPE_COLOR, + G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + model1 = GTK_TREE_MODEL(tree1); +} + +static void init_left_tree(void) +{ + GtkTreeView *view = GTK_TREE_VIEW(tree1_w); + GtkCellRenderer *renderer; + GtkTreeSelection *sel; + GtkTreeViewColumn *column; + + gtk_tree_view_set_model(view, model1); + + column = gtk_tree_view_column_new(); + gtk_tree_view_append_column(view, column); + gtk_tree_view_column_set_title(column, "Options"); + + renderer = gtk_cell_renderer_toggle_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "active", COL_BTNACT, + "inconsistent", COL_BTNINC, + "visible", COL_BTNVIS, + "radio", COL_BTNRAD, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "text", COL_OPTION, + "foreground-gdk", + COL_COLOR, NULL); + + sel = gtk_tree_view_get_selection(view); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); +} + +static void init_right_tree(void) +{ + GtkTreeView *view = GTK_TREE_VIEW(tree2_w); + GtkCellRenderer *renderer; + GtkTreeSelection *sel; + GtkTreeViewColumn *column; + gint i; + + gtk_tree_view_set_model(view, model2); + + column = gtk_tree_view_column_new(); + gtk_tree_view_append_column(view, column); + gtk_tree_view_column_set_title(column, "Options"); + + renderer = gtk_cell_renderer_pixbuf_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "pixbuf", COL_PIXBUF, + "visible", COL_PIXVIS, NULL); + renderer = gtk_cell_renderer_toggle_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "active", COL_BTNACT, + "inconsistent", COL_BTNINC, + "visible", COL_BTNVIS, + "radio", COL_BTNRAD, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column), + renderer, FALSE); + gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), + renderer, + "text", COL_OPTION, + "foreground-gdk", + COL_COLOR, NULL); + + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "Name", renderer, + "text", COL_NAME, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "N", renderer, + "text", COL_NO, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "M", renderer, + "text", COL_MOD, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "Y", renderer, + "text", COL_YES, + "foreground-gdk", + COL_COLOR, NULL); + renderer = gtk_cell_renderer_text_new(); + gtk_tree_view_insert_column_with_attributes(view, -1, + "Value", renderer, + "text", COL_VALUE, + "editable", + COL_EDIT, + "foreground-gdk", + COL_COLOR, NULL); + g_signal_connect(G_OBJECT(renderer), "edited", + G_CALLBACK(renderer_edited), NULL); + + for (i = 0; i < COL_VALUE; i++) { + column = gtk_tree_view_get_column(view, i); + gtk_tree_view_column_set_resizable(column, TRUE); + } + + sel = gtk_tree_view_get_selection(view); + gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE); +} /* Main */ int main(int ac, char *av[]) -- cgit v1.2.3 From 4eaf6120c16a0fdb60177410507de86eefbe8c0f Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 27 Jun 2025 12:39:53 -0600 Subject: docs: kdoc: remove KernelEntry::in_doc_sect This field is not used for anything, just get rid of it. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250627184000.132291-2-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 4 ---- 1 file changed, 4 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 3557c512c85a..f3970ffbf402 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -151,8 +151,6 @@ class KernelEntry: # State flags self.brcount = 0 - - self.in_doc_sect = False self.declaration_start_line = ln + 1 # TODO: rename to emit_message after removal of kernel-doc.pl @@ -1227,7 +1225,6 @@ class KernelDoc: # start a new entry self.reset_state(ln) - self.entry.in_doc_sect = False # next line is always the function name self.state = state.NAME @@ -1315,7 +1312,6 @@ class KernelDoc: # def is_new_section(self, ln, line): if doc_sect.search(line): - self.entry.in_doc_sect = True self.state = state.BODY # # Pick out the name of our new section, tweaking it if need be. -- cgit v1.2.3 From 1e2a79ca39ae33f1da347692c8a0573e120da511 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 27 Jun 2025 12:39:54 -0600 Subject: docs: kdoc: Move content handling into KernelEntry Rather than having other code mucking around with this bit of internal state, encapsulate it internally. Accumulate the description as a list of strings, joining them at the end, which is a more efficient way of building the text. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250627184000.132291-3-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 62 ++++++++++++++++++++--------------------- 1 file changed, 30 insertions(+), 32 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index f3970ffbf402..f87355b63c19 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -128,7 +128,7 @@ class KernelEntry: def __init__(self, config, ln): self.config = config - self.contents = "" + self._contents = [] self.function = "" self.sectcheck = "" self.struct_actual = "" @@ -153,6 +153,15 @@ class KernelEntry: self.brcount = 0 self.declaration_start_line = ln + 1 + # + # Management of section contents + # + def add_text(self, text): + self._contents.append(text) + + def contents(self): + return '\n'.join(self._contents) + '\n' + # TODO: rename to emit_message after removal of kernel-doc.pl def emit_msg(self, log_msg, warning=True): """Emit a message""" @@ -180,9 +189,14 @@ class KernelEntry: """ Dumps section contents to arrays/hashes intended for that purpose. """ - + # + # If we have accumulated no contents in the default ("description") + # section, don't bother. + # + if self.section == SECTION_DEFAULT and not self._contents: + return name = self.section - contents = self.contents + contents = self.contents() if type_param.match(name): name = type_param.group(1) @@ -206,7 +220,8 @@ class KernelEntry: if name != SECTION_DEFAULT: self.emit_msg(self.new_start_line, f"duplicate section name '{name}'\n") - self.sections[name] += contents + # Treat as a new paragraph - add a blank line + self.sections[name] += '\n' + contents else: self.sections[name] = contents self.sectionlist.append(name) @@ -217,7 +232,7 @@ class KernelEntry: if start_new: self.section = SECTION_DEFAULT - self.contents = "" + self._contents = [] class KernelDoc: @@ -1334,16 +1349,11 @@ class KernelDoc: newcontents = doc_sect.group(2) if not newcontents: newcontents = "" - - if self.entry.contents.strip("\n"): - self.dump_section() - + self.dump_section() self.entry.begin_section(ln, newsection) self.entry.leading_space = None - self.entry.contents = newcontents.lstrip() - if self.entry.contents: - self.entry.contents += "\n" + self.entry.add_text(newcontents.lstrip()) return True return False @@ -1385,7 +1395,6 @@ class KernelDoc: # if cont == "": self.state = state.BODY - self.entry.contents += "\n" # needed? # # Otherwise we have more of the declaration section to soak up. # @@ -1407,7 +1416,6 @@ class KernelDoc: # if KernRe(r"\s*\*\s*$").match(line): self.entry.begin_section(ln, dump = True) - self.entry.contents += '\n' self.state = state.BODY return # @@ -1444,7 +1452,7 @@ class KernelDoc: # # Add the trimmed result to the section and we're done. # - self.entry.contents += cont[self.entry.leading_space:] + '\n' + self.entry.add_text(cont[self.entry.leading_space:]) else: # Unknown line, ignore self.emit_msg(ln, f"bad line: {line}") @@ -1458,7 +1466,7 @@ class KernelDoc: if doc_content.search(line): cont = doc_content.group(1) - self.entry.contents += cont + "\n" + self.entry.add_text(cont) else: # Unknown line, ignore self.emit_msg(ln, f"bad line: {line}") @@ -1470,27 +1478,20 @@ class KernelDoc: doc_inline_sect.search(line): self.entry.begin_section(ln, doc_inline_sect.group(1)) - self.entry.contents = doc_inline_sect.group(2).lstrip() - if self.entry.contents != "": - self.entry.contents += "\n" - + self.entry.add_text(doc_inline_sect.group(2).lstrip()) self.inline_doc_state = state.INLINE_TEXT # Documentation block end */ return if doc_inline_end.search(line): - if self.entry.contents not in ["", "\n"]: - self.dump_section() - + self.dump_section() self.state = state.PROTO self.inline_doc_state = state.INLINE_NA return if doc_content.search(line): if self.inline_doc_state == state.INLINE_TEXT: - self.entry.contents += doc_content.group(1) + "\n" - if not self.entry.contents.strip(" ").rstrip("\n"): - self.entry.contents = "" + self.entry.add_text(doc_content.group(1)) elif self.inline_doc_state == state.INLINE_NAME: self.emit_msg(ln, @@ -1668,11 +1669,8 @@ class KernelDoc: if doc_inline_oneline.search(line): self.entry.begin_section(ln, doc_inline_oneline.group(1)) - self.entry.contents = doc_inline_oneline.group(2) - - if self.entry.contents != "": - self.entry.contents += "\n" - self.dump_section(start_new=False) + self.entry.add_text(doc_inline_oneline.group(2)) + self.dump_section() elif doc_inline_start.search(line): self.state = state.INLINE @@ -1696,7 +1694,7 @@ class KernelDoc: self.reset_state(ln) elif doc_content.search(line): - self.entry.contents += doc_content.group(1) + "\n" + self.entry.add_text(doc_content.group(1)) def parse_export(self): """ -- cgit v1.2.3 From 08cd655e5b1ffaaac20724ce56ce543be41f300a Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 27 Jun 2025 12:39:55 -0600 Subject: docs: kdoc: remove a bit of dead code The type_param regex matches "@..." just fine, so the special-case branch for that in dump_section() is never executed. Just remove it. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250627184000.132291-4-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 7 ------- 1 file changed, 7 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index f87355b63c19..9e46cfa20978 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -207,13 +207,6 @@ class KernelEntry: self.sectcheck += name + " " self.new_start_line = 0 - elif name == "@...": - name = "..." - self.parameterdescs[name] = contents - self.sectcheck += name + " " - self.parameterdesc_start_lines[name] = self.new_start_line - self.new_start_line = 0 - else: if name in self.sections and self.sections[name] != "": # Only warn on user-specified duplicate section names -- cgit v1.2.3 From 0aa3675c26b976db2b645385e8a7990270b39db0 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 27 Jun 2025 12:39:56 -0600 Subject: docs: kdoc: remove KernelEntry::function This member is unused, to take it out. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250627184000.132291-5-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 1 - 1 file changed, 1 deletion(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 9e46cfa20978..224dea5f7c2e 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -129,7 +129,6 @@ class KernelEntry: self.config = config self._contents = [] - self.function = "" self.sectcheck = "" self.struct_actual = "" self.prototype = "" -- cgit v1.2.3 From d06c54fd3e84e2e740c0cb2b5613a088e8a42e9a Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 27 Jun 2025 12:39:57 -0600 Subject: docs: kdoc: rework process_export() slightly Reorganize process_export() to eliminate duplicated code, don't look for exports in states where we don't expect them, and don't bother with normal state-machine processing if an export declaration has been found. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250627184000.132291-6-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 224dea5f7c2e..734b908579c3 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1211,16 +1211,17 @@ class KernelDoc: if export_symbol.search(line): symbol = export_symbol.group(2) - for suffix in suffixes: - symbol = symbol.removesuffix(suffix) - function_set.add(symbol) - return - - if export_symbol_ns.search(line): + elif export_symbol_ns.search(line): symbol = export_symbol_ns.group(2) - for suffix in suffixes: - symbol = symbol.removesuffix(suffix) - function_set.add(symbol) + else: + return False + # + # Found an export, trim out any special suffixes + # + for suffix in suffixes: + symbol = symbol.removesuffix(suffix) + function_set.add(symbol) + return True def process_normal(self, ln, line): """ @@ -1767,13 +1768,10 @@ class KernelDoc: # it was read twice. Here, we use the already-existing # loop to parse exported symbols as well. # - # TODO: It should be noticed that not all states are - # needed here. On a future cleanup, process export only - # at the states that aren't handling comment markups. - self.process_export(export_table, line) - - # Hand this line to the appropriate state handler - self.state_actions[self.state](self, ln, line) + if (self.state != state.NORMAL) or \ + not self.process_export(export_table, line): + # Hand this line to the appropriate state handler + self.state_actions[self.state](self, ln, line) except OSError: self.config.log.error(f"Error: Cannot open file {self.fname}") -- cgit v1.2.3 From 388f4da27c4eaa2813259bcfbfc9b94ac8754902 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 27 Jun 2025 12:39:58 -0600 Subject: docs: kdoc: remove the INLINE_END state It is never used, so just get rid of it. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250627184000.132291-7-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 734b908579c3..03a0e44707a7 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -108,8 +108,7 @@ class state: INLINE_NA = 0 # not applicable ($state != INLINE) INLINE_NAME = 1 # looking for member name (@foo:) INLINE_TEXT = 2 # looking for member documentation - INLINE_END = 3 # done - INLINE_ERROR = 4 # error - Comment without header was found. + INLINE_ERROR = 3 # error - Comment without header was found. # Spit a warning as it's not # proper kernel-doc and ignore the rest. @@ -117,7 +116,6 @@ class state: "", "_NAME", "_TEXT", - "_END", "_ERROR", ] -- cgit v1.2.3 From 8976f993a3aa8c00699ed5cb1bc939e11c88a713 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 27 Jun 2025 12:39:59 -0600 Subject: docs: kdoc: remove the inline states-within-a-state The processing of inline kerneldoc comments is a state like the rest, but it was implemented as a set of separate substates. Just remove the substate logic and make the inline states normal ones like the rest. INLINE_ERROR was never actually used for anything, so just take it out. No changes to the generated output. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250627184000.132291-8-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 43 +++++++++++++---------------------------- 1 file changed, 13 insertions(+), 30 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 03a0e44707a7..a931c1471fa8 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -91,7 +91,8 @@ class state: SPECIAL_SECTION = 4 # doc section ending with a blank line PROTO = 5 # scanning prototype DOCBLOCK = 6 # documentation block - INLINE = 7 # gathering doc outside main block + INLINE_NAME = 7 # gathering doc outside main block + INLINE_TEXT = 8 # reading the body of inline docs name = [ "NORMAL", @@ -101,23 +102,10 @@ class state: "SPECIAL_SECTION", "PROTO", "DOCBLOCK", - "INLINE", + "INLINE_NAME", + "INLINE_TEXT", ] - # Inline documentation state - INLINE_NA = 0 # not applicable ($state != INLINE) - INLINE_NAME = 1 # looking for member name (@foo:) - INLINE_TEXT = 2 # looking for member documentation - INLINE_ERROR = 3 # error - Comment without header was found. - # Spit a warning as it's not - # proper kernel-doc and ignore the rest. - - inline_name = [ - "", - "_NAME", - "_TEXT", - "_ERROR", - ] SECTION_DEFAULT = "Description" # default section @@ -246,7 +234,6 @@ class KernelDoc: # Initial state for the state machines self.state = state.NORMAL - self.inline_doc_state = state.INLINE_NA # Store entry currently being processed self.entry = None @@ -323,7 +310,6 @@ class KernelDoc: # State flags self.state = state.NORMAL - self.inline_doc_state = state.INLINE_NA def push_parameter(self, ln, decl_type, param, dtype, org_arg, declaration_name): @@ -1465,30 +1451,28 @@ class KernelDoc: def process_inline(self, ln, line): """STATE_INLINE: docbook comments within a prototype.""" - if self.inline_doc_state == state.INLINE_NAME and \ + if self.state == state.INLINE_NAME and \ doc_inline_sect.search(line): self.entry.begin_section(ln, doc_inline_sect.group(1)) self.entry.add_text(doc_inline_sect.group(2).lstrip()) - self.inline_doc_state = state.INLINE_TEXT + self.state = state.INLINE_TEXT # Documentation block end */ return if doc_inline_end.search(line): self.dump_section() self.state = state.PROTO - self.inline_doc_state = state.INLINE_NA return if doc_content.search(line): - if self.inline_doc_state == state.INLINE_TEXT: + if self.state == state.INLINE_TEXT: self.entry.add_text(doc_content.group(1)) - elif self.inline_doc_state == state.INLINE_NAME: + elif self.state == state.INLINE_NAME: self.emit_msg(ln, f"Incorrect use of kernel-doc format: {line}") - - self.inline_doc_state = state.INLINE_ERROR + self.state = state.PROTO def syscall_munge(self, ln, proto): # pylint: disable=W0613 """ @@ -1664,8 +1648,7 @@ class KernelDoc: self.dump_section() elif doc_inline_start.search(line): - self.state = state.INLINE - self.inline_doc_state = state.INLINE_NAME + self.state = state.INLINE_NAME elif self.entry.decl_type == 'function': self.process_proto_function(ln, line) @@ -1716,7 +1699,8 @@ class KernelDoc: state.BODY: process_body, state.DECLARATION: process_decl, state.SPECIAL_SECTION: process_special, - state.INLINE: process_inline, + state.INLINE_NAME: process_inline, + state.INLINE_TEXT: process_inline, state.PROTO: process_proto, state.DOCBLOCK: process_docblock, } @@ -1756,9 +1740,8 @@ class KernelDoc: prev = "" prev_ln = None - self.config.log.debug("%d %s%s: %s", + self.config.log.debug("%d %s: %s", ln, state.name[self.state], - state.inline_name[self.inline_doc_state], line) # This is an optimization over the original script. -- cgit v1.2.3 From 0cde7924b8de5abec29f03519ce9486b27c809ea Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Fri, 27 Jun 2025 12:40:00 -0600 Subject: docs: kdoc: split the processing of the two remaining inline states Now that "inline_*" are just ordinary parser states, split them into two separate functions, getting rid of some nested conditional logic. The original process_inline() would simply ignore lines that didn't match any of the regexes (those lacking the initial " * " marker). I have preserved that behavior, but we should perhaps emit a warning instead. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250627184000.132291-9-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index a931c1471fa8..93938155fce2 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1448,31 +1448,30 @@ class KernelDoc: # Unknown line, ignore self.emit_msg(ln, f"bad line: {line}") - def process_inline(self, ln, line): - """STATE_INLINE: docbook comments within a prototype.""" + def process_inline_name(self, ln, line): + """STATE_INLINE_NAME: beginning of docbook comments within a prototype.""" - if self.state == state.INLINE_NAME and \ - doc_inline_sect.search(line): + if doc_inline_sect.search(line): self.entry.begin_section(ln, doc_inline_sect.group(1)) - self.entry.add_text(doc_inline_sect.group(2).lstrip()) self.state = state.INLINE_TEXT - # Documentation block end */ - return - - if doc_inline_end.search(line): + elif doc_inline_end.search(line): self.dump_section() self.state = state.PROTO - return + elif doc_content.search(line): + self.emit_msg(ln, f"Incorrect use of kernel-doc format: {line}") + self.state = state.PROTO + # else ... ?? - if doc_content.search(line): - if self.state == state.INLINE_TEXT: - self.entry.add_text(doc_content.group(1)) + def process_inline_text(self, ln, line): + """STATE_INLINE_TEXT: docbook comments within a prototype.""" - elif self.state == state.INLINE_NAME: - self.emit_msg(ln, - f"Incorrect use of kernel-doc format: {line}") - self.state = state.PROTO + if doc_inline_end.search(line): + self.dump_section() + self.state = state.PROTO + elif doc_content.search(line): + self.entry.add_text(doc_content.group(1)) + # else ... ?? def syscall_munge(self, ln, proto): # pylint: disable=W0613 """ @@ -1699,8 +1698,8 @@ class KernelDoc: state.BODY: process_body, state.DECLARATION: process_decl, state.SPECIAL_SECTION: process_special, - state.INLINE_NAME: process_inline, - state.INLINE_TEXT: process_inline, + state.INLINE_NAME: process_inline_name, + state.INLINE_TEXT: process_inline_text, state.PROTO: process_proto, state.DOCBLOCK: process_docblock, } -- cgit v1.2.3 From 1aeb8099d053af79d50f4ffee740c29cc10d56fc Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Mon, 30 Jun 2025 11:08:32 -0600 Subject: docs: kdoc: rework type prototype parsing process_proto_type() is using a complex regex and a "while True" loop to split a declaration into chunks and, in the end, count brackets. Switch to using a simpler regex to just do the split directly, and handle each chunk as it comes. The result is, IMO, easier to understand and reason about. The old algorithm would occasionally elide the space between function parameters; see struct rng_alg->generate(), foe example. The only output difference is to not elide that space, which is more correct. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 43 ++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 18 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 935f2a3c4b47..61da297df623 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1594,30 +1594,37 @@ class KernelDoc: # Strip C99-style comments and surrounding whitespace line = KernRe(r"//.*$", re.S).sub('', line).strip() + if not line: + return # nothing to see here # To distinguish preprocessor directive from regular declaration later. if line.startswith('#'): line += ";" - - r = KernRe(r'([^\{\};]*)([\{\};])(.*)') - while True: - if r.search(line): - if self.entry.prototype: - self.entry.prototype += " " - self.entry.prototype += r.group(1) + r.group(2) - - self.entry.brcount += r.group(2).count('{') - self.entry.brcount -= r.group(2).count('}') - - if r.group(2) == ';' and self.entry.brcount <= 0: + # + # Split the declaration on any of { } or ;, and accumulate pieces + # until we hit a semicolon while not inside {brackets} + # + r = KernRe(r'(.*?)([{};])') + for chunk in r.split(line): + if chunk: # Ignore empty matches + self.entry.prototype += chunk + # + # This cries out for a match statement ... someday after we can + # drop Python 3.9 ... + # + if chunk == '{': + self.entry.brcount += 1 + elif chunk == '}': + self.entry.brcount -= 1 + elif chunk == ';' and self.entry.brcount <= 0: self.dump_declaration(ln, self.entry.prototype) self.reset_state(ln) - break - - line = r.group(3) - else: - self.entry.prototype += line - break + return + # + # We hit the end of the line while still in the declaration; put + # in a space to represent the newline. + # + self.entry.prototype += ' ' def process_proto(self, ln, line): """STATE_PROTO: reading a function/whatever prototype.""" -- cgit v1.2.3 From 901f506945b8d0a9386c126a2af6bec52354f7b3 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Mon, 30 Jun 2025 11:38:42 -0600 Subject: docs: kdoc: some tweaks to process_proto_function() Add a set of comments to process_proto_function and reorganize the logic slightly; no functional change. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 43 +++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 19 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 61da297df623..d5ef3ce87438 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1553,39 +1553,44 @@ class KernelDoc: """Ancillary routine to process a function prototype""" # strip C99-style comments to end of line - r = KernRe(r"\/\/.*$", re.S) - line = r.sub('', line) - + line = KernRe(r"\/\/.*$", re.S).sub('', line) + # + # Soak up the line's worth of prototype text, stopping at { or ; if present. + # if KernRe(r'\s*#\s*define').match(line): self.entry.prototype = line - elif line.startswith('#'): - # Strip other macros like #ifdef/#ifndef/#endif/... - pass - else: + elif not line.startswith('#'): # skip other preprocessor stuff r = KernRe(r'([^\{]*)') if r.match(line): self.entry.prototype += r.group(1) + " " - + # + # If we now have the whole prototype, clean it up and declare victory. + # if '{' in line or ';' in line or KernRe(r'\s*#\s*define').match(line): # strip comments and surrounding spaces - r = KernRe(r'/\*.*\*/') - self.entry.prototype = r.sub('', self.entry.prototype).strip() - + self.entry.prototype = KernRe(r'/\*.*\*/').sub('', self.entry.prototype).strip() + # # Handle self.entry.prototypes for function pointers like: # int (*pcs_config)(struct foo) - + # by turning it into + # int pcs_config(struct foo) + # r = KernRe(r'^(\S+\s+)\(\s*\*(\S+)\)') self.entry.prototype = r.sub(r'\1\2', self.entry.prototype) - + # + # Handle special declaration syntaxes + # if 'SYSCALL_DEFINE' in self.entry.prototype: self.entry.prototype = self.syscall_munge(ln, self.entry.prototype) - - r = KernRe(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT') - if r.search(self.entry.prototype): - self.entry.prototype = self.tracepoint_munge(ln, - self.entry.prototype) - + else: + r = KernRe(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT') + if r.search(self.entry.prototype): + self.entry.prototype = self.tracepoint_munge(ln, + self.entry.prototype) + # + # ... and we're done + # self.dump_function(ln, self.entry.prototype) self.reset_state(ln) -- cgit v1.2.3 From d1af2889682e83acc791e2a2191687958b548da1 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Tue, 1 Jul 2025 13:02:54 -0600 Subject: docs: kdoc: pretty up dump_enum() Add some comments to dump_enum to help the next person who has to figure out what it is actually doing. Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index d5ef3ce87438..831f061f61b8 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -860,39 +860,48 @@ class KernelDoc: # Strip #define macros inside enums proto = KernRe(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=re.S).sub('', proto) - members = None - declaration_name = None - + # + # Parse out the name and members of the enum. Typedef form first. + # r = KernRe(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;') if r.search(proto): declaration_name = r.group(2) members = r.group(1).rstrip() + # + # Failing that, look for a straight enum + # else: r = KernRe(r'enum\s+(\w*)\s*\{(.*)\}') if r.match(proto): declaration_name = r.group(1) members = r.group(2).rstrip() - - if not members: - self.emit_msg(ln, f"{proto}: error: Cannot parse enum!") - return - + # + # OK, this isn't going to work. + # + else: + self.emit_msg(ln, f"{proto}: error: Cannot parse enum!") + return + # + # Make sure we found what we were expecting. + # if self.entry.identifier != declaration_name: if self.entry.identifier == "": self.emit_msg(ln, f"{proto}: wrong kernel-doc identifier on prototype") else: self.emit_msg(ln, - f"expecting prototype for enum {self.entry.identifier}. Prototype was for enum {declaration_name} instead") + f"expecting prototype for enum {self.entry.identifier}. " + f"Prototype was for enum {declaration_name} instead") return if not declaration_name: declaration_name = "(anonymous)" - + # + # Parse out the name of each enum member, and verify that we + # have a description for it. + # member_set = set() - - members = KernRe(r'\([^;]*?[\)]').sub('', members) - + members = KernRe(r'\([^;)]*\)').sub('', members) for arg in members.split(','): if not arg: continue @@ -903,7 +912,9 @@ class KernelDoc: self.emit_msg(ln, f"Enum value '{arg}' not described in enum '{declaration_name}'") member_set.add(arg) - + # + # Ensure that every described member actually exists in the enum. + # for k in self.entry.parameterdescs: if k not in member_set: self.emit_msg(ln, -- cgit v1.2.3 From 8d6b5c14ab0bdca5b8a7583ccbec4a092e3458bf Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:13 +0900 Subject: kconfig: gconf: refactor view setting code Factor out common code for setting the view into a new function, set_view_mode(). Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap --- scripts/kconfig/gconf.c | 74 +++++++++++++++++++++++++------------------------ 1 file changed, 38 insertions(+), 36 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 9d06c050b270..185084fccc23 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -18,7 +18,7 @@ #include #include -enum { +enum view_mode { SINGLE_VIEW, SPLIT_VIEW, FULL_VIEW }; @@ -116,6 +116,39 @@ static void text_insert_msg(const char *title, const char *message) NULL); } +static void set_view_mode(enum view_mode mode) +{ + view_mode = mode; + + if (mode == SPLIT_VIEW) { // two panes + gint w; + + gtk_widget_show(tree1_w); + gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, NULL); + gtk_paned_set_position(GTK_PANED(hpaned), w / 2); + } else { + gtk_widget_hide(tree1_w); + gtk_paned_set_position(GTK_PANED(hpaned), 0); + } + + switch (mode) { + case SINGLE_VIEW: + current = &rootmenu; + display_tree_part(); + break; + case SPLIT_VIEW: + gtk_tree_store_clear(tree2); + display_list(); + break; + case FULL_VIEW: + gtk_tree_store_clear(tree2); + display_tree(&rootmenu); + break; + } + + if (mode != SINGLE_VIEW) + gtk_widget_set_sensitive(back_btn, FALSE); +} /* Main Windows Callbacks */ @@ -435,35 +468,19 @@ void on_load_clicked(GtkButton * button, gpointer user_data) void on_single_clicked(GtkButton * button, gpointer user_data) { - view_mode = SINGLE_VIEW; - gtk_widget_hide(tree1_w); - current = &rootmenu; - display_tree_part(); + set_view_mode(SINGLE_VIEW); } void on_split_clicked(GtkButton * button, gpointer user_data) { - gint w; - view_mode = SPLIT_VIEW; - gtk_widget_show(tree1_w); - gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, NULL); - gtk_paned_set_position(GTK_PANED(hpaned), w / 2); - gtk_tree_store_clear(tree2); - display_list(); - - /* Disable back btn, like in full mode. */ - gtk_widget_set_sensitive(back_btn, FALSE); + set_view_mode(SPLIT_VIEW); } void on_full_clicked(GtkButton * button, gpointer user_data) { - view_mode = FULL_VIEW; - gtk_widget_hide(tree1_w); - gtk_tree_store_clear(tree2); - display_tree(&rootmenu); - gtk_widget_set_sensitive(back_btn, FALSE); + set_view_mode(FULL_VIEW); } @@ -1039,11 +1056,6 @@ static void _display_tree(struct menu *menu, GtkTreeIter *parent) || (view_mode == FULL_VIEW) || (view_mode == SPLIT_VIEW))*/ - /* Change paned position if the view is not in 'split mode' */ - if (view_mode == SINGLE_VIEW || view_mode == FULL_VIEW) { - gtk_paned_set_position(GTK_PANED(hpaned), 0); - } - if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT)) || (view_mode == FULL_VIEW) || (view_mode == SPLIT_VIEW)) @@ -1368,17 +1380,7 @@ int main(int ac, char *av[]) conf_read(NULL); - switch (view_mode) { - case SINGLE_VIEW: - display_tree_part(); - break; - case SPLIT_VIEW: - display_list(); - break; - case FULL_VIEW: - display_tree(&rootmenu); - break; - } + set_view_mode(view_mode); gtk_main(); -- cgit v1.2.3 From b22bbaea7f59ea1fa609462bed2eb075eea97586 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:14 +0900 Subject: kconfig: gconf: grey out button for current view This clarifies which view is currently selected. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/gconf.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 185084fccc23..77e742eebf24 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -38,8 +38,7 @@ static GtkWidget *tree2_w; // right frame static GtkWidget *text_w; static GtkWidget *hpaned; static GtkWidget *vpaned; -static GtkWidget *back_btn; -static GtkWidget *save_btn; +static GtkWidget *back_btn, *save_btn, *single_btn, *split_btn, *full_btn; static GtkWidget *save_menu_item; static GtkTextTag *tag1, *tag2; @@ -131,18 +130,25 @@ static void set_view_mode(enum view_mode mode) gtk_paned_set_position(GTK_PANED(hpaned), 0); } + gtk_widget_set_sensitive(single_btn, TRUE); + gtk_widget_set_sensitive(split_btn, TRUE); + gtk_widget_set_sensitive(full_btn, TRUE); + switch (mode) { case SINGLE_VIEW: current = &rootmenu; display_tree_part(); + gtk_widget_set_sensitive(single_btn, FALSE); break; case SPLIT_VIEW: gtk_tree_store_clear(tree2); display_list(); + gtk_widget_set_sensitive(split_btn, FALSE); break; case FULL_VIEW: gtk_tree_store_clear(tree2); display_tree(&rootmenu); + gtk_widget_set_sensitive(full_btn, FALSE); break; } @@ -1167,10 +1173,15 @@ static void init_main_window(const gchar *glade_file) style = gtk_widget_get_style(main_wnd); + single_btn = glade_xml_get_widget(xml, "button4"); replace_button_icon(xml, main_wnd->window, style, "button4", (gchar **) xpm_single_view); + + split_btn = glade_xml_get_widget(xml, "button5"); replace_button_icon(xml, main_wnd->window, style, "button5", (gchar **) xpm_split_view); + + full_btn = glade_xml_get_widget(xml, "button6"); replace_button_icon(xml, main_wnd->window, style, "button6", (gchar **) xpm_tree_view); -- cgit v1.2.3 From 3e0fb3ef01584bcace87c42a4f96abacad624386 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:15 +0900 Subject: kconfig: gconf: move the main window event handlers below This allows removal of the forward delcaration of on_save_activate(). Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 163 +++++++++++++++++++++++------------------------- 1 file changed, 78 insertions(+), 85 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 77e742eebf24..d265d25f72cb 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -156,83 +156,6 @@ static void set_view_mode(enum view_mode mode) gtk_widget_set_sensitive(back_btn, FALSE); } -/* Main Windows Callbacks */ - -void on_save_activate(GtkMenuItem * menuitem, gpointer user_data); -gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event, - gpointer user_data) -{ - GtkWidget *dialog, *label; - gint result; - gint ret = FALSE; - - if (!conf_get_changed()) - return FALSE; - - dialog = gtk_dialog_new_with_buttons("Warning !", - GTK_WINDOW(main_wnd), - (GtkDialogFlags) - (GTK_DIALOG_MODAL | - GTK_DIALOG_DESTROY_WITH_PARENT), - GTK_STOCK_OK, - GTK_RESPONSE_YES, - GTK_STOCK_NO, - GTK_RESPONSE_NO, - GTK_STOCK_CANCEL, - GTK_RESPONSE_CANCEL, NULL); - gtk_dialog_set_default_response(GTK_DIALOG(dialog), - GTK_RESPONSE_CANCEL); - - label = gtk_label_new("\nSave configuration ?\n"); - gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label); - gtk_widget_show(label); - - result = gtk_dialog_run(GTK_DIALOG(dialog)); - switch (result) { - case GTK_RESPONSE_YES: - on_save_activate(NULL, NULL); - break; - case GTK_RESPONSE_NO: - break; - case GTK_RESPONSE_CANCEL: - case GTK_RESPONSE_DELETE_EVENT: - default: - ret = TRUE; - break; - } - - gtk_widget_destroy(dialog); - - return ret; -} - - -void on_window1_destroy(GtkObject * object, gpointer user_data) -{ - gtk_main_quit(); -} - - -void -on_window1_size_request(GtkWidget * widget, - GtkRequisition * requisition, gpointer user_data) -{ - static gint old_h; - gint w, h; - - if (widget->window == NULL) - gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); - else - gdk_window_get_size(widget->window, &w, &h); - - if (h == old_h) - return; - old_h = h; - - gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3); -} - - /* Menu & Toolbar Callbacks */ @@ -311,14 +234,6 @@ void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data) gtk_widget_show(fs); } - -void on_quit1_activate(GtkMenuItem * menuitem, gpointer user_data) -{ - if (!on_window1_delete_event(NULL, NULL, NULL)) - gtk_widget_destroy(GTK_WIDGET(main_wnd)); -} - - void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data) { GtkTreeViewColumn *col; @@ -501,6 +416,84 @@ void on_expand_clicked(GtkButton * button, gpointer user_data) gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); } +/* Main Windows Callbacks */ + +void on_window1_destroy(GtkObject *object, gpointer user_data) +{ + gtk_main_quit(); +} + +void on_window1_size_request(GtkWidget *widget, + GtkRequisition *requisition, + gpointer user_data) +{ + static gint old_h; + gint w, h; + + if (widget->window == NULL) + gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); + else + gdk_window_get_size(widget->window, &w, &h); + + if (h == old_h) + return; + old_h = h; + + gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3); +} + +gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, + gpointer user_data) +{ + GtkWidget *dialog, *label; + gint result; + gint ret = FALSE; + + if (!conf_get_changed()) + return FALSE; + + dialog = gtk_dialog_new_with_buttons("Warning !", + GTK_WINDOW(main_wnd), + (GtkDialogFlags) + (GTK_DIALOG_MODAL | + GTK_DIALOG_DESTROY_WITH_PARENT), + GTK_STOCK_OK, + GTK_RESPONSE_YES, + GTK_STOCK_NO, + GTK_RESPONSE_NO, + GTK_STOCK_CANCEL, + GTK_RESPONSE_CANCEL, NULL); + gtk_dialog_set_default_response(GTK_DIALOG(dialog), + GTK_RESPONSE_CANCEL); + + label = gtk_label_new("\nSave configuration ?\n"); + gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label); + gtk_widget_show(label); + + result = gtk_dialog_run(GTK_DIALOG(dialog)); + switch (result) { + case GTK_RESPONSE_YES: + on_save_activate(NULL, NULL); + break; + case GTK_RESPONSE_NO: + break; + case GTK_RESPONSE_CANCEL: + case GTK_RESPONSE_DELETE_EVENT: + default: + ret = TRUE; + break; + } + + gtk_widget_destroy(dialog); + + return ret; +} + +void on_quit1_activate(GtkMenuItem *menuitem, gpointer user_data) +{ + if (!on_window1_delete_event(NULL, NULL, NULL)) + gtk_widget_destroy(GTK_WIDGET(main_wnd)); +} /* CTree Callbacks */ -- cgit v1.2.3 From f0049c937d2f38ae208c06aa5ef114dac226d01c Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:16 +0900 Subject: kconfig: gconf: move button1 and save1 initialization code Move the relevant initialization code closer together. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index d265d25f72cb..3a7bb27b4871 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1145,8 +1145,8 @@ static void init_main_window(const gchar *glade_file) tree2_w = glade_xml_get_widget(xml, "treeview2"); text_w = glade_xml_get_widget(xml, "textview3"); - back_btn = glade_xml_get_widget(xml, "button1"); - gtk_widget_set_sensitive(back_btn, FALSE); + /* menubar */ + save_menu_item = glade_xml_get_widget(xml, "save1"); widget = glade_xml_get_widget(xml, "show_name1"); gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, @@ -1160,9 +1160,11 @@ static void init_main_window(const gchar *glade_file) gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, show_value); + /* toolbar */ + back_btn = glade_xml_get_widget(xml, "button1"); + gtk_widget_set_sensitive(back_btn, FALSE); + save_btn = glade_xml_get_widget(xml, "button3"); - save_menu_item = glade_xml_get_widget(xml, "save1"); - conf_set_changed_callback(conf_changed); style = gtk_widget_get_style(main_wnd); @@ -1190,6 +1192,8 @@ static void init_main_window(const gchar *glade_file) gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text); gtk_widget_show(main_wnd); + + conf_set_changed_callback(conf_changed); } static void init_tree_model(void) -- cgit v1.2.3 From 77e8ff988918de554e0176c4ce0064944935efb7 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:17 +0900 Subject: kconfig: gconf: add static qualifiers to event handlers This fixes several -Wmissing-prototypes warnings. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap --- scripts/kconfig/gconf.c | 178 +++++++++++++++++++++++++++++++------------- scripts/kconfig/gconf.glade | 30 -------- 2 files changed, 125 insertions(+), 83 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 3a7bb27b4871..3b4bd897856c 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -173,7 +173,7 @@ load_filename(GtkFileSelection * file_selector, gpointer user_data) display_tree_part(); } -void on_load1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_load1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkWidget *fs; @@ -192,8 +192,7 @@ void on_load1_activate(GtkMenuItem * menuitem, gpointer user_data) gtk_widget_show(fs); } - -void on_save_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_save_activate(GtkMenuItem *menuitem, gpointer user_data) { if (conf_write(NULL)) text_insert_msg("Error", "Unable to save configuration !"); @@ -215,7 +214,7 @@ store_filename(GtkFileSelection * file_selector, gpointer user_data) gtk_widget_destroy(GTK_WIDGET(user_data)); } -void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_save_as1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkWidget *fs; @@ -234,7 +233,7 @@ void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data) gtk_widget_show(fs); } -void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_show_name1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkTreeViewColumn *col; @@ -244,8 +243,7 @@ void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data) gtk_tree_view_column_set_visible(col, show_name); } - -void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_show_range1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkTreeViewColumn *col; @@ -262,8 +260,7 @@ void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data) } - -void on_show_data1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_show_data1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkTreeViewColumn *col; @@ -273,35 +270,31 @@ void on_show_data1_activate(GtkMenuItem * menuitem, gpointer user_data) gtk_tree_view_column_set_visible(col, show_value); } - -void -on_set_option_mode1_activate(GtkMenuItem *menuitem, gpointer user_data) +static void on_set_option_mode1_activate(GtkMenuItem *menuitem, + gpointer user_data) { opt_mode = OPT_NORMAL; gtk_tree_store_clear(tree2); display_tree(&rootmenu); /* instead of update_tree to speed-up */ } - -void -on_set_option_mode2_activate(GtkMenuItem *menuitem, gpointer user_data) +static void on_set_option_mode2_activate(GtkMenuItem *menuitem, + gpointer user_data) { opt_mode = OPT_ALL; gtk_tree_store_clear(tree2); display_tree(&rootmenu); /* instead of update_tree to speed-up */ } - -void -on_set_option_mode3_activate(GtkMenuItem *menuitem, gpointer user_data) +static void on_set_option_mode3_activate(GtkMenuItem *menuitem, + gpointer user_data) { opt_mode = OPT_PROMPT; gtk_tree_store_clear(tree2); display_tree(&rootmenu); /* instead of update_tree to speed-up */ } - -void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_introduction1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkWidget *dialog; const gchar *intro_text = @@ -328,8 +321,7 @@ void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data) gtk_widget_show_all(dialog); } - -void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_about1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkWidget *dialog; const gchar *about_text = @@ -346,8 +338,7 @@ void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data) gtk_widget_show_all(dialog); } - -void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data) +static void on_license1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkWidget *dialog; const gchar *license_text = @@ -365,8 +356,8 @@ void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data) gtk_widget_show_all(dialog); } - -void on_back_clicked(GtkButton * button, gpointer user_data) +/* toolbar handlers */ +static void on_back_clicked(GtkButton *button, gpointer user_data) { enum prop_type ptype; @@ -380,50 +371,44 @@ void on_back_clicked(GtkButton * button, gpointer user_data) gtk_widget_set_sensitive(back_btn, FALSE); } - -void on_load_clicked(GtkButton * button, gpointer user_data) +static void on_load_clicked(GtkButton *button, gpointer user_data) { on_load1_activate(NULL, user_data); } - -void on_single_clicked(GtkButton * button, gpointer user_data) +static void on_single_clicked(GtkButton *button, gpointer user_data) { set_view_mode(SINGLE_VIEW); } - -void on_split_clicked(GtkButton * button, gpointer user_data) +static void on_split_clicked(GtkButton *button, gpointer user_data) { set_view_mode(SPLIT_VIEW); } - -void on_full_clicked(GtkButton * button, gpointer user_data) +static void on_full_clicked(GtkButton *button, gpointer user_data) { set_view_mode(FULL_VIEW); } - -void on_collapse_clicked(GtkButton * button, gpointer user_data) +static void on_collapse_clicked(GtkButton *button, gpointer user_data) { gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w)); } - -void on_expand_clicked(GtkButton * button, gpointer user_data) +static void on_expand_clicked(GtkButton *button, gpointer user_data) { gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); } /* Main Windows Callbacks */ -void on_window1_destroy(GtkObject *object, gpointer user_data) +static void on_window1_destroy(GtkObject *object, gpointer user_data) { gtk_main_quit(); } -void on_window1_size_request(GtkWidget *widget, +static void on_window1_size_request(GtkWidget *widget, GtkRequisition *requisition, gpointer user_data) { @@ -442,7 +427,7 @@ void on_window1_size_request(GtkWidget *widget, gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3); } -gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, +static gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_data) { GtkWidget *dialog, *label; @@ -489,7 +474,7 @@ gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, return ret; } -void on_quit1_activate(GtkMenuItem *menuitem, gpointer user_data) +static void on_quit1_activate(GtkMenuItem *menuitem, gpointer user_data) { if (!on_window1_delete_event(NULL, NULL, NULL)) gtk_widget_destroy(GTK_WIDGET(main_wnd)); @@ -599,9 +584,9 @@ static gint column2index(GtkTreeViewColumn * column) /* User click: update choice (full) or goes down (single) */ -gboolean -on_treeview2_button_press_event(GtkWidget * widget, - GdkEventButton * event, gpointer user_data) +static gboolean on_treeview2_button_press_event(GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) { GtkTreeView *view = GTK_TREE_VIEW(widget); GtkTreePath *path; @@ -649,9 +634,9 @@ on_treeview2_button_press_event(GtkWidget * widget, } /* Key pressed: update choice */ -gboolean -on_treeview2_key_press_event(GtkWidget * widget, - GdkEventKey * event, gpointer user_data) +static gboolean on_treeview2_key_press_event(GtkWidget *widget, + GdkEventKey *event, + gpointer user_data) { GtkTreeView *view = GTK_TREE_VIEW(widget); GtkTreePath *path; @@ -691,8 +676,8 @@ on_treeview2_key_press_event(GtkWidget * widget, /* Row selection changed: update help */ -void -on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data) +static void on_treeview2_cursor_changed(GtkTreeView *treeview, + gpointer user_data) { GtkTreeSelection *selection; GtkTreeIter iter; @@ -707,9 +692,9 @@ on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data) /* User click: display sub-tree in the right frame. */ -gboolean -on_treeview1_button_press_event(GtkWidget * widget, - GdkEventButton * event, gpointer user_data) +static gboolean on_treeview1_button_press_event(GtkWidget *widget, + GdkEventButton *event, + gpointer user_data) { GtkTreeView *view = GTK_TREE_VIEW(widget); GtkTreePath *path; @@ -1139,47 +1124,134 @@ static void init_main_window(const gchar *glade_file) glade_xml_signal_autoconnect(xml); main_wnd = glade_xml_get_widget(xml, "window1"); + g_signal_connect(main_wnd, "destroy", + G_CALLBACK(on_window1_destroy), NULL); + g_signal_connect(main_wnd, "size_request", + G_CALLBACK(on_window1_size_request), NULL); + g_signal_connect(main_wnd, "delete_event", + G_CALLBACK(on_window1_delete_event), NULL); + hpaned = glade_xml_get_widget(xml, "hpaned1"); vpaned = glade_xml_get_widget(xml, "vpaned1"); tree1_w = glade_xml_get_widget(xml, "treeview1"); + g_signal_connect(tree1_w, "cursor_changed", + G_CALLBACK(on_treeview2_cursor_changed), NULL); + g_signal_connect(tree1_w, "button_press_event", + G_CALLBACK(on_treeview1_button_press_event), NULL); + g_signal_connect(tree1_w, "key_press_event", + G_CALLBACK(on_treeview2_key_press_event), NULL); + tree2_w = glade_xml_get_widget(xml, "treeview2"); + g_signal_connect(tree2_w, "cursor_changed", + G_CALLBACK(on_treeview2_cursor_changed), NULL); + g_signal_connect(tree2_w, "button_press_event", + G_CALLBACK(on_treeview2_button_press_event), NULL); + g_signal_connect(tree2_w, "key_press_event", + G_CALLBACK(on_treeview2_key_press_event), NULL); + text_w = glade_xml_get_widget(xml, "textview3"); /* menubar */ + widget = glade_xml_get_widget(xml, "load1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_load1_activate), NULL); + save_menu_item = glade_xml_get_widget(xml, "save1"); + g_signal_connect(save_menu_item, "activate", + G_CALLBACK(on_save_activate), NULL); + + widget = glade_xml_get_widget(xml, "save_as1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_save_as1_activate), NULL); + + widget = glade_xml_get_widget(xml, "quit1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_quit1_activate), NULL); widget = glade_xml_get_widget(xml, "show_name1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_show_name1_activate), NULL); gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, show_name); widget = glade_xml_get_widget(xml, "show_range1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_show_range1_activate), NULL); gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, show_range); widget = glade_xml_get_widget(xml, "show_data1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_show_data1_activate), NULL); gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, show_value); + widget = glade_xml_get_widget(xml, "set_option_mode1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_set_option_mode1_activate), NULL); + + widget = glade_xml_get_widget(xml, "set_option_mode2"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_set_option_mode2_activate), NULL); + + widget = glade_xml_get_widget(xml, "set_option_mode3"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_set_option_mode3_activate), NULL); + + widget = glade_xml_get_widget(xml, "introduction1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_introduction1_activate), NULL); + + widget = glade_xml_get_widget(xml, "about1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_about1_activate), NULL); + + widget = glade_xml_get_widget(xml, "license1"); + g_signal_connect(widget, "activate", + G_CALLBACK(on_license1_activate), NULL); + /* toolbar */ back_btn = glade_xml_get_widget(xml, "button1"); + g_signal_connect(back_btn, "clicked", + G_CALLBACK(on_back_clicked), NULL); gtk_widget_set_sensitive(back_btn, FALSE); + widget = glade_xml_get_widget(xml, "button2"); + g_signal_connect(widget, "clicked", + G_CALLBACK(on_load_clicked), NULL); + save_btn = glade_xml_get_widget(xml, "button3"); + g_signal_connect(save_btn, "clicked", + G_CALLBACK(on_save_activate), NULL); style = gtk_widget_get_style(main_wnd); single_btn = glade_xml_get_widget(xml, "button4"); + g_signal_connect(single_btn, "clicked", + G_CALLBACK(on_single_clicked), NULL); replace_button_icon(xml, main_wnd->window, style, "button4", (gchar **) xpm_single_view); split_btn = glade_xml_get_widget(xml, "button5"); + g_signal_connect(split_btn, "clicked", + G_CALLBACK(on_split_clicked), NULL); replace_button_icon(xml, main_wnd->window, style, "button5", (gchar **) xpm_split_view); full_btn = glade_xml_get_widget(xml, "button6"); + g_signal_connect(full_btn, "clicked", + G_CALLBACK(on_full_clicked), NULL); replace_button_icon(xml, main_wnd->window, style, "button6", (gchar **) xpm_tree_view); + widget = glade_xml_get_widget(xml, "button7"); + g_signal_connect(widget, "clicked", + G_CALLBACK(on_collapse_clicked), NULL); + + widget = glade_xml_get_widget(xml, "button8"); + g_signal_connect(widget, "clicked", + G_CALLBACK(on_expand_clicked), NULL); + txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1", "foreground", "red", diff --git a/scripts/kconfig/gconf.glade b/scripts/kconfig/gconf.glade index 19b80f2ec1ff..8519104a3c2b 100644 --- a/scripts/kconfig/gconf.glade +++ b/scripts/kconfig/gconf.glade @@ -17,9 +17,6 @@ False GDK_WINDOW_TYPE_HINT_NORMAL GDK_GRAVITY_NORTH_WEST - - - @@ -46,7 +43,6 @@ Load a config file _Load True - @@ -57,7 +53,6 @@ Save the config in .config _Save True - @@ -68,7 +63,6 @@ Save the config in a file Save _as True - @@ -83,7 +77,6 @@ True _Quit True - @@ -108,7 +101,6 @@ Show _name True False - @@ -119,7 +111,6 @@ Show _range True False - @@ -130,7 +121,6 @@ Show _data True False - @@ -147,7 +137,6 @@ Show normal options True True - @@ -159,7 +148,6 @@ True False set_option_mode1 - @@ -171,7 +159,6 @@ True False set_option_mode1 - @@ -194,7 +181,6 @@ True _Introduction True - @@ -204,7 +190,6 @@ True _About True - @@ -214,7 +199,6 @@ True _License True - @@ -254,7 +238,6 @@ True True False - False @@ -291,7 +274,6 @@ True True False - False @@ -309,7 +291,6 @@ True True False - False @@ -346,7 +327,6 @@ True True False - False @@ -364,7 +344,6 @@ True True False - False @@ -382,7 +361,6 @@ True True False - False @@ -419,7 +397,6 @@ True True False - False @@ -437,7 +414,6 @@ True True False - False @@ -477,9 +453,6 @@ False False False - - - @@ -512,9 +485,6 @@ False False False - - - -- cgit v1.2.3 From e06b176bf1b4a779f76b686ab5230dce45a8360e Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:18 +0900 Subject: kconfig: gconf: remove glade_xml_signal_autoconnect() call Now that all signals are connected manually, this is no longer necessary. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 1 - 1 file changed, 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 3b4bd897856c..1c2fd71369f0 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1121,7 +1121,6 @@ static void init_main_window(const gchar *glade_file) xml = glade_xml_new(glade_file, "window1", NULL); if (!xml) g_error("GUI loading failed !\n"); - glade_xml_signal_autoconnect(xml); main_wnd = glade_xml_get_widget(xml, "window1"); g_signal_connect(main_wnd, "destroy", -- cgit v1.2.3 From 3beae8659513550b6b82a4ccdc4d25be9497f208 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:19 +0900 Subject: kconfig: gconf: make key_press_event work in left pane too Currently, on_treeview2_key_press_event() returns early for the tree1 widget. We can make it work on the left pane as well by avoiding the hardcoded use of model2. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 1c2fd71369f0..4acbcf912c6e 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -639,6 +639,7 @@ static gboolean on_treeview2_key_press_event(GtkWidget *widget, gpointer user_data) { GtkTreeView *view = GTK_TREE_VIEW(widget); + GtkTreeModel *model = gtk_tree_view_get_model(view); GtkTreePath *path; GtkTreeIter iter; struct menu *menu; @@ -655,11 +656,9 @@ static gboolean on_treeview2_key_press_event(GtkWidget *widget, gtk_tree_view_expand_row(view, path, FALSE); return TRUE; } - if (widget == tree1_w) - return FALSE; - gtk_tree_model_get_iter(model2, &iter, path); - gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); if (!strcasecmp(event->string, "n")) col = COL_NO; -- cgit v1.2.3 From cae9cdbcd9af044810bcceeb43a87accca47c71d Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:20 +0900 Subject: kconfig: gconf: avoid hardcoding model2 in on_treeview2_cursor_changed() The on_treeview2_cursor_changed() handler is connected to both the left and right tree views, but it hardcodes model2 (the GtkTreeModel of the right tree view). This is incorrect. Get the associated model from the view. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 4acbcf912c6e..d7aa7bad965f 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -678,13 +678,14 @@ static gboolean on_treeview2_key_press_event(GtkWidget *widget, static void on_treeview2_cursor_changed(GtkTreeView *treeview, gpointer user_data) { + GtkTreeModel *model = gtk_tree_view_get_model(treeview); GtkTreeSelection *selection; GtkTreeIter iter; struct menu *menu; selection = gtk_tree_view_get_selection(treeview); - if (gtk_tree_selection_get_selected(selection, &model2, &iter)) { - gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + if (gtk_tree_selection_get_selected(selection, &model, &iter)) { + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); text_insert_help(menu); } } -- cgit v1.2.3 From e6991e8004bf1ff8fc31b14833c4995672f18b04 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:21 +0900 Subject: kconfig: gconf: avoid hardcoding model2 in renderer_edited() Although this is only used in the right tree view, it is better not to hardcode model2 for consistency. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index d7aa7bad965f..df822f4e13c5 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -487,19 +487,21 @@ static void renderer_edited(GtkCellRendererText * cell, const gchar * path_string, const gchar * new_text, gpointer user_data) { + GtkTreeView *view = GTK_TREE_VIEW(user_data); + GtkTreeModel *model = gtk_tree_view_get_model(view); GtkTreePath *path = gtk_tree_path_new_from_string(path_string); GtkTreeIter iter; const char *old_def, *new_def; struct menu *menu; struct symbol *sym; - if (!gtk_tree_model_get_iter(model2, &iter, path)) + if (!gtk_tree_model_get_iter(model, &iter, path)) goto free; - gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); sym = menu->sym; - gtk_tree_model_get(model2, &iter, COL_VALUE, &old_def, -1); + gtk_tree_model_get(model, &iter, COL_VALUE, &old_def, -1); new_def = new_text; sym_set_string_value(sym, new_def); @@ -1399,7 +1401,7 @@ static void init_right_tree(void) "foreground-gdk", COL_COLOR, NULL); g_signal_connect(G_OBJECT(renderer), "edited", - G_CALLBACK(renderer_edited), NULL); + G_CALLBACK(renderer_edited), tree2_w); for (i = 0; i < COL_VALUE; i++) { column = gtk_tree_view_get_column(view, i); -- cgit v1.2.3 From 59adbcd8051a222023f52cfac0143d927735b194 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:22 +0900 Subject: kconfig: gconf: avoid hardcoding model* in on_treeview*_button_press_event() It is better not to hardcode model1 or model2 for consistency. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index df822f4e13c5..f03e94cd5fa3 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -591,6 +591,7 @@ static gboolean on_treeview2_button_press_event(GtkWidget *widget, gpointer user_data) { GtkTreeView *view = GTK_TREE_VIEW(widget); + GtkTreeModel *model = gtk_tree_view_get_model(view); GtkTreePath *path; GtkTreeViewColumn *column; GtkTreeIter iter; @@ -603,9 +604,9 @@ static gboolean on_treeview2_button_press_event(GtkWidget *widget, if (path == NULL) return FALSE; - if (!gtk_tree_model_get_iter(model2, &iter, path)) + if (!gtk_tree_model_get_iter(model, &iter, path)) return FALSE; - gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1); + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); col = column2index(column); if (event->type == GDK_2BUTTON_PRESS) { @@ -699,6 +700,7 @@ static gboolean on_treeview1_button_press_event(GtkWidget *widget, gpointer user_data) { GtkTreeView *view = GTK_TREE_VIEW(widget); + GtkTreeModel *model = gtk_tree_view_get_model(view); GtkTreePath *path; GtkTreeIter iter; struct menu *menu; @@ -709,8 +711,8 @@ static gboolean on_treeview1_button_press_event(GtkWidget *widget, if (path == NULL) return FALSE; - gtk_tree_model_get_iter(model1, &iter, path); - gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1); + gtk_tree_model_get_iter(model, &iter, path); + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); if (event->type == GDK_2BUTTON_PRESS) { toggle_sym_value(menu); -- cgit v1.2.3 From 4d89059a722d9a562bfe6ee1e3941ccc3c6c70b6 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:23 +0900 Subject: kconfig: gconf: add on_save_clicked() event handler The "clicked" event handler for GtkToolButton takes the GtkToolButton* as the first parameter. This is different from the existing on_save_activate() handler. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index f03e94cd5fa3..4e21cf46ff01 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -376,6 +376,11 @@ static void on_load_clicked(GtkButton *button, gpointer user_data) on_load1_activate(NULL, user_data); } +static void on_save_clicked(GtkButton *button, gpointer user_data) +{ + on_save_activate(NULL, user_data); +} + static void on_single_clicked(GtkButton *button, gpointer user_data) { set_view_mode(SINGLE_VIEW); @@ -1225,7 +1230,7 @@ static void init_main_window(const gchar *glade_file) save_btn = glade_xml_get_widget(xml, "button3"); g_signal_connect(save_btn, "clicked", - G_CALLBACK(on_save_activate), NULL); + G_CALLBACK(on_save_clicked), NULL); style = gtk_widget_get_style(main_wnd); -- cgit v1.2.3 From 30dda0fdf7a6655bf180b61063087aed28812007 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:24 +0900 Subject: kconfig: gconf: use GtkFileChooser in on_load1_activate() gtk_file_selection_new() is deprecated, and gtk_file_chooser_dialog_new() should be used instead. [1] [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/docs/reference/gtk/tmpl/gtkfilesel.sgml?ref_type=tags#L156 Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/gconf.c | 54 ++++++++++++++++++++++++------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 4e21cf46ff01..20a20a5888b9 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -158,38 +158,38 @@ static void set_view_mode(enum view_mode mode) /* Menu & Toolbar Callbacks */ - -static void -load_filename(GtkFileSelection * file_selector, gpointer user_data) +static void on_load1_activate(GtkMenuItem *menuitem, gpointer user_data) { - const gchar *fn; + GtkWidget *dialog; + GtkFileChooser *chooser; + gint res; - fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION - (user_data)); + dialog = gtk_file_chooser_dialog_new("Load file...", + GTK_WINDOW(user_data), + GTK_FILE_CHOOSER_ACTION_OPEN, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Open", GTK_RESPONSE_ACCEPT, + NULL); - if (conf_read(fn)) - text_insert_msg("Error", "Unable to load configuration !"); - else - display_tree_part(); -} + chooser = GTK_FILE_CHOOSER(dialog); + gtk_file_chooser_set_filename(chooser, conf_get_configname()); -static void on_load1_activate(GtkMenuItem *menuitem, gpointer user_data) -{ - GtkWidget *fs; + res = gtk_dialog_run(GTK_DIALOG(dialog)); + if (res == GTK_RESPONSE_ACCEPT) { + char *filename; - fs = gtk_file_selection_new("Load file..."); - g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), - "clicked", - G_CALLBACK(load_filename), (gpointer) fs); - g_signal_connect_swapped(GTK_OBJECT - (GTK_FILE_SELECTION(fs)->ok_button), - "clicked", G_CALLBACK(gtk_widget_destroy), - (gpointer) fs); - g_signal_connect_swapped(GTK_OBJECT - (GTK_FILE_SELECTION(fs)->cancel_button), - "clicked", G_CALLBACK(gtk_widget_destroy), - (gpointer) fs); - gtk_widget_show(fs); + filename = gtk_file_chooser_get_filename(chooser); + + if (conf_read(filename)) + text_insert_msg("Error", + "Unable to load configuration!"); + else + display_tree_part(); + + g_free(filename); + } + + gtk_widget_destroy(GTK_WIDGET(dialog)); } static void on_save_activate(GtkMenuItem *menuitem, gpointer user_data) -- cgit v1.2.3 From fd7fd8024c32ae2037f98d53198e42d5c597ca0b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:25 +0900 Subject: kconfig: gconf: use GtkFileChooser in on_save_as1_activate() gtk_file_selection_new() is deprecated, and gtk_file_chooser_dialog_new() should be used instead. [1] [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/docs/reference/gtk/tmpl/gtkfilesel.sgml?ref_type=tags#L156 Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/gconf.c | 52 ++++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 27 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 20a20a5888b9..82e8edb8a82c 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -199,38 +199,36 @@ static void on_save_activate(GtkMenuItem *menuitem, gpointer user_data) conf_write_autoconf(0); } - -static void -store_filename(GtkFileSelection * file_selector, gpointer user_data) +static void on_save_as1_activate(GtkMenuItem *menuitem, gpointer user_data) { - const gchar *fn; + GtkWidget *dialog; + GtkFileChooser *chooser; + gint res; - fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION - (user_data)); + dialog = gtk_file_chooser_dialog_new("Save file as...", + GTK_WINDOW(user_data), + GTK_FILE_CHOOSER_ACTION_SAVE, + "_Cancel", GTK_RESPONSE_CANCEL, + "_Save", GTK_RESPONSE_ACCEPT, + NULL); - if (conf_write(fn)) - text_insert_msg("Error", "Unable to save configuration !"); + chooser = GTK_FILE_CHOOSER(dialog); + gtk_file_chooser_set_filename(chooser, conf_get_configname()); - gtk_widget_destroy(GTK_WIDGET(user_data)); -} + res = gtk_dialog_run(GTK_DIALOG(dialog)); + if (res == GTK_RESPONSE_ACCEPT) { + char *filename; -static void on_save_as1_activate(GtkMenuItem *menuitem, gpointer user_data) -{ - GtkWidget *fs; - - fs = gtk_file_selection_new("Save file as..."); - g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button), - "clicked", - G_CALLBACK(store_filename), (gpointer) fs); - g_signal_connect_swapped(GTK_OBJECT - (GTK_FILE_SELECTION(fs)->ok_button), - "clicked", G_CALLBACK(gtk_widget_destroy), - (gpointer) fs); - g_signal_connect_swapped(GTK_OBJECT - (GTK_FILE_SELECTION(fs)->cancel_button), - "clicked", G_CALLBACK(gtk_widget_destroy), - (gpointer) fs); - gtk_widget_show(fs); + filename = gtk_file_chooser_get_filename(chooser); + + if (conf_write(filename)) + text_insert_msg("Error", + "Unable to save configuration !"); + + g_free(filename); + } + + gtk_widget_destroy(dialog); } static void on_show_name1_activate(GtkMenuItem *menuitem, gpointer user_data) -- cgit v1.2.3 From 9517f47dbf8ab7a7e554e7b34563982cfc63c366 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:26 +0900 Subject: kconfig: gconf: use GdkPixbuf in replace_button_icon() gdk_pixmap_create_from_xpm_d has been deprecated since version 2.22. Use a GdkPixbuf instead. You can use gdk_pixbuf_new_from_xpm_data() to create it. [1] [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/gdk/gdkpixmap.c#L742 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 82e8edb8a82c..2eac486cec5b 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1103,17 +1103,16 @@ static void fixup_rootmenu(struct menu *menu) static void replace_button_icon(GladeXML *xml, GdkDrawable *window, GtkStyle *style, gchar *btn_name, gchar **xpm) { - GdkPixmap *pixmap; - GdkBitmap *mask; - GtkToolButton *button; + GdkPixbuf *pixbuf; GtkWidget *image; + GtkToolButton *button; - pixmap = gdk_pixmap_create_from_xpm_d(window, &mask, - &style->bg[GTK_STATE_NORMAL], - xpm); + pixbuf = gdk_pixbuf_new_from_xpm_data((const char **)xpm); + image = gtk_image_new_from_pixbuf(pixbuf); + g_object_unref(pixbuf); button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name)); - image = gtk_image_new_from_pixmap(pixmap, mask); + gtk_widget_show(image); gtk_tool_button_set_icon_widget(button, image); } -- cgit v1.2.3 From b8f660aabcf2dbbd7e8f84c6341e015124bbfc4e Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:27 +0900 Subject: kconfig: gconf: refactor replace_button_icon() The "window" and "style" arguments for replace_button_icon() are now unused. Remove them and refactor the function accordingly. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 2eac486cec5b..045729d76feb 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1100,21 +1100,17 @@ static void fixup_rootmenu(struct menu *menu) } /* Main Window Initialization */ -static void replace_button_icon(GladeXML *xml, GdkDrawable *window, - GtkStyle *style, gchar *btn_name, gchar **xpm) +static void replace_button_icon(GtkWidget *widget, const char * const xpm[]) { GdkPixbuf *pixbuf; GtkWidget *image; - GtkToolButton *button; pixbuf = gdk_pixbuf_new_from_xpm_data((const char **)xpm); image = gtk_image_new_from_pixbuf(pixbuf); g_object_unref(pixbuf); - button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name)); - gtk_widget_show(image); - gtk_tool_button_set_icon_widget(button, image); + gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(widget), image); } static void init_main_window(const gchar *glade_file) @@ -1122,7 +1118,6 @@ static void init_main_window(const gchar *glade_file) GladeXML *xml; GtkWidget *widget; GtkTextBuffer *txtbuf; - GtkStyle *style; xml = glade_xml_new(glade_file, "window1", NULL); if (!xml) @@ -1229,25 +1224,20 @@ static void init_main_window(const gchar *glade_file) g_signal_connect(save_btn, "clicked", G_CALLBACK(on_save_clicked), NULL); - style = gtk_widget_get_style(main_wnd); - single_btn = glade_xml_get_widget(xml, "button4"); g_signal_connect(single_btn, "clicked", G_CALLBACK(on_single_clicked), NULL); - replace_button_icon(xml, main_wnd->window, style, - "button4", (gchar **) xpm_single_view); + replace_button_icon(single_btn, xpm_single_view); split_btn = glade_xml_get_widget(xml, "button5"); g_signal_connect(split_btn, "clicked", G_CALLBACK(on_split_clicked), NULL); - replace_button_icon(xml, main_wnd->window, style, - "button5", (gchar **) xpm_split_view); + replace_button_icon(split_btn, xpm_split_view); full_btn = glade_xml_get_widget(xml, "button6"); g_signal_connect(full_btn, "clicked", G_CALLBACK(on_full_clicked), NULL); - replace_button_icon(xml, main_wnd->window, style, - "button6", (gchar **) xpm_tree_view); + replace_button_icon(full_btn, xpm_tree_view); widget = glade_xml_get_widget(xml, "button7"); g_signal_connect(widget, "clicked", -- cgit v1.2.3 From 20f375cbfe4f7e3870226f68877c9285bd8401fe Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:28 +0900 Subject: kconfig: gconf: make introduction, about, license dialogs modal These are modal dialogs in xconfig. Make them modal in gconfig as well. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/gconf.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 045729d76feb..0e9e078f9c34 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -313,10 +313,8 @@ static void on_introduction1_activate(GtkMenuItem *menuitem, gpointer user_data) GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s", intro_text); - g_signal_connect_swapped(GTK_OBJECT(dialog), "response", - G_CALLBACK(gtk_widget_destroy), - GTK_OBJECT(dialog)); - gtk_widget_show_all(dialog); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); } static void on_about1_activate(GtkMenuItem *menuitem, gpointer user_data) @@ -330,10 +328,8 @@ static void on_about1_activate(GtkMenuItem *menuitem, gpointer user_data) GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s", about_text); - g_signal_connect_swapped(GTK_OBJECT(dialog), "response", - G_CALLBACK(gtk_widget_destroy), - GTK_OBJECT(dialog)); - gtk_widget_show_all(dialog); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); } static void on_license1_activate(GtkMenuItem *menuitem, gpointer user_data) @@ -348,10 +344,8 @@ static void on_license1_activate(GtkMenuItem *menuitem, gpointer user_data) GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, GTK_BUTTONS_CLOSE, "%s", license_text); - g_signal_connect_swapped(GTK_OBJECT(dialog), "response", - G_CALLBACK(gtk_widget_destroy), - GTK_OBJECT(dialog)); - gtk_widget_show_all(dialog); + gtk_dialog_run(GTK_DIALOG(dialog)); + gtk_widget_destroy(dialog); } /* toolbar handlers */ -- cgit v1.2.3 From 1bd81df0b1cc8d17b7818889c4c1cdf53415e606 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:29 +0900 Subject: kconfig: gconf: remove global 'tree' variable Pass the tree store as a function parameter to make it clearer which tree is being updated. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 42 +++++++++++++++++++++--------------------- 1 file changed, 21 insertions(+), 21 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 0e9e078f9c34..b0dffca142a0 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -43,7 +43,7 @@ static GtkWidget *save_menu_item; static GtkTextTag *tag1, *tag2; -static GtkTreeStore *tree1, *tree2, *tree; +static GtkTreeStore *tree1, *tree2; static GtkTreeModel *model1, *model2; static struct menu *current; // current node for SINGLE view @@ -57,7 +57,7 @@ enum { }; static void display_list(void); -static void display_tree(struct menu *menu); +static void display_tree(GtkTreeStore *store, struct menu *menu); static void display_tree_part(void); static void update_tree(struct menu *src, GtkTreeIter * dst); @@ -147,7 +147,7 @@ static void set_view_mode(enum view_mode mode) break; case FULL_VIEW: gtk_tree_store_clear(tree2); - display_tree(&rootmenu); + display_tree(tree2, &rootmenu); gtk_widget_set_sensitive(full_btn, FALSE); break; } @@ -273,7 +273,7 @@ static void on_set_option_mode1_activate(GtkMenuItem *menuitem, { opt_mode = OPT_NORMAL; gtk_tree_store_clear(tree2); - display_tree(&rootmenu); /* instead of update_tree to speed-up */ + display_tree(tree2, &rootmenu); /* instead of update_tree to speed-up */ } static void on_set_option_mode2_activate(GtkMenuItem *menuitem, @@ -281,7 +281,7 @@ static void on_set_option_mode2_activate(GtkMenuItem *menuitem, { opt_mode = OPT_ALL; gtk_tree_store_clear(tree2); - display_tree(&rootmenu); /* instead of update_tree to speed-up */ + display_tree(tree2, &rootmenu); /* instead of update_tree to speed-up */ } static void on_set_option_mode3_activate(GtkMenuItem *menuitem, @@ -289,7 +289,7 @@ static void on_set_option_mode3_activate(GtkMenuItem *menuitem, { opt_mode = OPT_PROMPT; gtk_tree_store_clear(tree2); - display_tree(&rootmenu); /* instead of update_tree to speed-up */ + display_tree(tree2, &rootmenu); /* instead of update_tree to speed-up */ } static void on_introduction1_activate(GtkMenuItem *menuitem, gpointer user_data) @@ -853,7 +853,8 @@ static gchar **fill_row(struct menu *menu) /* Set the node content with a row of strings */ -static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row) +static void set_node(GtkTreeStore *tree, GtkTreeIter *node, + struct menu *menu, gchar **row) { GdkColor color; gboolean success; @@ -977,7 +978,7 @@ static void update_tree(struct menu *src, GtkTreeIter * dst) gtk_tree_store_insert_before(tree2, child2, dst, sibling); - set_node(child2, menu1, fill_row(menu1)); + set_node(tree2, child2, menu1, fill_row(menu1)); if (menu2 == NULL) valid = TRUE; } else { // remove node @@ -991,7 +992,7 @@ static void update_tree(struct menu *src, GtkTreeIter * dst) goto reparse; // next child } } else if (sym && (child1->flags & MENU_CHANGED)) { - set_node(child2, menu1, fill_row(menu1)); + set_node(tree2, child2, menu1, fill_row(menu1)); } update_tree(child1, child2); @@ -1002,7 +1003,8 @@ static void update_tree(struct menu *src, GtkTreeIter * dst) /* Display the whole tree (single/split/full view) */ -static void _display_tree(struct menu *menu, GtkTreeIter *parent) +static void _display_tree(GtkTreeStore *tree, struct menu *menu, + GtkTreeIter *parent) { struct property *prop; struct menu *child; @@ -1030,7 +1032,7 @@ static void _display_tree(struct menu *menu, GtkTreeIter *parent) (opt_mode == OPT_PROMPT && menu_has_prompt(child)) || (opt_mode == OPT_ALL && menu_get_prompt(child))) { gtk_tree_store_append(tree, &iter, parent); - set_node(&iter, child, fill_row(child)); + set_node(tree, &iter, child, fill_row(child)); } if ((view_mode != FULL_VIEW) && (ptype == P_MENU) @@ -1044,13 +1046,13 @@ static void _display_tree(struct menu *menu, GtkTreeIter *parent) if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT)) || (view_mode == FULL_VIEW) || (view_mode == SPLIT_VIEW)) - _display_tree(child, &iter); + _display_tree(tree, child, &iter); } } -static void display_tree(struct menu *menu) +static void display_tree(GtkTreeStore *store, struct menu *menu) { - _display_tree(menu, NULL); + _display_tree(store, menu, NULL); } /* Display a part of the tree starting at current node (single/split view) */ @@ -1058,11 +1060,11 @@ static void display_tree_part(void) { gtk_tree_store_clear(tree2); if (view_mode == SINGLE_VIEW) - display_tree(current); + display_tree(tree2, current); else if (view_mode == SPLIT_VIEW) - display_tree(browsed); + display_tree(tree2, browsed); else if (view_mode == FULL_VIEW) - display_tree(&rootmenu); + display_tree(tree2, &rootmenu); gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); } @@ -1071,10 +1073,8 @@ static void display_list(void) { gtk_tree_store_clear(tree1); - tree = tree1; - display_tree(&rootmenu); + display_tree(tree1, &rootmenu); gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w)); - tree = tree2; } static void fixup_rootmenu(struct menu *menu) @@ -1259,7 +1259,7 @@ static void init_main_window(const gchar *glade_file) static void init_tree_model(void) { - tree = tree2 = gtk_tree_store_new(COL_NUMBER, + tree2 = gtk_tree_store_new(COL_NUMBER, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, -- cgit v1.2.3 From ecaa87d4e9c2820a376270955cd166cd77f82891 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:30 +0900 Subject: kconfig: gconf: merge 'current' and 'browsed' global variables The 'current' (SINGLE view) and 'browsed' (SPLIT_VIEW) variables serve similar purposes and are not needed at the same time. Merge them. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 32 +++++++++++++------------------- 1 file changed, 13 insertions(+), 19 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index b0dffca142a0..bebd18ae07b0 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -46,8 +46,7 @@ static GtkTextTag *tag1, *tag2; static GtkTreeStore *tree1, *tree2; static GtkTreeModel *model1, *model2; -static struct menu *current; // current node for SINGLE view -static struct menu *browsed; // browsed node for SPLIT view +static struct menu *browsed; // browsed menu for SINGLE/SPLIT view enum { COL_OPTION, COL_NAME, COL_NO, COL_MOD, COL_YES, COL_VALUE, @@ -136,7 +135,7 @@ static void set_view_mode(enum view_mode mode) switch (mode) { case SINGLE_VIEW: - current = &rootmenu; + browsed = &rootmenu; display_tree_part(); gtk_widget_set_sensitive(single_btn, FALSE); break; @@ -353,13 +352,13 @@ static void on_back_clicked(GtkButton *button, gpointer user_data) { enum prop_type ptype; - current = current->parent; - ptype = current->prompt ? current->prompt->type : P_UNKNOWN; + browsed = browsed->parent; + ptype = browsed->prompt ? browsed->prompt->type : P_UNKNOWN; if (ptype != P_MENU) - current = current->parent; + browsed = browsed->parent; display_tree_part(); - if (current == &rootmenu) + if (browsed == &rootmenu) gtk_widget_set_sensitive(back_btn, FALSE); } @@ -612,7 +611,7 @@ static gboolean on_treeview2_button_press_event(GtkWidget *widget, if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) { // goes down into menu - current = menu; + browsed = menu; display_tree_part(); gtk_widget_set_sensitive(back_btn, TRUE); } else if (col == COL_OPTION) { @@ -711,14 +710,11 @@ static gboolean on_treeview1_button_press_event(GtkWidget *widget, gtk_tree_model_get_iter(model, &iter, path); gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); - if (event->type == GDK_2BUTTON_PRESS) { + if (event->type == GDK_2BUTTON_PRESS) toggle_sym_value(menu); - current = menu; - display_tree_part(); - } else { - browsed = menu; - display_tree_part(); - } + + browsed = menu; + display_tree_part(); gtk_tree_view_set_cursor(view, path, NULL, FALSE); gtk_widget_grab_focus(tree2_w); @@ -1012,7 +1008,7 @@ static void _display_tree(GtkTreeStore *tree, struct menu *menu, GtkTreeIter iter; if (menu == &rootmenu) - current = &rootmenu; + browsed = &rootmenu; for (child = menu->list; child; child = child->next) { prop = child->prompt; @@ -1059,9 +1055,7 @@ static void display_tree(GtkTreeStore *store, struct menu *menu) static void display_tree_part(void) { gtk_tree_store_clear(tree2); - if (view_mode == SINGLE_VIEW) - display_tree(tree2, current); - else if (view_mode == SPLIT_VIEW) + if (view_mode == SINGLE_VIEW || view_mode == SPLIT_VIEW) display_tree(tree2, browsed); else if (view_mode == FULL_VIEW) display_tree(tree2, &rootmenu); -- cgit v1.2.3 From ab026457d3f8132b62f6855840817467ea92671e Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:31 +0900 Subject: kconfig: gconf: preserve menu selection when switching view mode Preserve the current menu selection when switching to a different view mode, as it improves usability. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 87 ++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 79 insertions(+), 8 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index bebd18ae07b0..eed6a10660eb 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -47,6 +47,7 @@ static GtkTreeStore *tree1, *tree2; static GtkTreeModel *model1, *model2; static struct menu *browsed; // browsed menu for SINGLE/SPLIT view +static struct menu *selected; // selected entry enum { COL_OPTION, COL_NAME, COL_NO, COL_MOD, COL_YES, COL_VALUE, @@ -114,6 +115,49 @@ static void text_insert_msg(const char *title, const char *message) NULL); } +static void _select_menu(GtkTreeView *view, GtkTreeModel *model, + GtkTreeIter *parent, struct menu *match) +{ + GtkTreeIter iter; + gboolean valid; + + valid = gtk_tree_model_iter_children(model, &iter, parent); + while (valid) { + struct menu *menu; + + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); + + if (menu == match) { + GtkTreeSelection *selection; + GtkTreePath *path; + + /* + * Expand parents to reflect the selection, and + * scroll down to it. + */ + path = gtk_tree_model_get_path(model, &iter); + gtk_tree_view_expand_to_path(view, path); + gtk_tree_view_scroll_to_cell(view, path, NULL, TRUE, + 0.5, 0.0); + gtk_tree_path_free(path); + + selection = gtk_tree_view_get_selection(view); + gtk_tree_selection_select_iter(selection, &iter); + + text_insert_help(menu); + } + + _select_menu(view, model, &iter, match); + + valid = gtk_tree_model_iter_next(model, &iter); + } +} + +static void select_menu(GtkTreeView *view, struct menu *match) +{ + _select_menu(view, gtk_tree_view_get_model(view), NULL, match); +} + static void set_view_mode(enum view_mode mode) { view_mode = mode; @@ -135,24 +179,39 @@ static void set_view_mode(enum view_mode mode) switch (mode) { case SINGLE_VIEW: - browsed = &rootmenu; + if (selected) + browsed = menu_get_parent_menu(selected) ?: &rootmenu; + else + browsed = &rootmenu; display_tree_part(); + text_insert_msg("", ""); + select_menu(GTK_TREE_VIEW(tree2_w), selected); gtk_widget_set_sensitive(single_btn, FALSE); break; case SPLIT_VIEW: + browsed = selected; + while (browsed && !(browsed->flags & MENU_ROOT)) + browsed = browsed->parent; gtk_tree_store_clear(tree2); display_list(); + if (browsed) + display_tree(tree2, browsed); + text_insert_msg("", ""); + select_menu(GTK_TREE_VIEW(tree1_w), browsed); + select_menu(GTK_TREE_VIEW(tree2_w), selected); gtk_widget_set_sensitive(split_btn, FALSE); break; case FULL_VIEW: gtk_tree_store_clear(tree2); display_tree(tree2, &rootmenu); + text_insert_msg("", ""); + select_menu(GTK_TREE_VIEW(tree2_w), selected); gtk_widget_set_sensitive(full_btn, FALSE); break; } - if (mode != SINGLE_VIEW) - gtk_widget_set_sensitive(back_btn, FALSE); + gtk_widget_set_sensitive(back_btn, + mode == SINGLE_VIEW && browsed != &rootmenu); } /* Menu & Toolbar Callbacks */ @@ -604,6 +663,8 @@ static gboolean on_treeview2_button_press_event(GtkWidget *widget, return FALSE; gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); + selected = menu; + col = column2index(column); if (event->type == GDK_2BUTTON_PRESS) { enum prop_type ptype; @@ -713,8 +774,12 @@ static gboolean on_treeview1_button_press_event(GtkWidget *widget, if (event->type == GDK_2BUTTON_PRESS) toggle_sym_value(menu); - browsed = menu; - display_tree_part(); + selected = menu; + + if (menu->type == M_MENU) { + browsed = menu; + display_tree_part(); + } gtk_tree_view_set_cursor(view, path, NULL, FALSE); gtk_widget_grab_focus(tree2_w); @@ -1007,10 +1072,16 @@ static void _display_tree(GtkTreeStore *tree, struct menu *menu, enum prop_type ptype; GtkTreeIter iter; - if (menu == &rootmenu) - browsed = &rootmenu; - for (child = menu->list; child; child = child->next) { + /* + * REVISIT: + * menu_finalize() creates empty "if" entries. + * Do not confuse gtk_tree_model_get(), which would otherwise + * return "if" menu entry. + */ + if (child->type == M_IF) + continue; + prop = child->prompt; ptype = prop ? prop->type : P_UNKNOWN; -- cgit v1.2.3 From e3795479366833d576f8e096be8ef9f42f9d124e Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:32 +0900 Subject: kconfig: gconf: use GtkTreeModelFilter to control row visibility Currently, update_tree() adds/removes entries to show/hide rows. This approach is extremely complicated. Use the tree model filter to control row visibility instead. Do not toggle the MENU_CHANGED flag, as it is hard to control this correctly. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 217 ++++++++++++++++++------------------------------ 1 file changed, 80 insertions(+), 137 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index eed6a10660eb..e2b98b220513 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -59,7 +59,9 @@ enum { static void display_list(void); static void display_tree(GtkTreeStore *store, struct menu *menu); static void display_tree_part(void); -static void update_tree(struct menu *src, GtkTreeIter * dst); +static gchar **fill_row(struct menu *menu); +static void set_node(GtkTreeStore *tree, GtkTreeIter *node, + struct menu *menu, gchar **row); static void conf_changed(bool dirty) { @@ -158,6 +160,47 @@ static void select_menu(GtkTreeView *view, struct menu *match) _select_menu(view, gtk_tree_view_get_model(view), NULL, match); } +static void _update_row_visibility(GtkTreeView *view) +{ + GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER(gtk_tree_view_get_model(view)); + + gtk_tree_model_filter_refilter(filter); +} + +static void update_row_visibility(void) +{ + if (view_mode == SPLIT_VIEW) + _update_row_visibility(GTK_TREE_VIEW(tree1_w)); + _update_row_visibility(GTK_TREE_VIEW(tree2_w)); +} + +static void _update_tree(GtkTreeStore *store, GtkTreeIter *parent) +{ + GtkTreeModel *model = GTK_TREE_MODEL(store); + GtkTreeIter iter; + gboolean valid; + + valid = gtk_tree_model_iter_children(model, &iter, parent); + while (valid) { + struct menu *menu; + + gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); + + if (menu) + set_node(store, &iter, menu, fill_row(menu)); + + _update_tree(store, &iter); + + valid = gtk_tree_model_iter_next(model, &iter); + } +} + +static void update_tree(GtkTreeStore *store) +{ + _update_tree(store, NULL); + update_row_visibility(); +} + static void set_view_mode(enum view_mode mode) { view_mode = mode; @@ -330,24 +373,21 @@ static void on_set_option_mode1_activate(GtkMenuItem *menuitem, gpointer user_data) { opt_mode = OPT_NORMAL; - gtk_tree_store_clear(tree2); - display_tree(tree2, &rootmenu); /* instead of update_tree to speed-up */ + update_row_visibility(); } static void on_set_option_mode2_activate(GtkMenuItem *menuitem, gpointer user_data) { opt_mode = OPT_ALL; - gtk_tree_store_clear(tree2); - display_tree(tree2, &rootmenu); /* instead of update_tree to speed-up */ + update_row_visibility(); } static void on_set_option_mode3_activate(GtkMenuItem *menuitem, gpointer user_data) { opt_mode = OPT_PROMPT; - gtk_tree_store_clear(tree2); - display_tree(tree2, &rootmenu); /* instead of update_tree to speed-up */ + update_row_visibility(); } static void on_introduction1_activate(GtkMenuItem *menuitem, gpointer user_data) @@ -561,7 +601,7 @@ static void renderer_edited(GtkCellRendererText * cell, sym_set_string_value(sym, new_def); - update_tree(&rootmenu, NULL); + update_tree(tree2); free: gtk_tree_path_free(path); @@ -592,9 +632,9 @@ static void change_sym_value(struct menu *menu, gint col) newval = yes; sym_set_tristate_value(sym, newval); if (view_mode == FULL_VIEW) - update_tree(&rootmenu, NULL); + update_tree(tree2); else if (view_mode == SPLIT_VIEW) { - update_tree(browsed, NULL); + update_tree(tree2); display_list(); } else if (view_mode == SINGLE_VIEW) @@ -615,9 +655,9 @@ static void toggle_sym_value(struct menu *menu) sym_toggle_tristate_value(menu->sym); if (view_mode == FULL_VIEW) - update_tree(&rootmenu, NULL); + update_tree(tree2); else if (view_mode == SPLIT_VIEW) { - update_tree(browsed, NULL); + update_tree(tree2); display_list(); } else if (view_mode == SINGLE_VIEW) @@ -844,7 +884,6 @@ static gchar **fill_row(struct menu *menu) row[COL_NAME] = g_strdup(sym->name); sym_calc_value(sym); - menu->flags &= ~MENU_CHANGED; if (sym_is_choice(sym)) { // parse childs for getting final value struct menu *child; @@ -949,120 +988,6 @@ static void set_node(GtkTreeStore *tree, GtkTreeIter *node, g_object_unref(pix); } -/* Find a node in the GTK+ tree */ -static GtkTreeIter found; - -/* - * Find a menu in the GtkTree starting at parent. - */ -static GtkTreeIter *gtktree_iter_find_node(GtkTreeIter *parent, - struct menu *tofind) -{ - GtkTreeIter iter; - GtkTreeIter *child = &iter; - gboolean valid; - GtkTreeIter *ret; - - valid = gtk_tree_model_iter_children(model2, child, parent); - while (valid) { - struct menu *menu; - - gtk_tree_model_get(model2, child, 6, &menu, -1); - - if (menu == tofind) { - memcpy(&found, child, sizeof(GtkTreeIter)); - return &found; - } - - ret = gtktree_iter_find_node(child, tofind); - if (ret) - return ret; - - valid = gtk_tree_model_iter_next(model2, child); - } - - return NULL; -} - - -/* - * Update the tree by adding/removing entries - * Does not change other nodes - */ -static void update_tree(struct menu *src, GtkTreeIter * dst) -{ - struct menu *child1; - GtkTreeIter iter, tmp; - GtkTreeIter *child2 = &iter; - gboolean valid; - GtkTreeIter *sibling; - struct symbol *sym; - struct menu *menu1, *menu2; - - valid = gtk_tree_model_iter_children(model2, child2, dst); - for (child1 = src->list; child1; child1 = child1->next) { - - sym = child1->sym; - - reparse: - menu1 = child1; - if (valid) - gtk_tree_model_get(model2, child2, COL_MENU, - &menu2, -1); - else - menu2 = NULL; // force adding of a first child - - if ((opt_mode == OPT_NORMAL && !menu_is_visible(child1)) || - (opt_mode == OPT_PROMPT && !menu_has_prompt(child1)) || - (opt_mode == OPT_ALL && !menu_get_prompt(child1))) { - - /* remove node */ - if (gtktree_iter_find_node(dst, menu1) != NULL) { - memcpy(&tmp, child2, sizeof(GtkTreeIter)); - valid = gtk_tree_model_iter_next(model2, - child2); - gtk_tree_store_remove(tree2, &tmp); - if (!valid) - return; /* next parent */ - else - goto reparse; /* next child */ - } else - continue; - } - - if (menu1 != menu2) { - if (gtktree_iter_find_node(dst, menu1) == NULL) { // add node - if (!valid && !menu2) - sibling = NULL; - else - sibling = child2; - gtk_tree_store_insert_before(tree2, - child2, - dst, sibling); - set_node(tree2, child2, menu1, fill_row(menu1)); - if (menu2 == NULL) - valid = TRUE; - } else { // remove node - memcpy(&tmp, child2, sizeof(GtkTreeIter)); - valid = gtk_tree_model_iter_next(model2, - child2); - gtk_tree_store_remove(tree2, &tmp); - if (!valid) - return; // next parent - else - goto reparse; // next child - } - } else if (sym && (child1->flags & MENU_CHANGED)) { - set_node(tree2, child2, menu1, fill_row(menu1)); - } - - update_tree(child1, child2); - - valid = gtk_tree_model_iter_next(model2, child2); - } -} - - /* Display the whole tree (single/split/full view) */ static void _display_tree(GtkTreeStore *tree, struct menu *menu, GtkTreeIter *parent) @@ -1085,8 +1010,6 @@ static void _display_tree(GtkTreeStore *tree, struct menu *menu, prop = child->prompt; ptype = prop ? prop->type : P_UNKNOWN; - menu->flags &= ~MENU_CHANGED; - if ((view_mode == SPLIT_VIEW) && !(child->flags & MENU_ROOT) && (tree == tree1)) continue; @@ -1095,12 +1018,8 @@ static void _display_tree(GtkTreeStore *tree, struct menu *menu, && (tree == tree2)) continue; - if ((opt_mode == OPT_NORMAL && menu_is_visible(child)) || - (opt_mode == OPT_PROMPT && menu_has_prompt(child)) || - (opt_mode == OPT_ALL && menu_get_prompt(child))) { - gtk_tree_store_append(tree, &iter, parent); - set_node(tree, &iter, child, fill_row(child)); - } + gtk_tree_store_append(tree, &iter, parent); + set_node(tree, &iter, child, fill_row(child)); if ((view_mode != FULL_VIEW) && (ptype == P_MENU) && (tree == tree2)) @@ -1322,6 +1241,20 @@ static void init_main_window(const gchar *glade_file) conf_set_changed_callback(conf_changed); } +static gboolean visible_func(GtkTreeModel *model, GtkTreeIter *iter, + gpointer data) +{ + struct menu *menu; + + gtk_tree_model_get(model, iter, COL_MENU, &menu, -1); + + if (!menu) + return FALSE; + + return menu_is_visible(menu) || opt_mode == OPT_ALL || + (opt_mode == OPT_PROMPT && menu_has_prompt(menu)); +} + static void init_tree_model(void) { tree2 = gtk_tree_store_new(COL_NUMBER, @@ -1353,8 +1286,13 @@ static void init_left_tree(void) GtkCellRenderer *renderer; GtkTreeSelection *sel; GtkTreeViewColumn *column; + GtkTreeModel *filter; - gtk_tree_view_set_model(view, model1); + filter = gtk_tree_model_filter_new(model1, NULL); + + gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter), + visible_func, NULL, NULL); + gtk_tree_view_set_model(view, filter); column = gtk_tree_view_column_new(); gtk_tree_view_append_column(view, column); @@ -1388,9 +1326,14 @@ static void init_right_tree(void) GtkCellRenderer *renderer; GtkTreeSelection *sel; GtkTreeViewColumn *column; + GtkTreeModel *filter; gint i; - gtk_tree_view_set_model(view, model2); + filter = gtk_tree_model_filter_new(model2, NULL); + + gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter), + visible_func, NULL, NULL); + gtk_tree_view_set_model(view, filter); column = gtk_tree_view_column_new(); gtk_tree_view_append_column(view, column); -- cgit v1.2.3 From b674af6ec8541151750b424520fc1acec32deae9 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:33 +0900 Subject: kconfig: gconf: remove global 'model1' and 'model2' variables These variables are unnecessary because the current model can be retrieved using gtk_tree_view_get_model(). Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap --- scripts/kconfig/gconf.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index e2b98b220513..bf3151382e5d 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -44,7 +44,6 @@ static GtkWidget *save_menu_item; static GtkTextTag *tag1, *tag2; static GtkTreeStore *tree1, *tree2; -static GtkTreeModel *model1, *model2; static struct menu *browsed; // browsed menu for SINGLE/SPLIT view static struct menu *selected; // selected entry @@ -1266,7 +1265,6 @@ static void init_tree_model(void) G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN); - model2 = GTK_TREE_MODEL(tree2); tree1 = gtk_tree_store_new(COL_NUMBER, G_TYPE_STRING, G_TYPE_STRING, @@ -1277,7 +1275,6 @@ static void init_tree_model(void) G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN); - model1 = GTK_TREE_MODEL(tree1); } static void init_left_tree(void) @@ -1288,7 +1285,7 @@ static void init_left_tree(void) GtkTreeViewColumn *column; GtkTreeModel *filter; - filter = gtk_tree_model_filter_new(model1, NULL); + filter = gtk_tree_model_filter_new(GTK_TREE_MODEL(tree1), NULL); gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter), visible_func, NULL, NULL); @@ -1329,7 +1326,7 @@ static void init_right_tree(void) GtkTreeModel *filter; gint i; - filter = gtk_tree_model_filter_new(model2, NULL); + filter = gtk_tree_model_filter_new(GTK_TREE_MODEL(tree2), NULL); gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter), visible_func, NULL, NULL); -- cgit v1.2.3 From ed332436f3ca8a130ee9fc49d0882af5fbc344ef Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:34 +0900 Subject: kconfig: gconf: remove init_tree_model() Move the relevant code into init_left_tree() or init_right_tree(). Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index bf3151382e5d..8c024e93c302 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1254,17 +1254,13 @@ static gboolean visible_func(GtkTreeModel *model, GtkTreeIter *iter, (opt_mode == OPT_PROMPT && menu_has_prompt(menu)); } -static void init_tree_model(void) +static void init_left_tree(void) { - tree2 = gtk_tree_store_new(COL_NUMBER, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_POINTER, GDK_TYPE_COLOR, - G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, - G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, - G_TYPE_BOOLEAN); + GtkTreeView *view = GTK_TREE_VIEW(tree1_w); + GtkCellRenderer *renderer; + GtkTreeSelection *sel; + GtkTreeViewColumn *column; + GtkTreeModel *filter; tree1 = gtk_tree_store_new(COL_NUMBER, G_TYPE_STRING, G_TYPE_STRING, @@ -1275,15 +1271,6 @@ static void init_tree_model(void) G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN); -} - -static void init_left_tree(void) -{ - GtkTreeView *view = GTK_TREE_VIEW(tree1_w); - GtkCellRenderer *renderer; - GtkTreeSelection *sel; - GtkTreeViewColumn *column; - GtkTreeModel *filter; filter = gtk_tree_model_filter_new(GTK_TREE_MODEL(tree1), NULL); @@ -1326,6 +1313,16 @@ static void init_right_tree(void) GtkTreeModel *filter; gint i; + tree2 = gtk_tree_store_new(COL_NUMBER, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_STRING, G_TYPE_STRING, + G_TYPE_POINTER, GDK_TYPE_COLOR, + G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, + G_TYPE_BOOLEAN); + filter = gtk_tree_model_filter_new(GTK_TREE_MODEL(tree2), NULL); gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter), @@ -1448,7 +1445,6 @@ int main(int ac, char *av[]) /* Load the interface and connect signals */ init_main_window(glade_file); - init_tree_model(); init_left_tree(); init_right_tree(); -- cgit v1.2.3 From 64285dc5c41fc7a031695c2c286a2bfef9eaf2c6 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:35 +0900 Subject: kconfig: gconf: inline fill_row() into set_node() The row[] array is used to prepare data passed to set_node(), but this indirection is unnecessary. Squash fill_row() into set_node() and call gtk_tree_store_set() directly. Also, calling gdk_pixbuf_new_from_xpm_data() for every row is inefficient. Call it once and store the resulting pixbuf in a global variable. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 272 +++++++++++++++++++----------------------------- 1 file changed, 106 insertions(+), 166 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 8c024e93c302..9df32f47bf6b 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -44,6 +44,7 @@ static GtkWidget *save_menu_item; static GtkTextTag *tag1, *tag2; static GtkTreeStore *tree1, *tree2; +static GdkPixbuf *pix_menu; static struct menu *browsed; // browsed menu for SINGLE/SPLIT view static struct menu *selected; // selected entry @@ -58,9 +59,6 @@ enum { static void display_list(void); static void display_tree(GtkTreeStore *store, struct menu *menu); static void display_tree_part(void); -static gchar **fill_row(struct menu *menu); -static void set_node(GtkTreeStore *tree, GtkTreeIter *node, - struct menu *menu, gchar **row); static void conf_changed(bool dirty) { @@ -173,6 +171,104 @@ static void update_row_visibility(void) _update_row_visibility(GTK_TREE_VIEW(tree2_w)); } +static void set_node(GtkTreeStore *tree, GtkTreeIter *node, struct menu *menu) +{ + struct symbol *sym = menu->sym; + tristate val; + gchar *option; + const gchar *_no = ""; + const gchar *_mod = ""; + const gchar *_yes = ""; + const gchar *value = ""; + GdkColor color; + gboolean editable = FALSE; + gboolean btnvis = FALSE; + + option = g_strdup_printf("%s %s %s %s", + menu->type == M_COMMENT ? "***" : "", + menu_get_prompt(menu), + menu->type == M_COMMENT ? "***" : "", + sym && !sym_has_value(sym) ? "(NEW)" : ""); + + gdk_color_parse(menu_is_visible(menu) ? "Black" : "DarkGray", &color); + + if (!sym) + goto set; + + sym_calc_value(sym); + + if (menu->type == M_CHOICE) { // parse children to get a final value + struct symbol *def_sym = sym_calc_choice(menu); + struct menu *def_menu = NULL; + + for (struct menu *child = menu->list; child; child = child->next) { + if (menu_is_visible(child) && child->sym == def_sym) + def_menu = child; + } + + if (def_menu) + value = menu_get_prompt(def_menu); + + goto set; + } + + switch (sym_get_type(sym)) { + case S_BOOLEAN: + case S_TRISTATE: + + btnvis = TRUE; + + val = sym_get_tristate_value(sym); + switch (val) { + case no: + _no = "N"; + value = "N"; + break; + case mod: + _mod = "M"; + value = "M"; + break; + case yes: + _yes = "Y"; + value = "Y"; + break; + } + + if (val != no && sym_tristate_within_range(sym, no)) + _no = "_"; + if (val != mod && sym_tristate_within_range(sym, mod)) + _mod = "_"; + if (val != yes && sym_tristate_within_range(sym, yes)) + _yes = "_"; + break; + default: + value = sym_get_string_value(sym); + editable = TRUE; + break; + } + +set: + gtk_tree_store_set(tree, node, + COL_OPTION, option, + COL_NAME, sym ? sym->name : "", + COL_NO, _no, + COL_MOD, _mod, + COL_YES, _yes, + COL_VALUE, value, + COL_MENU, (gpointer) menu, + COL_COLOR, &color, + COL_EDIT, editable, + COL_PIXBUF, pix_menu, + COL_PIXVIS, view_mode == SINGLE_VIEW && menu->type == M_MENU, + COL_BTNVIS, btnvis, + COL_BTNACT, _yes[0] == 'Y', + COL_BTNINC, _mod[0] == 'M', + COL_BTNRAD, sym && sym_is_choice_value(sym), + -1); + + g_free(option); +} + static void _update_tree(GtkTreeStore *store, GtkTreeIter *parent) { GtkTreeModel *model = GTK_TREE_MODEL(store); @@ -186,7 +282,7 @@ static void _update_tree(GtkTreeStore *store, GtkTreeIter *parent) gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1); if (menu) - set_node(store, &iter, menu, fill_row(menu)); + set_node(store, &iter, menu); _update_tree(store, &iter); @@ -565,6 +661,9 @@ static gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, gtk_widget_destroy(dialog); + if (!ret) + g_object_unref(pix_menu); + return ret; } @@ -826,167 +925,6 @@ static gboolean on_treeview1_button_press_event(GtkWidget *widget, return FALSE; } - -/* Fill a row of strings */ -static gchar **fill_row(struct menu *menu) -{ - static gchar *row[COL_NUMBER]; - struct symbol *sym = menu->sym; - const char *def; - int stype; - tristate val; - enum prop_type ptype; - int i; - - for (i = COL_OPTION; i <= COL_COLOR; i++) - g_free(row[i]); - bzero(row, sizeof(row)); - - ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; - - row[COL_OPTION] = - g_strdup_printf("%s %s %s %s", - ptype == P_COMMENT ? "***" : "", - menu_get_prompt(menu), - ptype == P_COMMENT ? "***" : "", - sym && !sym_has_value(sym) ? "(NEW)" : ""); - - if (opt_mode == OPT_ALL && !menu_is_visible(menu)) - row[COL_COLOR] = g_strdup("DarkGray"); - else if (opt_mode == OPT_PROMPT && - menu_has_prompt(menu) && !menu_is_visible(menu)) - row[COL_COLOR] = g_strdup("DarkGray"); - else - row[COL_COLOR] = g_strdup("Black"); - - switch (ptype) { - case P_MENU: - row[COL_PIXBUF] = (gchar *) xpm_menu; - if (view_mode == SINGLE_VIEW) - row[COL_PIXVIS] = GINT_TO_POINTER(TRUE); - row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); - break; - case P_COMMENT: - row[COL_PIXBUF] = (gchar *) xpm_void; - row[COL_PIXVIS] = GINT_TO_POINTER(FALSE); - row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); - break; - default: - row[COL_PIXBUF] = (gchar *) xpm_void; - row[COL_PIXVIS] = GINT_TO_POINTER(FALSE); - row[COL_BTNVIS] = GINT_TO_POINTER(TRUE); - break; - } - - if (!sym) - return row; - row[COL_NAME] = g_strdup(sym->name); - - sym_calc_value(sym); - - if (sym_is_choice(sym)) { // parse childs for getting final value - struct menu *child; - struct symbol *def_sym = sym_calc_choice(menu); - struct menu *def_menu = NULL; - - for (child = menu->list; child; child = child->next) { - if (menu_is_visible(child) - && child->sym == def_sym) - def_menu = child; - } - - if (def_menu) - row[COL_VALUE] = - g_strdup(menu_get_prompt(def_menu)); - - row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); - return row; - } - if (sym_is_choice_value(sym)) - row[COL_BTNRAD] = GINT_TO_POINTER(TRUE); - - stype = sym_get_type(sym); - switch (stype) { - case S_BOOLEAN: - case S_TRISTATE: - val = sym_get_tristate_value(sym); - switch (val) { - case no: - row[COL_NO] = g_strdup("N"); - row[COL_VALUE] = g_strdup("N"); - row[COL_BTNACT] = GINT_TO_POINTER(FALSE); - row[COL_BTNINC] = GINT_TO_POINTER(FALSE); - break; - case mod: - row[COL_MOD] = g_strdup("M"); - row[COL_VALUE] = g_strdup("M"); - row[COL_BTNINC] = GINT_TO_POINTER(TRUE); - break; - case yes: - row[COL_YES] = g_strdup("Y"); - row[COL_VALUE] = g_strdup("Y"); - row[COL_BTNACT] = GINT_TO_POINTER(TRUE); - row[COL_BTNINC] = GINT_TO_POINTER(FALSE); - break; - } - - if (val != no && sym_tristate_within_range(sym, no)) - row[COL_NO] = g_strdup("_"); - if (val != mod && sym_tristate_within_range(sym, mod)) - row[COL_MOD] = g_strdup("_"); - if (val != yes && sym_tristate_within_range(sym, yes)) - row[COL_YES] = g_strdup("_"); - break; - case S_INT: - case S_HEX: - case S_STRING: - def = sym_get_string_value(sym); - row[COL_VALUE] = g_strdup(def); - row[COL_EDIT] = GINT_TO_POINTER(TRUE); - row[COL_BTNVIS] = GINT_TO_POINTER(FALSE); - break; - } - - return row; -} - - -/* Set the node content with a row of strings */ -static void set_node(GtkTreeStore *tree, GtkTreeIter *node, - struct menu *menu, gchar **row) -{ - GdkColor color; - gboolean success; - GdkPixbuf *pix; - - pix = gdk_pixbuf_new_from_xpm_data((const char **) - row[COL_PIXBUF]); - - gdk_color_parse(row[COL_COLOR], &color); - gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color, 1, - FALSE, FALSE, &success); - - gtk_tree_store_set(tree, node, - COL_OPTION, row[COL_OPTION], - COL_NAME, row[COL_NAME], - COL_NO, row[COL_NO], - COL_MOD, row[COL_MOD], - COL_YES, row[COL_YES], - COL_VALUE, row[COL_VALUE], - COL_MENU, (gpointer) menu, - COL_COLOR, &color, - COL_EDIT, GPOINTER_TO_INT(row[COL_EDIT]), - COL_PIXBUF, pix, - COL_PIXVIS, GPOINTER_TO_INT(row[COL_PIXVIS]), - COL_BTNVIS, GPOINTER_TO_INT(row[COL_BTNVIS]), - COL_BTNACT, GPOINTER_TO_INT(row[COL_BTNACT]), - COL_BTNINC, GPOINTER_TO_INT(row[COL_BTNINC]), - COL_BTNRAD, GPOINTER_TO_INT(row[COL_BTNRAD]), - -1); - - g_object_unref(pix); -} - /* Display the whole tree (single/split/full view) */ static void _display_tree(GtkTreeStore *tree, struct menu *menu, GtkTreeIter *parent) @@ -1018,7 +956,7 @@ static void _display_tree(GtkTreeStore *tree, struct menu *menu, continue; gtk_tree_store_append(tree, &iter, parent); - set_node(tree, &iter, child, fill_row(child)); + set_node(tree, &iter, child); if ((view_mode != FULL_VIEW) && (ptype == P_MENU) && (tree == tree2)) @@ -1393,6 +1331,8 @@ static void init_right_tree(void) g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(renderer_edited), tree2_w); + pix_menu = gdk_pixbuf_new_from_xpm_data((const char **)xpm_menu); + for (i = 0; i < COL_VALUE; i++) { column = gtk_tree_view_get_column(view, i); gtk_tree_view_column_set_resizable(column, TRUE); -- cgit v1.2.3 From 035c2f56f57432caa78378e3ab498a5fb9bd276b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:36 +0900 Subject: kconfig: gconf: do not reconstruct tree store when a symbol is changed There is no need to reconstruct the entire tree store when a symbol's value changes. Simply call gtk_tree_store_set() to update the row data. Introduce update_trees() to factor out the common update logic. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 9df32f47bf6b..73736f79ddcb 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -296,6 +296,13 @@ static void update_tree(GtkTreeStore *store) update_row_visibility(); } +static void update_trees(void) +{ + if (view_mode == SPLIT_VIEW) + update_tree(tree1); + update_tree(tree2); +} + static void set_view_mode(enum view_mode mode) { view_mode = mode; @@ -380,7 +387,7 @@ static void on_load1_activate(GtkMenuItem *menuitem, gpointer user_data) text_insert_msg("Error", "Unable to load configuration!"); else - display_tree_part(); + update_trees(); g_free(filename); } @@ -699,7 +706,7 @@ static void renderer_edited(GtkCellRendererText * cell, sym_set_string_value(sym, new_def); - update_tree(tree2); + update_trees(); free: gtk_tree_path_free(path); @@ -729,14 +736,7 @@ static void change_sym_value(struct menu *menu, gint col) if (!sym_tristate_within_range(sym, newval)) newval = yes; sym_set_tristate_value(sym, newval); - if (view_mode == FULL_VIEW) - update_tree(tree2); - else if (view_mode == SPLIT_VIEW) { - update_tree(tree2); - display_list(); - } - else if (view_mode == SINGLE_VIEW) - display_tree_part(); //fixme: keep exp/coll + update_trees(); break; case S_INT: case S_HEX: @@ -752,14 +752,7 @@ static void toggle_sym_value(struct menu *menu) return; sym_toggle_tristate_value(menu->sym); - if (view_mode == FULL_VIEW) - update_tree(tree2); - else if (view_mode == SPLIT_VIEW) { - update_tree(tree2); - display_list(); - } - else if (view_mode == SINGLE_VIEW) - display_tree_part(); //fixme: keep exp/coll + update_trees(); } static gint column2index(GtkTreeViewColumn * column) -- cgit v1.2.3 From 063a274a5e297720e18b3a1d7bbfe2d039e12192 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:37 +0900 Subject: kconfig: gconf: inline display_list() into set_view_mode() This function is now only called by set_view_mode(), so inline it for simplicity. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 73736f79ddcb..3c2e6be30c00 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -56,7 +56,6 @@ enum { COL_NUMBER }; -static void display_list(void); static void display_tree(GtkTreeStore *store, struct menu *menu); static void display_tree_part(void); @@ -337,8 +336,10 @@ static void set_view_mode(enum view_mode mode) browsed = selected; while (browsed && !(browsed->flags & MENU_ROOT)) browsed = browsed->parent; + gtk_tree_store_clear(tree1); + display_tree(tree1, &rootmenu); + gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w)); gtk_tree_store_clear(tree2); - display_list(); if (browsed) display_tree(tree2, browsed); text_insert_msg("", ""); @@ -982,15 +983,6 @@ static void display_tree_part(void) gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); } -/* Display the list in the left frame (split view) */ -static void display_list(void) -{ - gtk_tree_store_clear(tree1); - - display_tree(tree1, &rootmenu); - gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w)); -} - static void fixup_rootmenu(struct menu *menu) { struct menu *child; -- cgit v1.2.3 From bf5792da5ac14c5e95f1e8612df70096ee5a44d1 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:38 +0900 Subject: kconfig: gconf: remove dead code in display_tree_part() This function is no longer called in the FULL_VIEW mode, so remove the dead code. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 3c2e6be30c00..6afdba85158a 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -976,10 +976,7 @@ static void display_tree(GtkTreeStore *store, struct menu *menu) static void display_tree_part(void) { gtk_tree_store_clear(tree2); - if (view_mode == SINGLE_VIEW || view_mode == SPLIT_VIEW) - display_tree(tree2, browsed); - else if (view_mode == FULL_VIEW) - display_tree(tree2, &rootmenu); + display_tree(tree2, browsed); gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w)); } -- cgit v1.2.3 From 475c878f971661511fb3911af96c0ee0cb533527 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:42 +0900 Subject: kconfig: gconf: replace GDK_space with GDK_KEY_space In GTK3, keysyms changed to have a KEY_ prefix. [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/gdk/gdkkeysyms-compat.h#L24 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 6afdba85158a..b24d02972090 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -841,7 +841,7 @@ static gboolean on_treeview2_key_press_event(GtkWidget *widget, if (path == NULL) return FALSE; - if (event->keyval == GDK_space) { + if (event->keyval == GDK_KEY_space) { if (gtk_tree_view_row_expanded(view, path)) gtk_tree_view_collapse_row(view, path); else -- cgit v1.2.3 From 7783290143b37c568538d8e699d81d1e6c8af72b Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:43 +0900 Subject: kconfig: gconf: replace GTK_STOCK_{OK,NO,CANCEL} These are deprecated with GTK 3.10. [1] Use "_OK", "_no", "_Cancel". [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/3.10.0/gtk/deprecated/gtkstock.h#L827 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index b24d02972090..085a06667a21 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -640,11 +640,11 @@ static gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, (GtkDialogFlags) (GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT), - GTK_STOCK_OK, + "_OK", GTK_RESPONSE_YES, - GTK_STOCK_NO, + "_No", GTK_RESPONSE_NO, - GTK_STOCK_CANCEL, + "_Cancel", GTK_RESPONSE_CANCEL, NULL); gtk_dialog_set_default_response(GTK_DIALOG(dialog), GTK_RESPONSE_CANCEL); -- cgit v1.2.3 From ad452c27aeb80d1c3ee449250c3f790e7bd8ffaa Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:44 +0900 Subject: kconfig: gconf: remove "tooltips" property from glade The tips are still displayed without this. This property does not exist in GtkBuilder with GTK 3. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.glade | 1 - 1 file changed, 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.glade b/scripts/kconfig/gconf.glade index 8519104a3c2b..c0ada331a5bf 100644 --- a/scripts/kconfig/gconf.glade +++ b/scripts/kconfig/gconf.glade @@ -225,7 +225,6 @@ True GTK_ORIENTATION_HORIZONTAL GTK_TOOLBAR_BOTH - True True -- cgit v1.2.3 From 9d0e47c4c879dfbaa1f407ecbd4176682f711871 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:45 +0900 Subject: kconfig: gconf: replace "tooltip" property with "tooltip-text" This is no longer available in GTK 3. Use "tooltip-text" instead. Also reword "Goes up of one level" to "Goes up one level" while I am here. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.glade | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.glade b/scripts/kconfig/gconf.glade index c0ada331a5bf..35d5257f33c9 100644 --- a/scripts/kconfig/gconf.glade +++ b/scripts/kconfig/gconf.glade @@ -40,7 +40,7 @@ True - Load a config file + Load a config file _Load True @@ -50,7 +50,7 @@ True - Save the config in .config + Save the config in .config _Save True @@ -60,7 +60,7 @@ True - Save the config in a file + Save the config in a file Save _as True @@ -97,7 +97,7 @@ True - Show name + Show name Show _name True False @@ -107,7 +107,7 @@ True - Show range (Y/M/N) + Show range (Y/M/N) Show _range True False @@ -117,7 +117,7 @@ True - Show value of the option + Show value of the option Show _data True False @@ -133,7 +133,7 @@ True - Show normal options + Show normal options Show normal options True True @@ -143,7 +143,7 @@ True - Show all options + Show all options Show all _options True False @@ -154,7 +154,7 @@ True - Show all options with prompts + Show all options with prompts Show all prompt options True False @@ -230,7 +230,7 @@ True - Goes up of one level (single view) + Goes up one level (single view) Back True gtk-undo @@ -266,7 +266,7 @@ True - Load a config file + Load a config file Load True gtk-open @@ -283,7 +283,7 @@ True - Save a config file + Save a config file Save True gtk-save @@ -319,7 +319,7 @@ True - Single view + Single view Single True gtk-missing-image @@ -336,7 +336,7 @@ True - Split view + Split view Split True gtk-missing-image @@ -353,7 +353,7 @@ True - Full view + Full view Full True gtk-missing-image @@ -389,7 +389,7 @@ True - Collapse the whole tree in the right frame + Collapse the whole tree in the right frame Collapse True gtk-remove @@ -406,7 +406,7 @@ True - Expand the whole tree in the right frame + Expand the whole tree in the right frame Expand True gtk-add -- cgit v1.2.3 From ea1438f720aa2fa287237fbeb7e76a1e83a10af8 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:46 +0900 Subject: kconfig: gconf: remove unnecessary default message in text view This message looks odd because it is displayed when nothing is selected. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.glade | 1 - 1 file changed, 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.glade b/scripts/kconfig/gconf.glade index 35d5257f33c9..488342e6fce1 100644 --- a/scripts/kconfig/gconf.glade +++ b/scripts/kconfig/gconf.glade @@ -517,7 +517,6 @@ 0 0 0 - Sorry, no help available for this option yet. -- cgit v1.2.3 From 07944f94fc8c02344f283e461b1ea817a9108e17 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:47 +0900 Subject: kconfig: gconf: use gtk_check_menu_item_get_active() accessor GTK 3 removes many implementation details and struct members from its public headers. Use the gtk_check_menu_item_get_active() accessor. [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/docs/reference/gtk/compiling.sgml#L85 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 085a06667a21..a02078926274 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -439,7 +439,7 @@ static void on_show_name1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkTreeViewColumn *col; - show_name = GTK_CHECK_MENU_ITEM(menuitem)->active; + show_name = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)); col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NAME); if (col) gtk_tree_view_column_set_visible(col, show_name); @@ -449,7 +449,7 @@ static void on_show_range1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkTreeViewColumn *col; - show_range = GTK_CHECK_MENU_ITEM(menuitem)->active; + show_range = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)); col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NO); if (col) gtk_tree_view_column_set_visible(col, show_range); @@ -466,7 +466,7 @@ static void on_show_data1_activate(GtkMenuItem *menuitem, gpointer user_data) { GtkTreeViewColumn *col; - show_value = GTK_CHECK_MENU_ITEM(menuitem)->active; + show_value = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem)); col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_VALUE); if (col) gtk_tree_view_column_set_visible(col, show_value); -- cgit v1.2.3 From c34d632370592cb503991728afdf5287b2d1f7ed Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:48 +0900 Subject: kconfig: gconf: use gtk_dialog_get_content_area() accessor GTK 3 removes many implementation details and struct members from its public headers. Use the gtk_check_menu_item_get_active() accessor. [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/2.24.33/docs/reference/gtk/compiling.sgml#L85 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index a02078926274..28c4b5b37448 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -628,7 +628,7 @@ static void on_window1_size_request(GtkWidget *widget, static gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, gpointer user_data) { - GtkWidget *dialog, *label; + GtkWidget *dialog, *label, *content_area; gint result; gint ret = FALSE; @@ -650,7 +650,8 @@ static gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, GTK_RESPONSE_CANCEL); label = gtk_label_new("\nSave configuration ?\n"); - gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label); + content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_container_add(GTK_CONTAINER(content_area), label); gtk_widget_show(label); result = gtk_dialog_run(GTK_DIALOG(dialog)); -- cgit v1.2.3 From 9e0bd6db622c7c19aec5a8b248bb34493c9998e6 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Wed, 25 Jun 2025 00:05:49 +0900 Subject: kconfig: gconf: remove GtkHandleBox from glade GtkHandleBox is deprecated with GTK 3.4. [1] [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/3.4.0/gtk/deprecated/gtkhandlebox.c#L426 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.glade | 9 --------- 1 file changed, 9 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.glade b/scripts/kconfig/gconf.glade index 488342e6fce1..cd714e64cff1 100644 --- a/scripts/kconfig/gconf.glade +++ b/scripts/kconfig/gconf.glade @@ -214,13 +214,6 @@ - - True - GTK_SHADOW_OUT - GTK_POS_LEFT - GTK_POS_TOP - - True GTK_ORIENTATION_HORIZONTAL @@ -420,8 +413,6 @@ - - 0 False -- cgit v1.2.3 From be7efb2d20d67f334a7de2aef77ae6c69367e646 Mon Sep 17 00:00:00 2001 From: Andrey Albershteyn Date: Mon, 30 Jun 2025 18:20:16 +0200 Subject: fs: introduce file_getattr and file_setattr syscalls Introduce file_getattr() and file_setattr() syscalls to manipulate inode extended attributes. The syscalls takes pair of file descriptor and pathname. Then it operates on inode opened accroding to openat() semantics. The struct file_attr is passed to obtain/change extended attributes. This is an alternative to FS_IOC_FSSETXATTR ioctl with a difference that file don't need to be open as we can reference it with a path instead of fd. By having this we can manipulated inode extended attributes not only on regular files but also on special ones. This is not possible with FS_IOC_FSSETXATTR ioctl as with special files we can not call ioctl() directly on the filesystem inode using fd. This patch adds two new syscalls which allows userspace to get/set extended inode attributes on special files by using parent directory and a path - *at() like syscall. CC: linux-api@vger.kernel.org CC: linux-fsdevel@vger.kernel.org CC: linux-xfs@vger.kernel.org Signed-off-by: Andrey Albershteyn Link: https://lore.kernel.org/20250630-xattrat-syscall-v6-6-c4e3bc35227b@kernel.org Acked-by: Arnd Bergmann Signed-off-by: Christian Brauner --- arch/alpha/kernel/syscalls/syscall.tbl | 2 + arch/arm/tools/syscall.tbl | 2 + arch/arm64/tools/syscall_32.tbl | 2 + arch/m68k/kernel/syscalls/syscall.tbl | 2 + arch/microblaze/kernel/syscalls/syscall.tbl | 2 + arch/mips/kernel/syscalls/syscall_n32.tbl | 2 + arch/mips/kernel/syscalls/syscall_n64.tbl | 2 + arch/mips/kernel/syscalls/syscall_o32.tbl | 2 + arch/parisc/kernel/syscalls/syscall.tbl | 2 + arch/powerpc/kernel/syscalls/syscall.tbl | 2 + arch/s390/kernel/syscalls/syscall.tbl | 2 + arch/sh/kernel/syscalls/syscall.tbl | 2 + arch/sparc/kernel/syscalls/syscall.tbl | 2 + arch/x86/entry/syscalls/syscall_32.tbl | 2 + arch/x86/entry/syscalls/syscall_64.tbl | 2 + arch/xtensa/kernel/syscalls/syscall.tbl | 2 + fs/file_attr.c | 152 ++++++++++++++++++++++++++++ include/linux/syscalls.h | 7 ++ include/uapi/asm-generic/unistd.h | 8 +- include/uapi/linux/fs.h | 18 ++++ scripts/syscall.tbl | 2 + 21 files changed, 218 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/arch/alpha/kernel/syscalls/syscall.tbl b/arch/alpha/kernel/syscalls/syscall.tbl index 2dd6340de6b4..16dca28ebf17 100644 --- a/arch/alpha/kernel/syscalls/syscall.tbl +++ b/arch/alpha/kernel/syscalls/syscall.tbl @@ -507,3 +507,5 @@ 575 common listxattrat sys_listxattrat 576 common removexattrat sys_removexattrat 577 common open_tree_attr sys_open_tree_attr +578 common file_getattr sys_file_getattr +579 common file_setattr sys_file_setattr diff --git a/arch/arm/tools/syscall.tbl b/arch/arm/tools/syscall.tbl index 27c1d5ebcd91..b07e699aaa3c 100644 --- a/arch/arm/tools/syscall.tbl +++ b/arch/arm/tools/syscall.tbl @@ -482,3 +482,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/arch/arm64/tools/syscall_32.tbl b/arch/arm64/tools/syscall_32.tbl index 0765b3a8d6d6..8d9088bc577d 100644 --- a/arch/arm64/tools/syscall_32.tbl +++ b/arch/arm64/tools/syscall_32.tbl @@ -479,3 +479,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/arch/m68k/kernel/syscalls/syscall.tbl b/arch/m68k/kernel/syscalls/syscall.tbl index 9fe47112c586..f41d38dfbf13 100644 --- a/arch/m68k/kernel/syscalls/syscall.tbl +++ b/arch/m68k/kernel/syscalls/syscall.tbl @@ -467,3 +467,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/arch/microblaze/kernel/syscalls/syscall.tbl b/arch/microblaze/kernel/syscalls/syscall.tbl index 7b6e97828e55..580af574fe73 100644 --- a/arch/microblaze/kernel/syscalls/syscall.tbl +++ b/arch/microblaze/kernel/syscalls/syscall.tbl @@ -473,3 +473,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/arch/mips/kernel/syscalls/syscall_n32.tbl b/arch/mips/kernel/syscalls/syscall_n32.tbl index aa70e371bb54..d824ffe9a014 100644 --- a/arch/mips/kernel/syscalls/syscall_n32.tbl +++ b/arch/mips/kernel/syscalls/syscall_n32.tbl @@ -406,3 +406,5 @@ 465 n32 listxattrat sys_listxattrat 466 n32 removexattrat sys_removexattrat 467 n32 open_tree_attr sys_open_tree_attr +468 n32 file_getattr sys_file_getattr +469 n32 file_setattr sys_file_setattr diff --git a/arch/mips/kernel/syscalls/syscall_n64.tbl b/arch/mips/kernel/syscalls/syscall_n64.tbl index 1e8c44c7b614..7a7049c2c307 100644 --- a/arch/mips/kernel/syscalls/syscall_n64.tbl +++ b/arch/mips/kernel/syscalls/syscall_n64.tbl @@ -382,3 +382,5 @@ 465 n64 listxattrat sys_listxattrat 466 n64 removexattrat sys_removexattrat 467 n64 open_tree_attr sys_open_tree_attr +468 n64 file_getattr sys_file_getattr +469 n64 file_setattr sys_file_setattr diff --git a/arch/mips/kernel/syscalls/syscall_o32.tbl b/arch/mips/kernel/syscalls/syscall_o32.tbl index 114a5a1a6230..d330274f0601 100644 --- a/arch/mips/kernel/syscalls/syscall_o32.tbl +++ b/arch/mips/kernel/syscalls/syscall_o32.tbl @@ -455,3 +455,5 @@ 465 o32 listxattrat sys_listxattrat 466 o32 removexattrat sys_removexattrat 467 o32 open_tree_attr sys_open_tree_attr +468 o32 file_getattr sys_file_getattr +469 o32 file_setattr sys_file_setattr diff --git a/arch/parisc/kernel/syscalls/syscall.tbl b/arch/parisc/kernel/syscalls/syscall.tbl index 94df3cb957e9..88a788a7b18d 100644 --- a/arch/parisc/kernel/syscalls/syscall.tbl +++ b/arch/parisc/kernel/syscalls/syscall.tbl @@ -466,3 +466,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/arch/powerpc/kernel/syscalls/syscall.tbl b/arch/powerpc/kernel/syscalls/syscall.tbl index 9a084bdb8926..b453e80dfc00 100644 --- a/arch/powerpc/kernel/syscalls/syscall.tbl +++ b/arch/powerpc/kernel/syscalls/syscall.tbl @@ -558,3 +558,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/arch/s390/kernel/syscalls/syscall.tbl b/arch/s390/kernel/syscalls/syscall.tbl index a4569b96ef06..8a6744d658db 100644 --- a/arch/s390/kernel/syscalls/syscall.tbl +++ b/arch/s390/kernel/syscalls/syscall.tbl @@ -470,3 +470,5 @@ 465 common listxattrat sys_listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr sys_file_setattr diff --git a/arch/sh/kernel/syscalls/syscall.tbl b/arch/sh/kernel/syscalls/syscall.tbl index 52a7652fcff6..5e9c9eff5539 100644 --- a/arch/sh/kernel/syscalls/syscall.tbl +++ b/arch/sh/kernel/syscalls/syscall.tbl @@ -471,3 +471,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/arch/sparc/kernel/syscalls/syscall.tbl b/arch/sparc/kernel/syscalls/syscall.tbl index 83e45eb6c095..ebb7d06d1044 100644 --- a/arch/sparc/kernel/syscalls/syscall.tbl +++ b/arch/sparc/kernel/syscalls/syscall.tbl @@ -513,3 +513,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/arch/x86/entry/syscalls/syscall_32.tbl b/arch/x86/entry/syscalls/syscall_32.tbl index ac007ea00979..4877e16da69a 100644 --- a/arch/x86/entry/syscalls/syscall_32.tbl +++ b/arch/x86/entry/syscalls/syscall_32.tbl @@ -473,3 +473,5 @@ 465 i386 listxattrat sys_listxattrat 466 i386 removexattrat sys_removexattrat 467 i386 open_tree_attr sys_open_tree_attr +468 i386 file_getattr sys_file_getattr +469 i386 file_setattr sys_file_setattr diff --git a/arch/x86/entry/syscalls/syscall_64.tbl b/arch/x86/entry/syscalls/syscall_64.tbl index cfb5ca41e30d..92cf0fe2291e 100644 --- a/arch/x86/entry/syscalls/syscall_64.tbl +++ b/arch/x86/entry/syscalls/syscall_64.tbl @@ -391,6 +391,8 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr # # Due to a historical design error, certain syscalls are numbered differently diff --git a/arch/xtensa/kernel/syscalls/syscall.tbl b/arch/xtensa/kernel/syscalls/syscall.tbl index f657a77314f8..374e4cb788d8 100644 --- a/arch/xtensa/kernel/syscalls/syscall.tbl +++ b/arch/xtensa/kernel/syscalls/syscall.tbl @@ -438,3 +438,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr diff --git a/fs/file_attr.c b/fs/file_attr.c index 775f43fc9687..21d6a0607345 100644 --- a/fs/file_attr.c +++ b/fs/file_attr.c @@ -4,6 +4,10 @@ #include #include #include +#include +#include + +#include "internal.h" /** * fileattr_fill_xflags - initialize fileattr with xflags @@ -90,6 +94,19 @@ int vfs_fileattr_get(struct dentry *dentry, struct fileattr *fa) } EXPORT_SYMBOL(vfs_fileattr_get); +static void fileattr_to_file_attr(const struct fileattr *fa, + struct file_attr *fattr) +{ + __u32 mask = FS_XFLAGS_MASK; + + memset(fattr, 0, sizeof(struct file_attr)); + fattr->fa_xflags = fa->fsx_xflags & mask; + fattr->fa_extsize = fa->fsx_extsize; + fattr->fa_nextents = fa->fsx_nextents; + fattr->fa_projid = fa->fsx_projid; + fattr->fa_cowextsize = fa->fsx_cowextsize; +} + /** * copy_fsxattr_to_user - copy fsxattr to userspace. * @fa: fileattr pointer @@ -116,6 +133,23 @@ int copy_fsxattr_to_user(const struct fileattr *fa, struct fsxattr __user *ufa) } EXPORT_SYMBOL(copy_fsxattr_to_user); +static int file_attr_to_fileattr(const struct file_attr *fattr, + struct fileattr *fa) +{ + __u32 mask = FS_XFLAGS_MASK; + + if (fattr->fa_xflags & ~mask) + return -EINVAL; + + fileattr_fill_xflags(fa, fattr->fa_xflags); + fa->fsx_xflags &= ~FS_XFLAG_RDONLY_MASK; + fa->fsx_extsize = fattr->fa_extsize; + fa->fsx_projid = fattr->fa_projid; + fa->fsx_cowextsize = fattr->fa_cowextsize; + + return 0; +} + static int copy_fsxattr_from_user(struct fileattr *fa, struct fsxattr __user *ufa) { @@ -344,3 +378,121 @@ int ioctl_fssetxattr(struct file *file, void __user *argp) return err; } EXPORT_SYMBOL(ioctl_fssetxattr); + +SYSCALL_DEFINE5(file_getattr, int, dfd, const char __user *, filename, + struct file_attr __user *, ufattr, size_t, usize, + unsigned int, at_flags) +{ + struct path filepath __free(path_put) = {}; + struct filename *name __free(putname) = NULL; + unsigned int lookup_flags = 0; + struct file_attr fattr; + struct fileattr fa; + int error; + + BUILD_BUG_ON(sizeof(struct file_attr) < FILE_ATTR_SIZE_VER0); + BUILD_BUG_ON(sizeof(struct file_attr) != FILE_ATTR_SIZE_LATEST); + + if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0) + return -EINVAL; + + if (!(at_flags & AT_SYMLINK_NOFOLLOW)) + lookup_flags |= LOOKUP_FOLLOW; + + if (usize > PAGE_SIZE) + return -E2BIG; + + if (usize < FILE_ATTR_SIZE_VER0) + return -EINVAL; + + name = getname_maybe_null(filename, at_flags); + if (IS_ERR(name)) + return PTR_ERR(name); + + if (!name && dfd >= 0) { + CLASS(fd, f)(dfd); + if (fd_empty(f)) + return -EBADF; + + filepath = fd_file(f)->f_path; + path_get(&filepath); + } else { + error = filename_lookup(dfd, name, lookup_flags, &filepath, + NULL); + if (error) + return error; + } + + error = vfs_fileattr_get(filepath.dentry, &fa); + if (error) + return error; + + fileattr_to_file_attr(&fa, &fattr); + error = copy_struct_to_user(ufattr, usize, &fattr, + sizeof(struct file_attr), NULL); + + return error; +} + +SYSCALL_DEFINE5(file_setattr, int, dfd, const char __user *, filename, + struct file_attr __user *, ufattr, size_t, usize, + unsigned int, at_flags) +{ + struct path filepath __free(path_put) = {}; + struct filename *name __free(putname) = NULL; + unsigned int lookup_flags = 0; + struct file_attr fattr; + struct fileattr fa; + int error; + + BUILD_BUG_ON(sizeof(struct file_attr) < FILE_ATTR_SIZE_VER0); + BUILD_BUG_ON(sizeof(struct file_attr) != FILE_ATTR_SIZE_LATEST); + + if ((at_flags & ~(AT_SYMLINK_NOFOLLOW | AT_EMPTY_PATH)) != 0) + return -EINVAL; + + if (!(at_flags & AT_SYMLINK_NOFOLLOW)) + lookup_flags |= LOOKUP_FOLLOW; + + if (usize > PAGE_SIZE) + return -E2BIG; + + if (usize < FILE_ATTR_SIZE_VER0) + return -EINVAL; + + error = copy_struct_from_user(&fattr, sizeof(struct file_attr), ufattr, + usize); + if (error) + return error; + + error = file_attr_to_fileattr(&fattr, &fa); + if (error) + return error; + + name = getname_maybe_null(filename, at_flags); + if (IS_ERR(name)) + return PTR_ERR(name); + + if (!name && dfd >= 0) { + CLASS(fd, f)(dfd); + if (fd_empty(f)) + return -EBADF; + + filepath = fd_file(f)->f_path; + path_get(&filepath); + } else { + error = filename_lookup(dfd, name, lookup_flags, &filepath, + NULL); + if (error) + return error; + } + + error = mnt_want_write(filepath.mnt); + if (!error) { + error = vfs_fileattr_set(mnt_idmap(filepath.mnt), + filepath.dentry, &fa); + mnt_drop_write(filepath.mnt); + } + + return error; +} diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index e5603cc91963..77f45e5d4413 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -78,6 +78,7 @@ struct cachestat; struct statmount; struct mnt_id_req; struct xattr_args; +struct file_attr; #include #include @@ -371,6 +372,12 @@ asmlinkage long sys_removexattrat(int dfd, const char __user *path, asmlinkage long sys_lremovexattr(const char __user *path, const char __user *name); asmlinkage long sys_fremovexattr(int fd, const char __user *name); +asmlinkage long sys_file_getattr(int dfd, const char __user *filename, + struct file_attr __user *attr, size_t usize, + unsigned int at_flags); +asmlinkage long sys_file_setattr(int dfd, const char __user *filename, + struct file_attr __user *attr, size_t usize, + unsigned int at_flags); asmlinkage long sys_getcwd(char __user *buf, unsigned long size); asmlinkage long sys_eventfd2(unsigned int count, int flags); asmlinkage long sys_epoll_create1(int flags); diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h index 2892a45023af..04e0077fb4c9 100644 --- a/include/uapi/asm-generic/unistd.h +++ b/include/uapi/asm-generic/unistd.h @@ -852,8 +852,14 @@ __SYSCALL(__NR_removexattrat, sys_removexattrat) #define __NR_open_tree_attr 467 __SYSCALL(__NR_open_tree_attr, sys_open_tree_attr) +/* fs/inode.c */ +#define __NR_file_getattr 468 +__SYSCALL(__NR_file_getattr, sys_file_getattr) +#define __NR_file_setattr 469 +__SYSCALL(__NR_file_setattr, sys_file_setattr) + #undef __NR_syscalls -#define __NR_syscalls 468 +#define __NR_syscalls 470 /* * 32 bit systems traditionally used different diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index 0098b0ce8ccb..9663dbdda181 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -148,6 +148,24 @@ struct fsxattr { unsigned char fsx_pad[8]; }; +/* + * Variable size structure for file_[sg]et_attr(). + * + * Note. This is alternative to the structure 'struct fileattr'/'struct fsxattr'. + * As this structure is passed to/from userspace with its size, this can + * be versioned based on the size. + */ +struct file_attr { + __u64 fa_xflags; /* xflags field value (get/set) */ + __u32 fa_extsize; /* extsize field value (get/set)*/ + __u32 fa_nextents; /* nextents field value (get) */ + __u32 fa_projid; /* project identifier (get/set) */ + __u32 fa_cowextsize; /* CoW extsize field value (get/set) */ +}; + +#define FILE_ATTR_SIZE_VER0 24 +#define FILE_ATTR_SIZE_LATEST FILE_ATTR_SIZE_VER0 + /* * Flags for the fsx_xflags field */ diff --git a/scripts/syscall.tbl b/scripts/syscall.tbl index 580b4e246aec..d1ae5e92c615 100644 --- a/scripts/syscall.tbl +++ b/scripts/syscall.tbl @@ -408,3 +408,5 @@ 465 common listxattrat sys_listxattrat 466 common removexattrat sys_removexattrat 467 common open_tree_attr sys_open_tree_attr +468 common file_getattr sys_file_getattr +469 common file_setattr sys_file_setattr -- cgit v1.2.3 From 60016e0116b8d33f95e797b011799e717766ec13 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Tue, 1 Jul 2025 15:31:11 -0600 Subject: docs: kdoc; Add a rudimentary class to represent output items This class is intended to replace the unstructured dict used to accumulate an entry to pass to an output module. For now, it remains unstructured, but it works well enough that the output classes don't notice the difference. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_item.py | 26 ++++++++++++++++++++++++++ scripts/lib/kdoc/kdoc_parser.py | 30 +++++++++--------------------- 2 files changed, 35 insertions(+), 21 deletions(-) create mode 100644 scripts/lib/kdoc/kdoc_item.py (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_item.py b/scripts/lib/kdoc/kdoc_item.py new file mode 100644 index 000000000000..add2cc772fec --- /dev/null +++ b/scripts/lib/kdoc/kdoc_item.py @@ -0,0 +1,26 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# A class that will, eventually, encapsulate all of the parsed data that we +# then pass into the output modules. +# + +class KdocItem: + def __init__(self, name, type, start_line, **other_stuff): + self.name = name + self.type = type + self.declaration_start_line = start_line + # + # Just save everything else into our own dict so that the output + # side can grab it directly as before. As we move things into more + # structured data, this will, hopefully, fade away. + # + self.other_stuff = other_stuff + + def get(self, key, default = None): + ret = self.other_stuff.get(key, default) + if ret == default: + return self.__dict__.get(key, default) + return ret + + def __getitem__(self, key): + return self.get(key) diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 831f061f61b8..a5a59b97a444 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -16,7 +16,7 @@ import re from pprint import pformat from kdoc_re import NestedMatch, KernRe - +from kdoc_item import KdocItem # # Regular expressions used to parse kernel-doc markups at KernelDoc class. @@ -271,32 +271,20 @@ class KernelDoc: The actual output and output filters will be handled elsewhere """ - # The implementation here is different than the original kernel-doc: - # instead of checking for output filters or actually output anything, - # it just stores the declaration content at self.entries, as the - # output will happen on a separate class. - # - # For now, we're keeping the same name of the function just to make - # easier to compare the source code of both scripts - - args["declaration_start_line"] = self.entry.declaration_start_line - args["type"] = dtype - args["warnings"] = self.entry.warnings - - # TODO: use colletions.OrderedDict to remove sectionlist + item = KdocItem(name, dtype, self.entry.declaration_start_line, **args) + item.warnings = self.entry.warnings - sections = args.get('sections', {}) - sectionlist = args.get('sectionlist', []) + sections = item.get('sections', {}) + sectionlist = item.get('sectionlist', []) # Drop empty sections # TODO: improve empty sections logic to emit warnings for section in ["Description", "Return"]: - if section in sectionlist: - if not sections[section].rstrip(): - del sections[section] - sectionlist.remove(section) + if section in sectionlist and not sections[section].rstrip(): + del sections[section] + sectionlist.remove(section) - self.entries.append((name, args)) + self.entries.append((name, item)) self.config.log.debug("Output: %s:%s = %s", dtype, name, pformat(args)) -- cgit v1.2.3 From 703f9074a8e10ac3fe939025233acb7c47529608 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Tue, 1 Jul 2025 15:54:09 -0600 Subject: docs: kdoc: simplify the output-item passing Since our output items contain their name, we don't need to pass it separately. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_files.py | 4 ++-- scripts/lib/kdoc/kdoc_parser.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py index 9be4a64df71d..9e09b45b02fa 100644 --- a/scripts/lib/kdoc/kdoc_files.py +++ b/scripts/lib/kdoc/kdoc_files.py @@ -275,8 +275,8 @@ class KernelFiles(): self.config.log.warning("No kernel-doc for file %s", fname) continue - for name, arg in self.results[fname]: - m = self.out_msg(fname, name, arg) + for arg in self.results[fname]: + m = self.out_msg(fname, arg.name, arg) if m is None: ln = arg.get("ln", 0) diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index a5a59b97a444..97380ff30a0d 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -284,7 +284,7 @@ class KernelDoc: del sections[section] sectionlist.remove(section) - self.entries.append((name, item)) + self.entries.append(item) self.config.log.debug("Output: %s:%s = %s", dtype, name, pformat(args)) -- cgit v1.2.3 From e7e540363cc52207c5245ad934180db1a1f96522 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 3 Jul 2025 12:43:57 -0600 Subject: docs: kdoc: don't reinvent string.strip() process_proto_type() and process_proto_function() reinventing the strip() string method with a whole series of separate regexes; take all that out and just use strip(). The previous implementation also (in process_proto_type()) removed C++ comments *after* the above dance, leaving trailing whitespace in that case; now we do the stripping afterward. This results in exactly one output change: the removal of a spurious space in the definition of BACKLIGHT_POWER_REDUCED - see https://docs.kernel.org/gpu/backlight.html#c.backlight_properties. I note that we are putting semicolons after #define lines that really shouldn't be there - a task for another day. Reviewed-by: Mauro Carvalho Chehab Tested-by: Akira Yokosawa Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250703184403.274408-2-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 27 +++++---------------------- 1 file changed, 5 insertions(+), 22 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 93938155fce2..d9ff2d066160 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1567,17 +1567,9 @@ class KernelDoc: self.entry.prototype += r.group(1) + " " if '{' in line or ';' in line or KernRe(r'\s*#\s*define').match(line): - # strip comments - r = KernRe(r'/\*.*?\*/') - self.entry.prototype = r.sub('', self.entry.prototype) - - # strip newlines/cr's - r = KernRe(r'[\r\n]+') - self.entry.prototype = r.sub(' ', self.entry.prototype) - - # strip leading spaces - r = KernRe(r'^\s+') - self.entry.prototype = r.sub('', self.entry.prototype) + # strip comments and surrounding spaces + r = KernRe(r'/\*.*\*/') + self.entry.prototype = r.sub('', self.entry.prototype).strip() # Handle self.entry.prototypes for function pointers like: # int (*pcs_config)(struct foo) @@ -1600,17 +1592,8 @@ class KernelDoc: def process_proto_type(self, ln, line): """Ancillary routine to process a type""" - # Strip newlines/cr's. - line = KernRe(r'[\r\n]+', re.S).sub(' ', line) - - # Strip leading spaces - line = KernRe(r'^\s+', re.S).sub('', line) - - # Strip trailing spaces - line = KernRe(r'\s+$', re.S).sub('', line) - - # Strip C99-style comments to the end of the line - line = KernRe(r"\/\/.*$", re.S).sub('', line) + # Strip C99-style comments and surrounding whitespace + line = KernRe(r"//.*$", re.S).sub('', line).strip() # To distinguish preprocessor directive from regular declaration later. if line.startswith('#'): -- cgit v1.2.3 From 8078e0ed1f3f3bac776b412b0f85ada86f91fef0 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 3 Jul 2025 12:43:58 -0600 Subject: docs: kdoc: micro-optimize KernRe Rework _add_regex() to avoid doing the lookup twice for the (hopefully common) cache-hit case. Reviewed-by: Mauro Carvalho Chehab Tested-by: Akira Yokosawa Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250703184403.274408-3-corbet@lwn.net --- scripts/lib/kdoc/kdoc_re.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_re.py b/scripts/lib/kdoc/kdoc_re.py index e81695b273bf..612223e1e723 100644 --- a/scripts/lib/kdoc/kdoc_re.py +++ b/scripts/lib/kdoc/kdoc_re.py @@ -29,12 +29,9 @@ class KernRe: """ Adds a new regex or re-use it from the cache. """ - - if string in re_cache: - self.regex = re_cache[string] - else: + self.regex = re_cache.get(string, None) + if not self.regex: self.regex = re.compile(string, flags=flags) - if self.cache: re_cache[string] = self.regex -- cgit v1.2.3 From 061a1c1a27c9ea7ae5d99f72b579d542e767a3e2 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 3 Jul 2025 12:43:59 -0600 Subject: docs: kdoc: remove the brcount floor in process_proto_type() Putting the floor under brcount does not change the output in any way, just remove it. Change the termination test from ==0 to <=0 to prevent infinite loops in case somebody does something truly wacko in the code. Reviewed-by: Mauro Carvalho Chehab Tested-by: Akira Yokosawa Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250703184403.274408-4-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index d9ff2d066160..935f2a3c4b47 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1609,9 +1609,7 @@ class KernelDoc: self.entry.brcount += r.group(2).count('{') self.entry.brcount -= r.group(2).count('}') - self.entry.brcount = max(self.entry.brcount, 0) - - if r.group(2) == ';' and self.entry.brcount == 0: + if r.group(2) == ';' and self.entry.brcount <= 0: self.dump_declaration(ln, self.entry.prototype) self.reset_state(ln) break -- cgit v1.2.3 From b8ac0259f80e12d12cc72572c097b31caf524c88 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 3 Jul 2025 12:44:00 -0600 Subject: docs: kdoc: rework type prototype parsing process_proto_type() is using a complex regex and a "while True" loop to split a declaration into chunks and, in the end, count brackets. Switch to using a simpler regex to just do the split directly, and handle each chunk as it comes. The result is, IMO, easier to understand and reason about. The old algorithm would occasionally elide the space between function parameters; see struct rng_alg->generate(), foe example. The only output difference is to not elide that space, which is more correct. Reviewed-by: Mauro Carvalho Chehab Tested-by: Akira Yokosawa Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250703184403.274408-5-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 43 ++++++++++++++++++++++++----------------- 1 file changed, 25 insertions(+), 18 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 935f2a3c4b47..61da297df623 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1594,30 +1594,37 @@ class KernelDoc: # Strip C99-style comments and surrounding whitespace line = KernRe(r"//.*$", re.S).sub('', line).strip() + if not line: + return # nothing to see here # To distinguish preprocessor directive from regular declaration later. if line.startswith('#'): line += ";" - - r = KernRe(r'([^\{\};]*)([\{\};])(.*)') - while True: - if r.search(line): - if self.entry.prototype: - self.entry.prototype += " " - self.entry.prototype += r.group(1) + r.group(2) - - self.entry.brcount += r.group(2).count('{') - self.entry.brcount -= r.group(2).count('}') - - if r.group(2) == ';' and self.entry.brcount <= 0: + # + # Split the declaration on any of { } or ;, and accumulate pieces + # until we hit a semicolon while not inside {brackets} + # + r = KernRe(r'(.*?)([{};])') + for chunk in r.split(line): + if chunk: # Ignore empty matches + self.entry.prototype += chunk + # + # This cries out for a match statement ... someday after we can + # drop Python 3.9 ... + # + if chunk == '{': + self.entry.brcount += 1 + elif chunk == '}': + self.entry.brcount -= 1 + elif chunk == ';' and self.entry.brcount <= 0: self.dump_declaration(ln, self.entry.prototype) self.reset_state(ln) - break - - line = r.group(3) - else: - self.entry.prototype += line - break + return + # + # We hit the end of the line while still in the declaration; put + # in a space to represent the newline. + # + self.entry.prototype += ' ' def process_proto(self, ln, line): """STATE_PROTO: reading a function/whatever prototype.""" -- cgit v1.2.3 From 414ccf92ae07486f84572d88fe02a256c852dca1 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 3 Jul 2025 12:44:01 -0600 Subject: docs: kdoc: some tweaks to process_proto_function() Add a set of comments to process_proto_function and reorganize the logic slightly; no functional change. Reviewed-by: Mauro Carvalho Chehab Tested-by: Akira Yokosawa Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250703184403.274408-6-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 43 +++++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 19 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 61da297df623..d5ef3ce87438 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1553,39 +1553,44 @@ class KernelDoc: """Ancillary routine to process a function prototype""" # strip C99-style comments to end of line - r = KernRe(r"\/\/.*$", re.S) - line = r.sub('', line) - + line = KernRe(r"\/\/.*$", re.S).sub('', line) + # + # Soak up the line's worth of prototype text, stopping at { or ; if present. + # if KernRe(r'\s*#\s*define').match(line): self.entry.prototype = line - elif line.startswith('#'): - # Strip other macros like #ifdef/#ifndef/#endif/... - pass - else: + elif not line.startswith('#'): # skip other preprocessor stuff r = KernRe(r'([^\{]*)') if r.match(line): self.entry.prototype += r.group(1) + " " - + # + # If we now have the whole prototype, clean it up and declare victory. + # if '{' in line or ';' in line or KernRe(r'\s*#\s*define').match(line): # strip comments and surrounding spaces - r = KernRe(r'/\*.*\*/') - self.entry.prototype = r.sub('', self.entry.prototype).strip() - + self.entry.prototype = KernRe(r'/\*.*\*/').sub('', self.entry.prototype).strip() + # # Handle self.entry.prototypes for function pointers like: # int (*pcs_config)(struct foo) - + # by turning it into + # int pcs_config(struct foo) + # r = KernRe(r'^(\S+\s+)\(\s*\*(\S+)\)') self.entry.prototype = r.sub(r'\1\2', self.entry.prototype) - + # + # Handle special declaration syntaxes + # if 'SYSCALL_DEFINE' in self.entry.prototype: self.entry.prototype = self.syscall_munge(ln, self.entry.prototype) - - r = KernRe(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT') - if r.search(self.entry.prototype): - self.entry.prototype = self.tracepoint_munge(ln, - self.entry.prototype) - + else: + r = KernRe(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT') + if r.search(self.entry.prototype): + self.entry.prototype = self.tracepoint_munge(ln, + self.entry.prototype) + # + # ... and we're done + # self.dump_function(ln, self.entry.prototype) self.reset_state(ln) -- cgit v1.2.3 From 5d77dcc07fde1a544e822bf5af745fede32623cf Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 3 Jul 2025 12:44:03 -0600 Subject: docs: kdoc: pretty up dump_enum() Add some comments to dump_enum to help the next person who has to figure out what it is actually doing. Reviewed-by: Mauro Carvalho Chehab Tested-by: Akira Yokosawa Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250703184403.274408-8-corbet@lwn.net --- scripts/lib/kdoc/kdoc_parser.py | 39 +++++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 14 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index d5ef3ce87438..831f061f61b8 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -860,39 +860,48 @@ class KernelDoc: # Strip #define macros inside enums proto = KernRe(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=re.S).sub('', proto) - members = None - declaration_name = None - + # + # Parse out the name and members of the enum. Typedef form first. + # r = KernRe(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;') if r.search(proto): declaration_name = r.group(2) members = r.group(1).rstrip() + # + # Failing that, look for a straight enum + # else: r = KernRe(r'enum\s+(\w*)\s*\{(.*)\}') if r.match(proto): declaration_name = r.group(1) members = r.group(2).rstrip() - - if not members: - self.emit_msg(ln, f"{proto}: error: Cannot parse enum!") - return - + # + # OK, this isn't going to work. + # + else: + self.emit_msg(ln, f"{proto}: error: Cannot parse enum!") + return + # + # Make sure we found what we were expecting. + # if self.entry.identifier != declaration_name: if self.entry.identifier == "": self.emit_msg(ln, f"{proto}: wrong kernel-doc identifier on prototype") else: self.emit_msg(ln, - f"expecting prototype for enum {self.entry.identifier}. Prototype was for enum {declaration_name} instead") + f"expecting prototype for enum {self.entry.identifier}. " + f"Prototype was for enum {declaration_name} instead") return if not declaration_name: declaration_name = "(anonymous)" - + # + # Parse out the name of each enum member, and verify that we + # have a description for it. + # member_set = set() - - members = KernRe(r'\([^;]*?[\)]').sub('', members) - + members = KernRe(r'\([^;)]*\)').sub('', members) for arg in members.split(','): if not arg: continue @@ -903,7 +912,9 @@ class KernelDoc: self.emit_msg(ln, f"Enum value '{arg}' not described in enum '{declaration_name}'") member_set.add(arg) - + # + # Ensure that every described member actually exists in the enum. + # for k in self.entry.parameterdescs: if k not in member_set: self.emit_msg(ln, -- cgit v1.2.3 From eade9f57ca7245cc59072706f0f1fdbc446fda61 Mon Sep 17 00:00:00 2001 From: Paolo Abeni Date: Tue, 8 Jul 2025 17:54:46 +0200 Subject: scripts/kernel_doc.py: properly handle VIRTIO_DECLARE_FEATURES The mentioned macro introduce by the next patch will foul kdoc; fully expand the mentioned macro to avoid the issue. Signed-off-by: Paolo Abeni --- scripts/lib/kdoc/kdoc_parser.py | 1 + 1 file changed, 1 insertion(+) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 062453eefc7a..3115558925ac 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -666,6 +666,7 @@ class KernelDoc: (KernRe(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\1 \2[]'), (KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + args_pattern + r'\)', re.S), r'dma_addr_t \1'), (KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + args_pattern + r'\)', re.S), r'__u32 \1'), + (KernRe(r'VIRTIO_DECLARE_FEATURES\s*\(' + args_pattern + r'\)', re.S), r'u64 \1; u64 \1_array[VIRTIO_FEATURES_DWORDS]'), ] # Regexes here are guaranteed to have the end limiter matching -- cgit v1.2.3 From 7627b459aa0737bdd62a8591a1481cda467f20e3 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 23 Jun 2025 09:41:52 -0700 Subject: scripts/gdb: fix interrupts display after MCP on x86 The text line would not be appended to as it should have, it should have been a '+=' but ended up being a '==', fix that. Link: https://lkml.kernel.org/r/20250623164153.746359-1-florian.fainelli@broadcom.com Fixes: b0969d7687a7 ("scripts/gdb: print interrupts") Signed-off-by: Florian Fainelli Cc: Jan Kiszka Cc: Kieran Bingham Cc: Signed-off-by: Andrew Morton --- scripts/gdb/linux/interrupts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/gdb/linux/interrupts.py b/scripts/gdb/linux/interrupts.py index 616a5f26377a..199d9e8193f4 100644 --- a/scripts/gdb/linux/interrupts.py +++ b/scripts/gdb/linux/interrupts.py @@ -142,7 +142,7 @@ def x86_show_interupts(prec): if constants.LX_CONFIG_X86_MCE: text += x86_show_mce(prec, "&mce_exception_count", "MCE", "Machine check exceptions") - text == x86_show_mce(prec, "&mce_poll_count", "MCP", "Machine check polls") + text += x86_show_mce(prec, "&mce_poll_count", "MCP", "Machine check polls") text += show_irq_err_count(prec) -- cgit v1.2.3 From a02b0cde8ee515ee0c8efd33e7fbe6830c282e69 Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Tue, 24 Jun 2025 19:10:20 -0700 Subject: scripts/gdb: fix interrupts.py after maple tree conversion In commit 721255b9826b ("genirq: Use a maple tree for interrupt descriptor management"), the irq_desc_tree was replaced with a sparse_irqs tree using a maple tree structure. Since the script looked for the irq_desc_tree symbol which is no longer available, no interrupts would be printed and the script output would not be useful anymore. In addition to looking up the correct symbol (sparse_irqs), a new module (mapletree.py) is added whose mtree_load() implementation is largely copied after the C version and uses the same variable and intermediate function names wherever possible to ensure that both the C and Python version be updated in the future. This restores the scripts' output to match that of /proc/interrupts. Link: https://lkml.kernel.org/r/20250625021020.1056930-1-florian.fainelli@broadcom.com Fixes: 721255b9826b ("genirq: Use a maple tree for interrupt descriptor management") Signed-off-by: Florian Fainelli Cc: Jan Kiszka Cc: Kieran Bingham Cc: Shanker Donthineni Cc: Thomas Gleinxer Cc: Signed-off-by: Andrew Morton --- scripts/gdb/linux/constants.py.in | 7 ++ scripts/gdb/linux/interrupts.py | 12 +- scripts/gdb/linux/mapletree.py | 252 ++++++++++++++++++++++++++++++++++++++ scripts/gdb/linux/xarray.py | 28 +++++ 4 files changed, 293 insertions(+), 6 deletions(-) create mode 100644 scripts/gdb/linux/mapletree.py create mode 100644 scripts/gdb/linux/xarray.py (limited to 'scripts') diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in index fd6bd69c5096..f795302ddfa8 100644 --- a/scripts/gdb/linux/constants.py.in +++ b/scripts/gdb/linux/constants.py.in @@ -20,6 +20,7 @@ #include #include #include +#include #include #include #include @@ -93,6 +94,12 @@ LX_GDBPARSED(RADIX_TREE_MAP_SIZE) LX_GDBPARSED(RADIX_TREE_MAP_SHIFT) LX_GDBPARSED(RADIX_TREE_MAP_MASK) +/* linux/maple_tree.h */ +LX_VALUE(MAPLE_NODE_SLOTS) +LX_VALUE(MAPLE_RANGE64_SLOTS) +LX_VALUE(MAPLE_ARANGE64_SLOTS) +LX_GDBPARSED(MAPLE_NODE_MASK) + /* linux/vmalloc.h */ LX_VALUE(VM_IOREMAP) LX_VALUE(VM_ALLOC) diff --git a/scripts/gdb/linux/interrupts.py b/scripts/gdb/linux/interrupts.py index 199d9e8193f4..492d1f1e9529 100644 --- a/scripts/gdb/linux/interrupts.py +++ b/scripts/gdb/linux/interrupts.py @@ -7,7 +7,7 @@ import gdb from linux import constants from linux import cpus from linux import utils -from linux import radixtree +from linux import mapletree irq_desc_type = utils.CachedType("struct irq_desc") @@ -23,12 +23,12 @@ def irqd_is_level(desc): def show_irq_desc(prec, irq): text = "" - desc = radixtree.lookup(gdb.parse_and_eval("&irq_desc_tree"), irq) + desc = mapletree.mtree_load(gdb.parse_and_eval("&sparse_irqs"), irq) if desc is None: return text - desc = desc.cast(irq_desc_type.get_type()) - if desc is None: + desc = desc.cast(irq_desc_type.get_type().pointer()) + if desc == 0: return text if irq_settings_is_hidden(desc): @@ -221,8 +221,8 @@ class LxInterruptList(gdb.Command): gdb.write("CPU%-8d" % cpu) gdb.write("\n") - if utils.gdb_eval_or_none("&irq_desc_tree") is None: - return + if utils.gdb_eval_or_none("&sparse_irqs") is None: + raise gdb.GdbError("Unable to find the sparse IRQ tree, is CONFIG_SPARSE_IRQ enabled?") for irq in range(nr_irqs): gdb.write(show_irq_desc(prec, irq)) diff --git a/scripts/gdb/linux/mapletree.py b/scripts/gdb/linux/mapletree.py new file mode 100644 index 000000000000..d52d51c0a03f --- /dev/null +++ b/scripts/gdb/linux/mapletree.py @@ -0,0 +1,252 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Maple tree helpers +# +# Copyright (c) 2025 Broadcom +# +# Authors: +# Florian Fainelli + +import gdb + +from linux import utils +from linux import constants +from linux import xarray + +maple_tree_root_type = utils.CachedType("struct maple_tree") +maple_node_type = utils.CachedType("struct maple_node") +maple_enode_type = utils.CachedType("void") + +maple_dense = 0 +maple_leaf_64 = 1 +maple_range_64 = 2 +maple_arange_64 = 3 + +class Mas(object): + ma_active = 0 + ma_start = 1 + ma_root = 2 + ma_none = 3 + ma_pause = 4 + ma_overflow = 5 + ma_underflow = 6 + ma_error = 7 + + def __init__(self, mt, first, end): + if mt.type == maple_tree_root_type.get_type().pointer(): + self.tree = mt.dereference() + elif mt.type != maple_tree_root_type.get_type(): + raise gdb.GdbError("must be {} not {}" + .format(maple_tree_root_type.get_type().pointer(), mt.type)) + self.tree = mt + self.index = first + self.last = end + self.node = None + self.status = self.ma_start + self.min = 0 + self.max = -1 + + def is_start(self): + # mas_is_start() + return self.status == self.ma_start + + def is_ptr(self): + # mas_is_ptr() + return self.status == self.ma_root + + def is_none(self): + # mas_is_none() + return self.status == self.ma_none + + def root(self): + # mas_root() + return self.tree['ma_root'].cast(maple_enode_type.get_type().pointer()) + + def start(self): + # mas_start() + if self.is_start() is False: + return None + + self.min = 0 + self.max = ~0 + + while True: + self.depth = 0 + root = self.root() + if xarray.xa_is_node(root): + self.depth = 0 + self.status = self.ma_active + self.node = mte_safe_root(root) + self.offset = 0 + if mte_dead_node(self.node) is True: + continue + + return None + + self.node = None + # Empty tree + if root is None: + self.status = self.ma_none + self.offset = constants.LX_MAPLE_NODE_SLOTS + return None + + # Single entry tree + self.status = self.ma_root + self.offset = constants.LX_MAPLE_NODE_SLOTS + + if self.index != 0: + return None + + return root + + return None + + def reset(self): + # mas_reset() + self.status = self.ma_start + self.node = None + +def mte_safe_root(node): + if node.type != maple_enode_type.get_type().pointer(): + raise gdb.GdbError("{} must be {} not {}" + .format(mte_safe_root.__name__, maple_enode_type.get_type().pointer(), node.type)) + ulong_type = utils.get_ulong_type() + indirect_ptr = node.cast(ulong_type) & ~0x2 + val = indirect_ptr.cast(maple_enode_type.get_type().pointer()) + return val + +def mte_node_type(entry): + ulong_type = utils.get_ulong_type() + val = None + if entry.type == maple_enode_type.get_type().pointer(): + val = entry.cast(ulong_type) + elif entry.type == ulong_type: + val = entry + else: + raise gdb.GdbError("{} must be {} not {}" + .format(mte_node_type.__name__, maple_enode_type.get_type().pointer(), entry.type)) + return (val >> 0x3) & 0xf + +def ma_dead_node(node): + if node.type != maple_node_type.get_type().pointer(): + raise gdb.GdbError("{} must be {} not {}" + .format(ma_dead_node.__name__, maple_node_type.get_type().pointer(), node.type)) + ulong_type = utils.get_ulong_type() + parent = node['parent'] + indirect_ptr = node['parent'].cast(ulong_type) & ~constants.LX_MAPLE_NODE_MASK + return indirect_ptr == node + +def mte_to_node(enode): + ulong_type = utils.get_ulong_type() + if enode.type == maple_enode_type.get_type().pointer(): + indirect_ptr = enode.cast(ulong_type) + elif enode.type == ulong_type: + indirect_ptr = enode + else: + raise gdb.GdbError("{} must be {} not {}" + .format(mte_to_node.__name__, maple_enode_type.get_type().pointer(), enode.type)) + indirect_ptr = indirect_ptr & ~constants.LX_MAPLE_NODE_MASK + return indirect_ptr.cast(maple_node_type.get_type().pointer()) + +def mte_dead_node(enode): + if enode.type != maple_enode_type.get_type().pointer(): + raise gdb.GdbError("{} must be {} not {}" + .format(mte_dead_node.__name__, maple_enode_type.get_type().pointer(), enode.type)) + node = mte_to_node(enode) + return ma_dead_node(node) + +def ma_is_leaf(tp): + result = tp < maple_range_64 + return tp < maple_range_64 + +def mt_pivots(t): + if t == maple_dense: + return 0 + elif t == maple_leaf_64 or t == maple_range_64: + return constants.LX_MAPLE_RANGE64_SLOTS - 1 + elif t == maple_arange_64: + return constants.LX_MAPLE_ARANGE64_SLOTS - 1 + +def ma_pivots(node, t): + if node.type != maple_node_type.get_type().pointer(): + raise gdb.GdbError("{}: must be {} not {}" + .format(ma_pivots.__name__, maple_node_type.get_type().pointer(), node.type)) + if t == maple_arange_64: + return node['ma64']['pivot'] + elif t == maple_leaf_64 or t == maple_range_64: + return node['mr64']['pivot'] + else: + return None + +def ma_slots(node, tp): + if node.type != maple_node_type.get_type().pointer(): + raise gdb.GdbError("{}: must be {} not {}" + .format(ma_slots.__name__, maple_node_type.get_type().pointer(), node.type)) + if tp == maple_arange_64: + return node['ma64']['slot'] + elif tp == maple_range_64 or tp == maple_leaf_64: + return node['mr64']['slot'] + elif tp == maple_dense: + return node['slot'] + else: + return None + +def mt_slot(mt, slots, offset): + ulong_type = utils.get_ulong_type() + return slots[offset].cast(ulong_type) + +def mtree_lookup_walk(mas): + ulong_type = utils.get_ulong_type() + n = mas.node + + while True: + node = mte_to_node(n) + tp = mte_node_type(n) + pivots = ma_pivots(node, tp) + end = mt_pivots(tp) + offset = 0 + while True: + if pivots[offset] >= mas.index: + break + if offset >= end: + break + offset += 1 + + slots = ma_slots(node, tp) + n = mt_slot(mas.tree, slots, offset) + if ma_dead_node(node) is True: + mas.reset() + return None + break + + if ma_is_leaf(tp) is True: + break + + return n + +def mtree_load(mt, index): + ulong_type = utils.get_ulong_type() + # MT_STATE(...) + mas = Mas(mt, index, index) + entry = None + + while True: + entry = mas.start() + if mas.is_none(): + return None + + if mas.is_ptr(): + if index != 0: + entry = None + return entry + + entry = mtree_lookup_walk(mas) + if entry is None and mas.is_start(): + continue + else: + break + + if xarray.xa_is_zero(entry): + return None + + return entry diff --git a/scripts/gdb/linux/xarray.py b/scripts/gdb/linux/xarray.py new file mode 100644 index 000000000000..f4477b5def75 --- /dev/null +++ b/scripts/gdb/linux/xarray.py @@ -0,0 +1,28 @@ +# SPDX-License-Identifier: GPL-2.0 +# +# Xarray helpers +# +# Copyright (c) 2025 Broadcom +# +# Authors: +# Florian Fainelli + +import gdb + +from linux import utils +from linux import constants + +def xa_is_internal(entry): + ulong_type = utils.get_ulong_type() + return ((entry.cast(ulong_type) & 3) == 2) + +def xa_mk_internal(v): + return ((v << 2) | 2) + +def xa_is_zero(entry): + ulong_type = utils.get_ulong_type() + return entry.cast(ulong_type) == xa_mk_internal(257) + +def xa_is_node(entry): + ulong_type = utils.get_ulong_type() + return xa_is_internal(entry) and (entry.cast(ulong_type) > 4096) -- cgit v1.2.3 From 50f4d2ba26d5c3a4687ae0569be3bbf1c8f0cbed Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Mon, 23 Jun 2025 20:00:19 -0700 Subject: scripts/gdb: de-reference per-CPU MCE interrupts The per-CPU MCE interrupts are looked up by reference and need to be de-referenced before printing, otherwise we print the addresses of the variables instead of their contents: MCE: 18379471554386948492 Machine check exceptions MCP: 18379471554386948488 Machine check polls The corrected output looks like this instead now: MCE: 0 Machine check exceptions MCP: 1 Machine check polls Link: https://lkml.kernel.org/r/20250625021109.1057046-1-florian.fainelli@broadcom.com Link: https://lkml.kernel.org/r/20250624030020.882472-1-florian.fainelli@broadcom.com Fixes: b0969d7687a7 ("scripts/gdb: print interrupts") Signed-off-by: Florian Fainelli Cc: Jan Kiszka Cc: Kieran Bingham Cc: Signed-off-by: Andrew Morton --- scripts/gdb/linux/interrupts.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/gdb/linux/interrupts.py b/scripts/gdb/linux/interrupts.py index 492d1f1e9529..f4f715a8f0e3 100644 --- a/scripts/gdb/linux/interrupts.py +++ b/scripts/gdb/linux/interrupts.py @@ -110,7 +110,7 @@ def x86_show_mce(prec, var, pfx, desc): pvar = gdb.parse_and_eval(var) text = "%*s: " % (prec, pfx) for cpu in cpus.each_online_cpu(): - text += "%10u " % (cpus.per_cpu(pvar, cpu)) + text += "%10u " % (cpus.per_cpu(pvar, cpu).dereference()) text += " %s\n" % (desc) return text -- cgit v1.2.3 From e6d3e653b084f003977bf2e33820cb84d2e4541f Mon Sep 17 00:00:00 2001 From: Illia Ostapyshyn Date: Sun, 29 Jun 2025 02:38:11 +0200 Subject: scripts: gdb: vfs: support external dentry names d_shortname of struct dentry only reserves D_NAME_INLINE_LEN characters and contains garbage for longer names. Use d_name instead, which always references the valid name. Link: https://lore.kernel.org/all/20250525213709.878287-2-illia@yshyn.com/ Link: https://lkml.kernel.org/r/20250629003811.2420418-1-illia@yshyn.com Fixes: 79300ac805b6 ("scripts/gdb: fix dentry_name() lookup") Signed-off-by: Illia Ostapyshyn Tested-by: Florian Fainelli Reviewed-by: Florian Fainelli Cc: Al Viro Cc: Christian Brauner Cc: Jan Kara Cc: Jan Kiszka Cc: Kieran Bingham Cc: Signed-off-by: Andrew Morton --- scripts/gdb/linux/vfs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/gdb/linux/vfs.py b/scripts/gdb/linux/vfs.py index b5fbb18ccb77..9e921b645a68 100644 --- a/scripts/gdb/linux/vfs.py +++ b/scripts/gdb/linux/vfs.py @@ -22,7 +22,7 @@ def dentry_name(d): if parent == d or parent == 0: return "" p = dentry_name(d['d_parent']) + "/" - return p + d['d_shortname']['string'].string() + return p + d['d_name']['name'].string() class DentryName(gdb.Function): """Return string of the full path of a dentry. -- cgit v1.2.3 From 7e43195c609f0499e46c6bfa9472e39c76af445b Mon Sep 17 00:00:00 2001 From: Casey Chen Date: Tue, 10 Jun 2025 10:22:58 -0600 Subject: alloc_tag: remove empty module tag section The empty MOD_CODETAG_SECTIONS() macro added an incomplete .data section in module linker script, which caused symbol lookup tools like gdb to misinterpret symbol addresses e.g., __ib_process_cq incorrectly mapping to unrelated functions like below. (gdb) disas __ib_process_cq Dump of assembler code for function trace_event_fields_cq_schedule: Removing the empty section restores proper symbol resolution and layout, ensuring .data placement behaves as expected. Link: https://lkml.kernel.org/r/20250610162258.324645-1-cachen@purestorage.com Fixes: 0db6f8d7820a ("alloc_tag: load module tags into separate contiguous memory") 22d407b164ff ("lib: add allocation tagging support for memory allocation profiling") Signed-off-by: Casey Chen Reviewed-by: Yuanyuan Zhong Acked-by: Suren Baghdasaryan Cc: Arnd Bergmann Cc: Kent Overstreet Cc: Luis Chamberalin Cc: Pasha Tatashin Signed-off-by: Andrew Morton --- include/asm-generic/codetag.lds.h | 6 ------ scripts/module.lds.S | 5 ----- 2 files changed, 11 deletions(-) (limited to 'scripts') diff --git a/include/asm-generic/codetag.lds.h b/include/asm-generic/codetag.lds.h index 372c320c5043..a45fe3d141a1 100644 --- a/include/asm-generic/codetag.lds.h +++ b/include/asm-generic/codetag.lds.h @@ -11,12 +11,6 @@ #define CODETAG_SECTIONS() \ SECTION_WITH_BOUNDARIES(alloc_tags) -/* - * Module codetags which aren't used after module unload, therefore have the - * same lifespan as the module and can be safely unloaded with the module. - */ -#define MOD_CODETAG_SECTIONS() - #define MOD_SEPARATE_CODETAG_SECTION(_name) \ .codetag.##_name : { \ SECTION_WITH_BOUNDARIES(_name) \ diff --git a/scripts/module.lds.S b/scripts/module.lds.S index 450f1088d5fd..ee79c41059f3 100644 --- a/scripts/module.lds.S +++ b/scripts/module.lds.S @@ -52,17 +52,12 @@ SECTIONS { .data : { *(.data .data.[0-9a-zA-Z_]*) *(.data..L*) - MOD_CODETAG_SECTIONS() } .rodata : { *(.rodata .rodata.[0-9a-zA-Z_]*) *(.rodata..L*) } -#else - .data : { - MOD_CODETAG_SECTIONS() - } #endif MOD_SEPARATE_CODETAG_SECTIONS() } -- cgit v1.2.3 From 5eee4c2b2aebfd3c8f11d9722e49d838da4e4150 Mon Sep 17 00:00:00 2001 From: Antonio Borneo Date: Mon, 16 Jun 2025 09:59:13 +0200 Subject: checkpatch: use utf-8 match for spell checking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The current code that checks for misspelling verifies, in a more complex regex, if $rawline matches [^\w]($misspellings)[^\w] Being $rawline a byte-string, a utf-8 character in $rawline can match the non-word-char [^\w]. E.g.: ./scripts/checkpatch.pl --git 81c2f059ab9 WARNING: 'ment' may be misspelled - perhaps 'meant'? #36: FILE: MAINTAINERS:14360: +M: Clément Léger ^^^^ Use a utf-8 version of $rawline for spell checking. Link: https://lkml.kernel.org/r/20250616-b4-checkpatch-upstream-v2-1-5600ce4a3b43@foss.st.com Signed-off-by: Antonio Borneo Signed-off-by: Clément Le Goffic Cc: Andy Whitcroft Cc: Dwaipayan Ray Cc: Joe Perches Cc: Lukas Bulwahn Signed-off-by: Andrew Morton --- scripts/checkpatch.pl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 664f7b7a622c..489b74d52abe 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -3502,9 +3502,10 @@ sub process { # Check for various typo / spelling mistakes if (defined($misspellings) && ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) { - while ($rawline =~ /(?:^|[^\w\-'`])($misspellings)(?:[^\w\-'`]|$)/gi) { + my $rawline_utf8 = decode("utf8", $rawline); + while ($rawline_utf8 =~ /(?:^|[^\w\-'`])($misspellings)(?:[^\w\-'`]|$)/gi) { my $typo = $1; - my $blank = copy_spacing($rawline); + my $blank = copy_spacing($rawline_utf8); my $ptr = substr($blank, 0, $-[1]) . "^" x length($typo); my $hereptr = "$hereline$ptr\n"; my $typo_fix = $spelling_fix{lc($typo)}; -- cgit v1.2.3 From 41a7f737685eed2700654720d3faaffdf0132135 Mon Sep 17 00:00:00 2001 From: Johannes Berg Date: Wed, 18 Jun 2025 15:46:02 +0200 Subject: scripts: gdb: move MNT_* constants to gdb-parsed Since these are now no longer defines, but in an enum. Link: https://lkml.kernel.org/r/20250618134629.25700-2-johannes@sipsolutions.net Fixes: 101f2bbab541 ("fs: convert mount flags to enum") Reviewed-by: Benjamin Berg Signed-off-by: Johannes Berg Cc: Jan Kiszka Cc: Kieran Bingham Cc: Stephen Brennan Signed-off-by: Andrew Morton --- scripts/gdb/linux/constants.py.in | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'scripts') diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in index fd6bd69c5096..d5e3069f42a7 100644 --- a/scripts/gdb/linux/constants.py.in +++ b/scripts/gdb/linux/constants.py.in @@ -73,12 +73,12 @@ if IS_BUILTIN(CONFIG_MODULES): LX_GDBPARSED(MOD_RO_AFTER_INIT) /* linux/mount.h */ -LX_VALUE(MNT_NOSUID) -LX_VALUE(MNT_NODEV) -LX_VALUE(MNT_NOEXEC) -LX_VALUE(MNT_NOATIME) -LX_VALUE(MNT_NODIRATIME) -LX_VALUE(MNT_RELATIME) +LX_GDBPARSED(MNT_NOSUID) +LX_GDBPARSED(MNT_NODEV) +LX_GDBPARSED(MNT_NOEXEC) +LX_GDBPARSED(MNT_NOATIME) +LX_GDBPARSED(MNT_NODIRATIME) +LX_GDBPARSED(MNT_RELATIME) /* linux/threads.h */ LX_VALUE(NR_CPUS) -- cgit v1.2.3 From 22c2ed6996ac34df506040a069fac3e5100b5c0e Mon Sep 17 00:00:00 2001 From: Brian Norris Date: Wed, 2 Jul 2025 16:52:00 -0700 Subject: checkpatch: check for missing sentinels in ID arrays All of the ID tables based on (of_device_id, pci_device_id, ...) require their arrays to end in an empty sentinel value. That's usually spelled with an empty initializer entry (e.g., "{}"), but also sometimes with explicit 0 entries, field initializers (e.g., '.id = ""'), or even a macro entry (like PCMCIA_DEVICE_NULL). Without a sentinel, device-matching code may read out of bounds. I've found a number of such bugs in driver reviews, and we even occasionally commit one to the tree. See commit 5751eee5c620 ("i2c: nomadik: Add missing sentinel to match table") for example. Teach checkpatch to find these ID tables, and complain if it looks like there wasn't a sentinel value. Test output: $ git format-patch -1 a0d15cc47f29be6d --stdout | scripts/checkpatch.pl - ERROR: missing sentinel in ID array #57: FILE: drivers/i2c/busses/i2c-nomadik.c:1073: +static const struct of_device_id nmk_i2c_eyeq_match_table[] = { { .compatible = "XXXXXXXXXXXXXXXXXX", .data = (void *)(NMK_I2C_EYEQ_FLAG_32B_BUS | NMK_I2C_EYEQ_FLAG_IS_EYEQ5), }, }; total: 1 errors, 0 warnings, 66 lines checked NOTE: For some of the reported defects, checkpatch may be able to mechanically convert to the typical style using --fix or --fix-inplace. "[PATCH] i2c: nomadik: switch from of_device_is_compatible() to" has style problems, please review. NOTE: If any of the errors are false positives, please report them to the maintainer, see CHECKPATCH in MAINTAINERS. When run across the entire tree (scripts/checkpatch.pl -q --types MISSING_SENTINEL -f ...), false positives exist: * where macros are used that hide the table from analysis (e.g., drivers/gpu/drm/radeon/radeon_drv.c / radeon_PCI_IDS). There are fewer than 5 of these. * where such tables are processed correctly via ARRAY_SIZE() (fewer than 5 instances). This is by far not the typical usage of *_device_id arrays. * some odd parsing artifacts, where ctx_statement_block() seems to quit in the middle of a block due to #if/#else/#endif. Also, not every "struct *_device_id" is in fact a sentinel-requiring structure, but even with such types, false positives are very rare. Link: https://lkml.kernel.org/r/20250702235245.1007351-1-briannorris@chromium.org Signed-off-by: Brian Norris Acked-by: Joe Perches Cc: Andy Whitcroft Cc: Brian Norris Cc: Dwaipayan Ray Cc: Lukas Bulwahn Signed-off-by: Andrew Morton --- scripts/checkpatch.pl | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) (limited to 'scripts') diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 489b74d52abe..d4c24318548c 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -685,6 +685,9 @@ our $tracing_logging_tags = qr{(?xi: [\.\!:\s]* )}; +# Device ID types like found in include/linux/mod_devicetable.h. +our $dev_id_types = qr{\b[a-z]\w*_device_id\b}; + sub edit_distance_min { my (@arr) = @_; my $len = scalar @arr; @@ -7679,6 +7682,31 @@ sub process { WARN("DUPLICATED_SYSCTL_CONST", "duplicated sysctl range checking value '$1', consider using the shared one in include/linux/sysctl.h\n" . $herecurr); } + +# Check that *_device_id tables have sentinel entries. + if (defined $stat && $line =~ /struct\s+$dev_id_types\s+\w+\s*\[\s*\]\s*=\s*\{/) { + my $stripped = $stat; + + # Strip diff line prefixes. + $stripped =~ s/(^|\n)./$1/g; + # Line continuations. + $stripped =~ s/\\\n/\n/g; + # Strip whitespace, empty strings, zeroes, and commas. + $stripped =~ s/""//g; + $stripped =~ s/0x0//g; + $stripped =~ s/[\s$;,0]//g; + # Strip field assignments. + $stripped =~ s/\.$Ident=//g; + + if (!(substr($stripped, -4) eq "{}};" || + substr($stripped, -6) eq "{{}}};" || + $stripped =~ /ISAPNP_DEVICE_SINGLE_END}};$/ || + $stripped =~ /ISAPNP_CARD_END}};$/ || + $stripped =~ /NULL};$/ || + $stripped =~ /PCMCIA_DEVICE_NULL};$/)) { + ERROR("MISSING_SENTINEL", "missing sentinel in ID array\n" . "$here\n$stat\n"); + } + } } # If we have no input at all, then there is nothing to report on -- cgit v1.2.3 From b6d0427cfc699243fc1f82087b58f63d389321aa Mon Sep 17 00:00:00 2001 From: Ilya Leoshkevich Date: Wed, 25 Jun 2025 17:36:52 +0200 Subject: scripts/gdb/symbols: make lx-symbols skip the s390 decompressor When one starts QEMU with the -S flag and attaches GDB, the kernel is not yet loaded, and the current instruction is an entry point to the decompressor. In case the intention is to debug the early kernel boot, and not the decompressor, e.g., put a breakpoint on some kernel function and see all the invocations, one has to skip the decompressor. There are many ways to do this, and so far people wrote private scripts or memorized certain command sequences. Make it work out of the box like this: $ gdb -ex 'target remote :6812' -ex 'source vmlinux-gdb.py' vmlinux Remote debugging using :6812 0x0000000000010000 in ?? () (gdb) lx-symbols loading vmlinux (gdb) x/i $pc => 0x3ffe0100000 : lghi %r2,0 Implement this by reading the address of the jump_to_kernel() function from the lowcore, and step until DAT is turned on. Signed-off-by: Ilya Leoshkevich Acked-by: Jan Kiszka Tested-by: Alexander Gordeev Link: https://lore.kernel.org/r/20250625154220.75300-3-iii@linux.ibm.com Signed-off-by: Alexander Gordeev --- scripts/gdb/linux/symbols.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'scripts') diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py index 2332bd8eddf1..6edb99221675 100644 --- a/scripts/gdb/linux/symbols.py +++ b/scripts/gdb/linux/symbols.py @@ -84,6 +84,30 @@ def get_kerneloffset(): return None +def is_in_s390_decompressor(): + # DAT is always off in decompressor. Use this as an indicator. + # Note that in the kernel, DAT can be off during kexec() or restart. + # Accept this imprecision in order to avoid complicating things. + # It is unlikely that someone will run lx-symbols at these points. + pswm = int(gdb.parse_and_eval("$pswm")) + return (pswm & 0x0400000000000000) == 0 + + +def skip_decompressor(): + if utils.is_target_arch("s390"): + if is_in_s390_decompressor(): + # The address of the jump_to_kernel function is statically placed + # into svc_old_psw.addr (see ipl_data.c); read it from there. DAT + # is off, so we do not need to care about lowcore relocation. + svc_old_pswa = 0x148 + jump_to_kernel = int(gdb.parse_and_eval("*(unsigned long long *)" + + hex(svc_old_pswa))) + gdb.execute("tbreak *" + hex(jump_to_kernel)) + gdb.execute("continue") + while is_in_s390_decompressor(): + gdb.execute("stepi") + + class LxSymbols(gdb.Command): """(Re-)load symbols of Linux kernel and currently loaded modules. @@ -204,6 +228,8 @@ lx-symbols command.""" saved_state['breakpoint'].enabled = saved_state['enabled'] def invoke(self, arg, from_tty): + skip_decompressor() + self.module_paths = [os.path.abspath(os.path.expanduser(p)) for p in arg.split()] self.module_paths.append(os.getcwd()) -- cgit v1.2.3 From 950a81224e8bda92813c5ecf851f488c94f06aba Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 9 Jul 2025 13:01:09 -0700 Subject: lib/crypto: tests: Add hash-test-template.h and gen-hash-testvecs.py Add hash-test-template.h which generates the following KUnit test cases for hash functions: test_hash_test_vectors test_hash_all_lens_up_to_4096 test_hash_incremental_updates test_hash_buffer_overruns test_hash_overlaps test_hash_alignment_consistency test_hash_ctx_zeroization test_hash_interrupt_context_1 test_hash_interrupt_context_2 test_hmac (when HMAC is supported) benchmark_hash (when CONFIG_CRYPTO_LIB_BENCHMARK=y) The initial use cases for this will be sha224_kunit, sha256_kunit, sha384_kunit, sha512_kunit, and poly1305_kunit. Add a Python script gen-hash-testvecs.py which generates the test vectors required by test_hash_test_vectors, test_hash_all_lens_up_to_4096, and test_hmac. Acked-by: Ard Biesheuvel Link: https://lore.kernel.org/r/20250709200112.258500-2-ebiggers@kernel.org Signed-off-by: Eric Biggers --- lib/crypto/tests/hash-test-template.h | 683 ++++++++++++++++++++++++++++++++++ scripts/crypto/gen-hash-testvecs.py | 102 +++++ 2 files changed, 785 insertions(+) create mode 100644 lib/crypto/tests/hash-test-template.h create mode 100755 scripts/crypto/gen-hash-testvecs.py (limited to 'scripts') diff --git a/lib/crypto/tests/hash-test-template.h b/lib/crypto/tests/hash-test-template.h new file mode 100644 index 000000000000..ffee1741a1b3 --- /dev/null +++ b/lib/crypto/tests/hash-test-template.h @@ -0,0 +1,683 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Test cases for hash functions, including a benchmark. This is included by + * KUnit test suites that want to use it. See sha512_kunit.c for an example. + * + * Copyright 2025 Google LLC + */ +#include +#include +#include +#include +#include + +/* test_buf is a guarded buffer, i.e. &test_buf[TEST_BUF_LEN] is not mapped. */ +#define TEST_BUF_LEN 16384 +static u8 *test_buf; + +static u8 *orig_test_buf; + +static u64 random_seed; + +/* + * This is a simple linear congruential generator. It is used only for testing, + * which does not require cryptographically secure random numbers. A hard-coded + * algorithm is used instead of so that it matches the + * algorithm used by the test vector generation script. This allows the input + * data in random test vectors to be concisely stored as just the seed. + */ +static u32 rand32(void) +{ + random_seed = (random_seed * 25214903917 + 11) & ((1ULL << 48) - 1); + return random_seed >> 16; +} + +static void rand_bytes(u8 *out, size_t len) +{ + for (size_t i = 0; i < len; i++) + out[i] = rand32(); +} + +static void rand_bytes_seeded_from_len(u8 *out, size_t len) +{ + random_seed = len; + rand_bytes(out, len); +} + +static bool rand_bool(void) +{ + return rand32() % 2; +} + +/* Generate a random length, preferring small lengths. */ +static size_t rand_length(size_t max_len) +{ + size_t len; + + switch (rand32() % 3) { + case 0: + len = rand32() % 128; + break; + case 1: + len = rand32() % 3072; + break; + default: + len = rand32(); + break; + } + return len % (max_len + 1); +} + +static size_t rand_offset(size_t max_offset) +{ + return min(rand32() % 128, max_offset); +} + +static int hash_suite_init(struct kunit_suite *suite) +{ + /* + * Allocate the test buffer using vmalloc() with a page-aligned length + * so that it is immediately followed by a guard page. This allows + * buffer overreads to be detected, even in assembly code. + */ + size_t alloc_len = round_up(TEST_BUF_LEN, PAGE_SIZE); + + orig_test_buf = vmalloc(alloc_len); + if (!orig_test_buf) + return -ENOMEM; + + test_buf = orig_test_buf + alloc_len - TEST_BUF_LEN; + return 0; +} + +static void hash_suite_exit(struct kunit_suite *suite) +{ + vfree(orig_test_buf); + orig_test_buf = NULL; + test_buf = NULL; +} + +/* + * Test the hash function against a list of test vectors. + * + * Note that it's only necessary to run each test vector in one way (e.g., + * one-shot instead of incremental), since consistency between different ways of + * using the APIs is verified by other test cases. + */ +static void test_hash_test_vectors(struct kunit *test) +{ + for (size_t i = 0; i < ARRAY_SIZE(hash_testvecs); i++) { + size_t data_len = hash_testvecs[i].data_len; + u8 actual_hash[HASH_SIZE]; + + KUNIT_ASSERT_LE(test, data_len, TEST_BUF_LEN); + rand_bytes_seeded_from_len(test_buf, data_len); + + HASH(test_buf, data_len, actual_hash); + KUNIT_ASSERT_MEMEQ_MSG( + test, actual_hash, hash_testvecs[i].digest, HASH_SIZE, + "Wrong result with test vector %zu; data_len=%zu", i, + data_len); + } +} + +/* + * Test that the hash function produces correct results for *every* length up to + * 4096 bytes. To do this, generate seeded random data, then calculate a hash + * value for each length 0..4096, then hash the hash values. Verify just the + * final hash value, which should match only when all hash values were correct. + */ +static void test_hash_all_lens_up_to_4096(struct kunit *test) +{ + struct HASH_CTX ctx; + u8 hash[HASH_SIZE]; + + static_assert(TEST_BUF_LEN >= 4096); + rand_bytes_seeded_from_len(test_buf, 4096); + HASH_INIT(&ctx); + for (size_t len = 0; len <= 4096; len++) { + HASH(test_buf, len, hash); + HASH_UPDATE(&ctx, hash, HASH_SIZE); + } + HASH_FINAL(&ctx, hash); + KUNIT_ASSERT_MEMEQ(test, hash, hash_testvec_consolidated, HASH_SIZE); +} + +/* + * Test that the hash function produces the same result with a one-shot + * computation as it does with an incremental computation. + */ +static void test_hash_incremental_updates(struct kunit *test) +{ + for (int i = 0; i < 1000; i++) { + size_t total_len, offset; + struct HASH_CTX ctx; + u8 hash1[HASH_SIZE]; + u8 hash2[HASH_SIZE]; + size_t num_parts = 0; + size_t remaining_len, cur_offset; + + total_len = rand_length(TEST_BUF_LEN); + offset = rand_offset(TEST_BUF_LEN - total_len); + rand_bytes(&test_buf[offset], total_len); + + /* Compute the hash value in one shot. */ + HASH(&test_buf[offset], total_len, hash1); + + /* + * Compute the hash value incrementally, using a randomly + * selected sequence of update lengths that sum to total_len. + */ + HASH_INIT(&ctx); + remaining_len = total_len; + cur_offset = offset; + while (rand_bool()) { + size_t part_len = rand_length(remaining_len); + + HASH_UPDATE(&ctx, &test_buf[cur_offset], part_len); + num_parts++; + cur_offset += part_len; + remaining_len -= part_len; + } + if (remaining_len != 0 || rand_bool()) { + HASH_UPDATE(&ctx, &test_buf[cur_offset], remaining_len); + num_parts++; + } + HASH_FINAL(&ctx, hash2); + + /* Verify that the two hash values are the same. */ + KUNIT_ASSERT_MEMEQ_MSG( + test, hash1, hash2, HASH_SIZE, + "Incremental test failed with total_len=%zu num_parts=%zu offset=%zu", + total_len, num_parts, offset); + } +} + +/* + * Test that the hash function does not overrun any buffers. Uses a guard page + * to catch buffer overruns even if they occur in assembly code. + */ +static void test_hash_buffer_overruns(struct kunit *test) +{ + const size_t max_tested_len = TEST_BUF_LEN - sizeof(struct HASH_CTX); + void *const buf_end = &test_buf[TEST_BUF_LEN]; + struct HASH_CTX *guarded_ctx = buf_end - sizeof(*guarded_ctx); + + rand_bytes(test_buf, TEST_BUF_LEN); + + for (int i = 0; i < 100; i++) { + size_t len = rand_length(max_tested_len); + struct HASH_CTX ctx; + u8 hash[HASH_SIZE]; + + /* Check for overruns of the data buffer. */ + HASH(buf_end - len, len, hash); + HASH_INIT(&ctx); + HASH_UPDATE(&ctx, buf_end - len, len); + HASH_FINAL(&ctx, hash); + + /* Check for overruns of the hash value buffer. */ + HASH(test_buf, len, buf_end - HASH_SIZE); + HASH_INIT(&ctx); + HASH_UPDATE(&ctx, test_buf, len); + HASH_FINAL(&ctx, buf_end - HASH_SIZE); + + /* Check for overuns of the hash context. */ + HASH_INIT(guarded_ctx); + HASH_UPDATE(guarded_ctx, test_buf, len); + HASH_FINAL(guarded_ctx, hash); + } +} + +/* + * Test that the caller is permitted to alias the output digest and source data + * buffer, and also modify the source data buffer after it has been used. + */ +static void test_hash_overlaps(struct kunit *test) +{ + const size_t max_tested_len = TEST_BUF_LEN - HASH_SIZE; + struct HASH_CTX ctx; + u8 hash[HASH_SIZE]; + + rand_bytes(test_buf, TEST_BUF_LEN); + + for (int i = 0; i < 100; i++) { + size_t len = rand_length(max_tested_len); + size_t offset = HASH_SIZE + rand_offset(max_tested_len - len); + bool left_end = rand_bool(); + u8 *ovl_hash = left_end ? &test_buf[offset] : + &test_buf[offset + len - HASH_SIZE]; + + HASH(&test_buf[offset], len, hash); + HASH(&test_buf[offset], len, ovl_hash); + KUNIT_ASSERT_MEMEQ_MSG( + test, hash, ovl_hash, HASH_SIZE, + "Overlap test 1 failed with len=%zu offset=%zu left_end=%d", + len, offset, left_end); + + /* Repeat the above test, but this time use init+update+final */ + HASH(&test_buf[offset], len, hash); + HASH_INIT(&ctx); + HASH_UPDATE(&ctx, &test_buf[offset], len); + HASH_FINAL(&ctx, ovl_hash); + KUNIT_ASSERT_MEMEQ_MSG( + test, hash, ovl_hash, HASH_SIZE, + "Overlap test 2 failed with len=%zu offset=%zu left_end=%d", + len, offset, left_end); + + /* Test modifying the source data after it was used. */ + HASH(&test_buf[offset], len, hash); + HASH_INIT(&ctx); + HASH_UPDATE(&ctx, &test_buf[offset], len); + rand_bytes(&test_buf[offset], len); + HASH_FINAL(&ctx, ovl_hash); + KUNIT_ASSERT_MEMEQ_MSG( + test, hash, ovl_hash, HASH_SIZE, + "Overlap test 3 failed with len=%zu offset=%zu left_end=%d", + len, offset, left_end); + } +} + +/* + * Test that if the same data is hashed at different alignments in memory, the + * results are the same. + */ +static void test_hash_alignment_consistency(struct kunit *test) +{ + u8 hash1[128 + HASH_SIZE]; + u8 hash2[128 + HASH_SIZE]; + + for (int i = 0; i < 100; i++) { + size_t len = rand_length(TEST_BUF_LEN); + size_t data_offs1 = rand_offset(TEST_BUF_LEN - len); + size_t data_offs2 = rand_offset(TEST_BUF_LEN - len); + size_t hash_offs1 = rand_offset(128); + size_t hash_offs2 = rand_offset(128); + + rand_bytes(&test_buf[data_offs1], len); + HASH(&test_buf[data_offs1], len, &hash1[hash_offs1]); + memmove(&test_buf[data_offs2], &test_buf[data_offs1], len); + HASH(&test_buf[data_offs2], len, &hash2[hash_offs2]); + KUNIT_ASSERT_MEMEQ_MSG( + test, &hash1[hash_offs1], &hash2[hash_offs2], HASH_SIZE, + "Alignment consistency test failed with len=%zu data_offs=(%zu,%zu) hash_offs=(%zu,%zu)", + len, data_offs1, data_offs2, hash_offs1, hash_offs2); + } +} + +/* Test that HASH_FINAL zeroizes the context. */ +static void test_hash_ctx_zeroization(struct kunit *test) +{ + static const u8 zeroes[sizeof(struct HASH_CTX)]; + struct HASH_CTX ctx; + + rand_bytes(test_buf, 128); + HASH_INIT(&ctx); + HASH_UPDATE(&ctx, test_buf, 128); + HASH_FINAL(&ctx, test_buf); + KUNIT_ASSERT_MEMEQ_MSG(test, &ctx, zeroes, sizeof(ctx), + "Hash context was not zeroized by finalization"); +} + +#define IRQ_TEST_HRTIMER_INTERVAL us_to_ktime(5) + +struct hash_irq_test_state { + bool (*func)(void *test_specific_state); + void *test_specific_state; + bool task_func_reported_failure; + bool hardirq_func_reported_failure; + bool softirq_func_reported_failure; + unsigned long hardirq_func_calls; + unsigned long softirq_func_calls; + struct hrtimer timer; + struct work_struct bh_work; +}; + +static enum hrtimer_restart hash_irq_test_timer_func(struct hrtimer *timer) +{ + struct hash_irq_test_state *state = + container_of(timer, typeof(*state), timer); + + WARN_ON_ONCE(!in_hardirq()); + state->hardirq_func_calls++; + + if (!state->func(state->test_specific_state)) + state->hardirq_func_reported_failure = true; + + hrtimer_forward_now(&state->timer, IRQ_TEST_HRTIMER_INTERVAL); + queue_work(system_bh_wq, &state->bh_work); + return HRTIMER_RESTART; +} + +static void hash_irq_test_bh_work_func(struct work_struct *work) +{ + struct hash_irq_test_state *state = + container_of(work, typeof(*state), bh_work); + + WARN_ON_ONCE(!in_serving_softirq()); + state->softirq_func_calls++; + + if (!state->func(state->test_specific_state)) + state->softirq_func_reported_failure = true; +} + +/* + * Helper function which repeatedly runs the given @func in task, softirq, and + * hardirq context concurrently, and reports a failure to KUnit if any + * invocation of @func in any context returns false. @func is passed + * @test_specific_state as its argument. At most 3 invocations of @func will + * run concurrently: one in each of task, softirq, and hardirq context. + * + * The main purpose of this interrupt context testing is to validate fallback + * code paths that run in contexts where the normal code path cannot be used, + * typically due to the FPU or vector registers already being in-use in kernel + * mode. These code paths aren't covered when the test code is executed only by + * the KUnit test runner thread in task context. The reason for the concurrency + * is because merely using hardirq context is not sufficient to reach a fallback + * code path on some architectures; the hardirq actually has to occur while the + * FPU or vector unit was already in-use in kernel mode. + * + * Another purpose of this testing is to detect issues with the architecture's + * irq_fpu_usable() and kernel_fpu_begin/end() or equivalent functions, + * especially in softirq context when the softirq may have interrupted a task + * already using kernel-mode FPU or vector (if the arch didn't prevent that). + * Crypto functions are often executed in softirqs, so this is important. + */ +static void run_irq_test(struct kunit *test, bool (*func)(void *), + int max_iterations, void *test_specific_state) +{ + struct hash_irq_test_state state = { + .func = func, + .test_specific_state = test_specific_state, + }; + unsigned long end_jiffies; + + /* + * Set up a hrtimer (the way we access hardirq context) and a work + * struct for the BH workqueue (the way we access softirq context). + */ + hrtimer_setup_on_stack(&state.timer, hash_irq_test_timer_func, + CLOCK_MONOTONIC, HRTIMER_MODE_REL_HARD); + INIT_WORK(&state.bh_work, hash_irq_test_bh_work_func); + + /* Run for up to max_iterations or 1 second, whichever comes first. */ + end_jiffies = jiffies + HZ; + hrtimer_start(&state.timer, IRQ_TEST_HRTIMER_INTERVAL, + HRTIMER_MODE_REL_HARD); + for (int i = 0; i < max_iterations && !time_after(jiffies, end_jiffies); + i++) { + if (!func(test_specific_state)) + state.task_func_reported_failure = true; + } + + /* Cancel the timer and work. */ + hrtimer_cancel(&state.timer); + flush_work(&state.bh_work); + + /* Sanity check: the timer and BH functions should have been run. */ + KUNIT_EXPECT_GT_MSG(test, state.hardirq_func_calls, 0, + "Timer function was not called"); + KUNIT_EXPECT_GT_MSG(test, state.softirq_func_calls, 0, + "BH work function was not called"); + + /* Check for incorrect hash values reported from any context. */ + KUNIT_EXPECT_FALSE_MSG( + test, state.task_func_reported_failure, + "Incorrect hash values reported from task context"); + KUNIT_EXPECT_FALSE_MSG( + test, state.hardirq_func_reported_failure, + "Incorrect hash values reported from hardirq context"); + KUNIT_EXPECT_FALSE_MSG( + test, state.softirq_func_reported_failure, + "Incorrect hash values reported from softirq context"); +} + +#define IRQ_TEST_DATA_LEN 256 +#define IRQ_TEST_NUM_BUFFERS 3 /* matches max concurrency level */ + +struct hash_irq_test1_state { + u8 expected_hashes[IRQ_TEST_NUM_BUFFERS][HASH_SIZE]; + atomic_t seqno; +}; + +/* + * Compute the hash of one of the test messages and verify that it matches the + * expected hash from @state->expected_hashes. To increase the chance of + * detecting problems, cycle through multiple messages. + */ +static bool hash_irq_test1_func(void *state_) +{ + struct hash_irq_test1_state *state = state_; + u32 i = (u32)atomic_inc_return(&state->seqno) % IRQ_TEST_NUM_BUFFERS; + u8 actual_hash[HASH_SIZE]; + + HASH(&test_buf[i * IRQ_TEST_DATA_LEN], IRQ_TEST_DATA_LEN, actual_hash); + return memcmp(actual_hash, state->expected_hashes[i], HASH_SIZE) == 0; +} + +/* + * Test that if hashes are computed in task, softirq, and hardirq context + * concurrently, then all results are as expected. + */ +static void test_hash_interrupt_context_1(struct kunit *test) +{ + struct hash_irq_test1_state state = {}; + + /* Prepare some test messages and compute the expected hash of each. */ + rand_bytes(test_buf, IRQ_TEST_NUM_BUFFERS * IRQ_TEST_DATA_LEN); + for (int i = 0; i < IRQ_TEST_NUM_BUFFERS; i++) + HASH(&test_buf[i * IRQ_TEST_DATA_LEN], IRQ_TEST_DATA_LEN, + state.expected_hashes[i]); + + run_irq_test(test, hash_irq_test1_func, 100000, &state); +} + +struct hash_irq_test2_hash_ctx { + struct HASH_CTX hash_ctx; + atomic_t in_use; + int offset; + int step; +}; + +struct hash_irq_test2_state { + struct hash_irq_test2_hash_ctx ctxs[IRQ_TEST_NUM_BUFFERS]; + u8 expected_hash[HASH_SIZE]; + u16 update_lens[32]; + int num_steps; +}; + +static bool hash_irq_test2_func(void *state_) +{ + struct hash_irq_test2_state *state = state_; + struct hash_irq_test2_hash_ctx *ctx; + bool ret = true; + + for (ctx = &state->ctxs[0]; ctx < &state->ctxs[ARRAY_SIZE(state->ctxs)]; + ctx++) { + if (atomic_cmpxchg(&ctx->in_use, 0, 1) == 0) + break; + } + if (WARN_ON_ONCE(ctx == &state->ctxs[ARRAY_SIZE(state->ctxs)])) { + /* + * This should never happen, as the number of contexts is equal + * to the maximum concurrency level of run_irq_test(). + */ + return false; + } + + if (ctx->step == 0) { + /* Init step */ + HASH_INIT(&ctx->hash_ctx); + ctx->offset = 0; + ctx->step++; + } else if (ctx->step < state->num_steps - 1) { + /* Update step */ + HASH_UPDATE(&ctx->hash_ctx, &test_buf[ctx->offset], + state->update_lens[ctx->step - 1]); + ctx->offset += state->update_lens[ctx->step - 1]; + ctx->step++; + } else { + /* Final step */ + u8 actual_hash[HASH_SIZE]; + + if (WARN_ON_ONCE(ctx->offset != TEST_BUF_LEN)) + ret = false; + HASH_FINAL(&ctx->hash_ctx, actual_hash); + if (memcmp(actual_hash, state->expected_hash, HASH_SIZE) != 0) + ret = false; + ctx->step = 0; + } + atomic_set_release(&ctx->in_use, 0); + return ret; +} + +/* + * Test that if hashes are computed in task, softirq, and hardirq context + * concurrently, *including doing different parts of the same incremental + * computation in different contexts*, then all results are as expected. + * Besides detecting bugs similar to those that test_hash_interrupt_context_1 + * can detect, this test case can also detect bugs where hash function + * implementations don't correctly handle these mixed incremental computations. + */ +static void test_hash_interrupt_context_2(struct kunit *test) +{ + struct hash_irq_test2_state *state; + int remaining = TEST_BUF_LEN; + + state = kunit_kzalloc(test, sizeof(*state), GFP_KERNEL); + KUNIT_ASSERT_NOT_NULL(test, state); + + rand_bytes(test_buf, TEST_BUF_LEN); + HASH(test_buf, TEST_BUF_LEN, state->expected_hash); + + /* + * Generate a list of update lengths to use. Ensure that it contains + * multiple entries but is limited to a maximum length. + */ + static_assert(TEST_BUF_LEN / 4096 > 1); + for (state->num_steps = 0; + state->num_steps < ARRAY_SIZE(state->update_lens) - 1 && remaining; + state->num_steps++) { + state->update_lens[state->num_steps] = + rand_length(min(remaining, 4096)); + remaining -= state->update_lens[state->num_steps]; + } + if (remaining) + state->update_lens[state->num_steps++] = remaining; + state->num_steps += 2; /* for init and final */ + + run_irq_test(test, hash_irq_test2_func, 250000, state); +} + +#define UNKEYED_HASH_KUNIT_CASES \ + KUNIT_CASE(test_hash_test_vectors), \ + KUNIT_CASE(test_hash_all_lens_up_to_4096), \ + KUNIT_CASE(test_hash_incremental_updates), \ + KUNIT_CASE(test_hash_buffer_overruns), \ + KUNIT_CASE(test_hash_overlaps), \ + KUNIT_CASE(test_hash_alignment_consistency), \ + KUNIT_CASE(test_hash_ctx_zeroization), \ + KUNIT_CASE(test_hash_interrupt_context_1), \ + KUNIT_CASE(test_hash_interrupt_context_2) +/* benchmark_hash is omitted so that the suites can put it last. */ + +#ifdef HMAC +/* + * Test the corresponding HMAC variant. + * + * This test case is fairly short, since HMAC is just a simple C wrapper around + * the underlying unkeyed hash function, which is already well-tested by the + * other test cases. It's not useful to test things like data alignment or + * interrupt context again for HMAC, nor to have a long list of test vectors. + * + * Thus, just do a single consolidated test, which covers all data lengths up to + * 4096 bytes and all key lengths up to 292 bytes. For each data length, select + * a key length, generate the inputs from a seed, and compute the HMAC value. + * Concatenate all these HMAC values together, and compute the HMAC of that. + * Verify that value. If this fails, then the HMAC implementation is wrong. + * This won't show which specific input failed, but that should be fine. Any + * failure would likely be non-input-specific or also show in the unkeyed tests. + */ +static void test_hmac(struct kunit *test) +{ + static const u8 zeroes[sizeof(struct HMAC_CTX)]; + u8 *raw_key; + struct HMAC_KEY key; + struct HMAC_CTX ctx; + u8 mac[HASH_SIZE]; + u8 mac2[HASH_SIZE]; + + static_assert(TEST_BUF_LEN >= 4096 + 293); + rand_bytes_seeded_from_len(test_buf, 4096); + raw_key = &test_buf[4096]; + + rand_bytes_seeded_from_len(raw_key, 32); + HMAC_PREPAREKEY(&key, raw_key, 32); + HMAC_INIT(&ctx, &key); + for (size_t data_len = 0; data_len <= 4096; data_len++) { + /* + * Cycle through key lengths as well. Somewhat arbitrarily go + * up to 293, which is somewhat larger than the largest hash + * block size (which is the size at which the key starts being + * hashed down to one block); going higher would not be useful. + * To reduce correlation with data_len, use a prime number here. + */ + size_t key_len = data_len % 293; + + HMAC_UPDATE(&ctx, test_buf, data_len); + + rand_bytes_seeded_from_len(raw_key, key_len); + HMAC_USINGRAWKEY(raw_key, key_len, test_buf, data_len, mac); + HMAC_UPDATE(&ctx, mac, HASH_SIZE); + + /* Verify that HMAC() is consistent with HMAC_USINGRAWKEY(). */ + HMAC_PREPAREKEY(&key, raw_key, key_len); + HMAC(&key, test_buf, data_len, mac2); + KUNIT_ASSERT_MEMEQ_MSG( + test, mac, mac2, HASH_SIZE, + "HMAC gave different results with raw and prepared keys"); + } + HMAC_FINAL(&ctx, mac); + KUNIT_EXPECT_MEMEQ_MSG(test, mac, hmac_testvec_consolidated, HASH_SIZE, + "HMAC gave wrong result"); + KUNIT_EXPECT_MEMEQ_MSG(test, &ctx, zeroes, sizeof(ctx), + "HMAC context was not zeroized by finalization"); +} +#define HASH_KUNIT_CASES UNKEYED_HASH_KUNIT_CASES, KUNIT_CASE(test_hmac) +#else +#define HASH_KUNIT_CASES UNKEYED_HASH_KUNIT_CASES +#endif + +/* Benchmark the hash function on various data lengths. */ +static void benchmark_hash(struct kunit *test) +{ + static const size_t lens_to_test[] = { + 1, 16, 64, 127, 128, 200, 256, + 511, 512, 1024, 3173, 4096, 16384, + }; + u8 hash[HASH_SIZE]; + + if (!IS_ENABLED(CONFIG_CRYPTO_LIB_BENCHMARK)) + kunit_skip(test, "not enabled"); + + /* Warm-up */ + for (size_t i = 0; i < 10000000; i += TEST_BUF_LEN) + HASH(test_buf, TEST_BUF_LEN, hash); + + for (size_t i = 0; i < ARRAY_SIZE(lens_to_test); i++) { + size_t len = lens_to_test[i]; + /* The '+ 128' tries to account for per-message overhead. */ + size_t num_iters = 10000000 / (len + 128); + u64 t; + + KUNIT_ASSERT_LE(test, len, TEST_BUF_LEN); + preempt_disable(); + t = ktime_get_ns(); + for (size_t j = 0; j < num_iters; j++) + HASH(test_buf, len, hash); + t = ktime_get_ns() - t; + preempt_enable(); + kunit_info(test, "len=%zu: %llu MB/s", len, + div64_u64((u64)len * num_iters * 1000, t ?: 1)); + } +} diff --git a/scripts/crypto/gen-hash-testvecs.py b/scripts/crypto/gen-hash-testvecs.py new file mode 100755 index 000000000000..55f1010339a6 --- /dev/null +++ b/scripts/crypto/gen-hash-testvecs.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +# SPDX-License-Identifier: GPL-2.0-or-later +# +# Script that generates test vectors for the given cryptographic hash function. +# +# Copyright 2025 Google LLC + +import hashlib +import hmac +import sys + +DATA_LENS = [0, 1, 2, 3, 16, 32, 48, 49, 63, 64, 65, 127, 128, 129, 256, 511, + 513, 1000, 3333, 4096, 4128, 4160, 4224, 16384] + +# Generate the given number of random bytes, using the length itself as the seed +# for a simple linear congruential generator (LCG). The C test code uses the +# same LCG with the same seeding strategy to reconstruct the data, ensuring +# reproducibility without explicitly storing the data in the test vectors. +def rand_bytes(length): + seed = length + out = [] + for _ in range(length): + seed = (seed * 25214903917 + 11) % 2**48 + out.append((seed >> 16) % 256) + return bytes(out) + +def hash_init(alg): + return hashlib.new(alg) + +def hash_update(ctx, data): + ctx.update(data) + +def hash_final(ctx): + return ctx.digest() + +def compute_hash(alg, data): + ctx = hash_init(alg) + hash_update(ctx, data) + return hash_final(ctx) + +def print_bytes(prefix, value, bytes_per_line): + for i in range(0, len(value), bytes_per_line): + line = prefix + ''.join(f'0x{b:02x}, ' for b in value[i:i+bytes_per_line]) + print(f'{line.rstrip()}') + +def print_static_u8_array_definition(name, value): + print('') + print(f'static const u8 {name} = {{') + print_bytes('\t', value, 8) + print('};') + +def print_c_struct_u8_array_field(name, value): + print(f'\t\t.{name} = {{') + print_bytes('\t\t\t', value, 8) + print('\t\t},') + +def gen_unkeyed_testvecs(alg): + print('') + print('static const struct {') + print('\tsize_t data_len;') + print(f'\tu8 digest[{alg.upper()}_DIGEST_SIZE];') + print('} hash_testvecs[] = {') + for data_len in DATA_LENS: + data = rand_bytes(data_len) + print('\t{') + print(f'\t\t.data_len = {data_len},') + print_c_struct_u8_array_field('digest', compute_hash(alg, data)) + print('\t},') + print('};') + + data = rand_bytes(4096) + ctx = hash_init(alg) + for data_len in range(len(data) + 1): + hash_update(ctx, compute_hash(alg, data[:data_len])) + print_static_u8_array_definition( + f'hash_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]', + hash_final(ctx)) + +def gen_hmac_testvecs(alg): + ctx = hmac.new(rand_bytes(32), digestmod=alg) + data = rand_bytes(4096) + for data_len in range(len(data) + 1): + ctx.update(data[:data_len]) + key_len = data_len % 293 + key = rand_bytes(key_len) + mac = hmac.digest(key, data[:data_len], alg) + ctx.update(mac) + print_static_u8_array_definition( + f'hmac_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]', + ctx.digest()) + +if len(sys.argv) != 2: + sys.stderr.write('Usage: gen-hash-testvecs.py ALGORITHM\n') + sys.stderr.write('ALGORITHM may be any supported by Python hashlib.\n') + sys.stderr.write('Example: gen-hash-testvecs.py sha512\n') + sys.exit(1) + +alg = sys.argv[1] +print('/* SPDX-License-Identifier: GPL-2.0-or-later */') +print(f'/* This file was generated by: {sys.argv[0]} {" ".join(sys.argv[1:])} */') +gen_unkeyed_testvecs(alg) +gen_hmac_testvecs(alg) -- cgit v1.2.3 From 6dd4d9f7919e73bc7ad247eca82e8be1c123af0a Mon Sep 17 00:00:00 2001 From: Eric Biggers Date: Wed, 9 Jul 2025 13:01:12 -0700 Subject: lib/crypto: tests: Add KUnit tests for Poly1305 Add a KUnit test suite for the Poly1305 functions. Most of its test cases are instantiated from hash-test-template.h, which is also used by the SHA-2 tests. A couple additional test cases are also included to test edge cases specific to Poly1305. Acked-by: Ard Biesheuvel Link: https://lore.kernel.org/r/20250709200112.258500-5-ebiggers@kernel.org Signed-off-by: Eric Biggers --- lib/crypto/tests/Kconfig | 9 ++ lib/crypto/tests/Makefile | 1 + lib/crypto/tests/poly1305-testvecs.h | 186 +++++++++++++++++++++++++++++++++++ lib/crypto/tests/poly1305_kunit.c | 165 +++++++++++++++++++++++++++++++ scripts/crypto/gen-hash-testvecs.py | 49 ++++++++- 5 files changed, 408 insertions(+), 2 deletions(-) create mode 100644 lib/crypto/tests/poly1305-testvecs.h create mode 100644 lib/crypto/tests/poly1305_kunit.c (limited to 'scripts') diff --git a/lib/crypto/tests/Kconfig b/lib/crypto/tests/Kconfig index 5005aeac736d..65f462754419 100644 --- a/lib/crypto/tests/Kconfig +++ b/lib/crypto/tests/Kconfig @@ -1,5 +1,14 @@ # SPDX-License-Identifier: GPL-2.0-or-later +config CRYPTO_LIB_POLY1305_KUNIT_TEST + tristate "KUnit tests for Poly1305" if !KUNIT_ALL_TESTS + depends on KUNIT + default KUNIT_ALL_TESTS || CRYPTO_SELFTESTS + select CRYPTO_LIB_BENCHMARK_VISIBLE + select CRYPTO_LIB_POLY1305 + help + KUnit tests for the Poly1305 library functions. + # Option is named *_SHA256_KUNIT_TEST, though both SHA-224 and SHA-256 tests are # included, for consistency with the naming used elsewhere (e.g. CRYPTO_SHA256). config CRYPTO_LIB_SHA256_KUNIT_TEST diff --git a/lib/crypto/tests/Makefile b/lib/crypto/tests/Makefile index a963429061dc..d33f6d85ecaa 100644 --- a/lib/crypto/tests/Makefile +++ b/lib/crypto/tests/Makefile @@ -1,4 +1,5 @@ # SPDX-License-Identifier: GPL-2.0-or-later +obj-$(CONFIG_CRYPTO_LIB_POLY1305_KUNIT_TEST) += poly1305_kunit.o obj-$(CONFIG_CRYPTO_LIB_SHA256_KUNIT_TEST) += sha224_kunit.o sha256_kunit.o obj-$(CONFIG_CRYPTO_LIB_SHA512_KUNIT_TEST) += sha384_kunit.o sha512_kunit.o diff --git a/lib/crypto/tests/poly1305-testvecs.h b/lib/crypto/tests/poly1305-testvecs.h new file mode 100644 index 000000000000..ecf8662225c8 --- /dev/null +++ b/lib/crypto/tests/poly1305-testvecs.h @@ -0,0 +1,186 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* This file was generated by: ./scripts/crypto/gen-hash-testvecs.py poly1305 */ + +static const struct { + size_t data_len; + u8 digest[POLY1305_DIGEST_SIZE]; +} hash_testvecs[] = { + { + .data_len = 0, + .digest = { + 0xe8, 0x2d, 0x67, 0x2c, 0x01, 0x48, 0xf9, 0xb7, + 0x87, 0x85, 0x3f, 0xcf, 0x18, 0x66, 0x8c, 0xd3, + }, + }, + { + .data_len = 1, + .digest = { + 0xb8, 0xad, 0xca, 0x6b, 0x32, 0xba, 0x34, 0x42, + 0x54, 0x10, 0x28, 0xf5, 0x0f, 0x7e, 0x8e, 0xe3, + }, + }, + { + .data_len = 2, + .digest = { + 0xb8, 0xf7, 0xf4, 0xc2, 0x85, 0x33, 0x80, 0x63, + 0xd1, 0x45, 0xda, 0xf8, 0x7c, 0x79, 0x42, 0xd1, + }, + }, + { + .data_len = 3, + .digest = { + 0xf3, 0x73, 0x7b, 0x60, 0x24, 0xcc, 0x5d, 0x3e, + 0xd1, 0x95, 0x86, 0xce, 0x89, 0x0a, 0x33, 0xba, + }, + }, + { + .data_len = 16, + .digest = { + 0x0a, 0x1a, 0x2d, 0x39, 0xea, 0x49, 0x8f, 0xb7, + 0x90, 0xb6, 0x74, 0x3b, 0x41, 0x3b, 0x96, 0x11, + }, + }, + { + .data_len = 32, + .digest = { + 0x99, 0x05, 0xe3, 0xa7, 0x9e, 0x2a, 0xd2, 0x42, + 0xb9, 0x45, 0x0c, 0x08, 0xe7, 0x10, 0xe4, 0xe1, + }, + }, + { + .data_len = 48, + .digest = { + 0xe1, 0xb2, 0x15, 0xee, 0xa2, 0xf3, 0x04, 0xac, + 0xdd, 0x27, 0x57, 0x95, 0x2f, 0x45, 0xa8, 0xd3, + }, + }, + { + .data_len = 49, + .digest = { + 0x1c, 0xf3, 0xab, 0x39, 0xc0, 0x69, 0x49, 0x69, + 0x89, 0x6f, 0x1f, 0x03, 0x16, 0xe7, 0xc0, 0xf0, + }, + }, + { + .data_len = 63, + .digest = { + 0x30, 0xb0, 0x32, 0x87, 0x51, 0x55, 0x9c, 0x39, + 0x38, 0x42, 0x06, 0xe9, 0x2a, 0x3e, 0x2c, 0x92, + }, + }, + { + .data_len = 64, + .digest = { + 0x2c, 0x04, 0x16, 0x36, 0x55, 0x25, 0x2d, 0xc6, + 0x3d, 0x70, 0x5b, 0x88, 0x46, 0xb6, 0x71, 0x77, + }, + }, + { + .data_len = 65, + .digest = { + 0x03, 0x87, 0xdd, 0xbe, 0xe8, 0x30, 0xf2, 0x15, + 0x40, 0x44, 0x29, 0x7b, 0xb1, 0xe9, 0x9d, 0xe7, + }, + }, + { + .data_len = 127, + .digest = { + 0x29, 0x83, 0x4f, 0xcb, 0x5a, 0x93, 0x25, 0xad, + 0x05, 0xa4, 0xb3, 0x24, 0x77, 0x62, 0x2d, 0x3d, + }, + }, + { + .data_len = 128, + .digest = { + 0x20, 0x0e, 0x2c, 0x05, 0xe2, 0x0b, 0x85, 0xa0, + 0x24, 0x73, 0x7f, 0x65, 0x70, 0x6c, 0x3e, 0xb0, + }, + }, + { + .data_len = 129, + .digest = { + 0xef, 0x2f, 0x98, 0x42, 0xc2, 0x90, 0x55, 0xea, + 0xba, 0x28, 0x76, 0xfd, 0x9e, 0x3e, 0x4d, 0x53, + }, + }, + { + .data_len = 256, + .digest = { + 0x9e, 0x75, 0x4b, 0xc7, 0x69, 0x68, 0x51, 0x90, + 0xdc, 0x29, 0xc8, 0xfa, 0x86, 0xf1, 0xc9, 0xb3, + }, + }, + { + .data_len = 511, + .digest = { + 0x9d, 0x13, 0xf5, 0x54, 0xe6, 0xe3, 0x45, 0x38, + 0x8b, 0x6d, 0x5c, 0xc4, 0x50, 0xeb, 0x90, 0xcb, + }, + }, + { + .data_len = 513, + .digest = { + 0xaa, 0xb2, 0x3e, 0x3c, 0x2a, 0xfc, 0x62, 0x0e, + 0xd4, 0xe6, 0xe5, 0x5c, 0x6b, 0x9f, 0x3d, 0xc7, + }, + }, + { + .data_len = 1000, + .digest = { + 0xd6, 0x8c, 0xea, 0x8a, 0x0f, 0x68, 0xa9, 0xa8, + 0x67, 0x86, 0xf9, 0xc1, 0x4c, 0x26, 0x10, 0x6d, + }, + }, + { + .data_len = 3333, + .digest = { + 0xdc, 0xc1, 0x54, 0xe7, 0x38, 0xc6, 0xdb, 0x24, + 0xa7, 0x0b, 0xff, 0xd3, 0x1b, 0x93, 0x01, 0xa6, + }, + }, + { + .data_len = 4096, + .digest = { + 0x8f, 0x88, 0x3e, 0x9c, 0x7b, 0x2e, 0x82, 0x5a, + 0x1d, 0x31, 0x82, 0xcc, 0x69, 0xb4, 0x16, 0x26, + }, + }, + { + .data_len = 4128, + .digest = { + 0x23, 0x45, 0x94, 0xa8, 0x11, 0x54, 0x9d, 0xf2, + 0xa1, 0x9a, 0xca, 0xf9, 0x3e, 0x65, 0x52, 0xfd, + }, + }, + { + .data_len = 4160, + .digest = { + 0x7b, 0xfc, 0xa9, 0x1e, 0x03, 0xad, 0xef, 0x03, + 0xe2, 0x20, 0x92, 0xc7, 0x54, 0x83, 0xfa, 0x37, + }, + }, + { + .data_len = 4224, + .digest = { + 0x46, 0xab, 0x8c, 0x75, 0xb3, 0x10, 0xa6, 0x3f, + 0x74, 0x55, 0x42, 0x6d, 0x69, 0x35, 0xd2, 0xf5, + }, + }, + { + .data_len = 16384, + .digest = { + 0xd0, 0xfe, 0x26, 0xc2, 0xca, 0x94, 0x2d, 0x52, + 0x2d, 0xe1, 0x11, 0xdd, 0x42, 0x28, 0x83, 0xa6, + }, + }, +}; + +static const u8 hash_testvec_consolidated[POLY1305_DIGEST_SIZE] = { + 0x9d, 0x07, 0x5d, 0xc9, 0x6c, 0xeb, 0x62, 0x5d, + 0x02, 0x5f, 0xe1, 0xe3, 0xd1, 0x71, 0x69, 0x34, +}; + +static const u8 poly1305_allones_macofmacs[POLY1305_DIGEST_SIZE] = { + 0x0c, 0x26, 0x6b, 0x45, 0x87, 0x06, 0xcf, 0xc4, + 0x3f, 0x70, 0x7d, 0xb3, 0x50, 0xdd, 0x81, 0x25, +}; diff --git a/lib/crypto/tests/poly1305_kunit.c b/lib/crypto/tests/poly1305_kunit.c new file mode 100644 index 000000000000..7ac191bd96b6 --- /dev/null +++ b/lib/crypto/tests/poly1305_kunit.c @@ -0,0 +1,165 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * Copyright 2025 Google LLC + */ +#include +#include "poly1305-testvecs.h" + +/* + * A fixed key used when presenting Poly1305 as an unkeyed hash function in + * order to reuse hash-test-template.h. At the beginning of the test suite, + * this is initialized to bytes generated from a fixed seed. + */ +static u8 test_key[POLY1305_KEY_SIZE]; + +/* This probably should be in the actual API, but just define it here for now */ +static void poly1305(const u8 key[POLY1305_KEY_SIZE], const u8 *data, + size_t len, u8 out[POLY1305_DIGEST_SIZE]) +{ + struct poly1305_desc_ctx ctx; + + poly1305_init(&ctx, key); + poly1305_update(&ctx, data, len); + poly1305_final(&ctx, out); +} + +static void poly1305_init_withtestkey(struct poly1305_desc_ctx *ctx) +{ + poly1305_init(ctx, test_key); +} + +static void poly1305_withtestkey(const u8 *data, size_t len, + u8 out[POLY1305_DIGEST_SIZE]) +{ + poly1305(test_key, data, len, out); +} + +/* Generate the HASH_KUNIT_CASES using hash-test-template.h. */ +#define HASH poly1305_withtestkey +#define HASH_CTX poly1305_desc_ctx +#define HASH_SIZE POLY1305_DIGEST_SIZE +#define HASH_INIT poly1305_init_withtestkey +#define HASH_UPDATE poly1305_update +#define HASH_FINAL poly1305_final +#include "hash-test-template.h" + +static int poly1305_suite_init(struct kunit_suite *suite) +{ + rand_bytes_seeded_from_len(test_key, POLY1305_KEY_SIZE); + return hash_suite_init(suite); +} + +static void poly1305_suite_exit(struct kunit_suite *suite) +{ + hash_suite_exit(suite); +} + +/* + * Poly1305 test case which uses a key and message consisting only of one bits: + * + * - Using an all-one-bits r_key tests the key clamping. + * - Using an all-one-bits s_key tests carries in implementations of the + * addition mod 2**128 during finalization. + * - Using all-one-bits message, and to a lesser extent r_key, tends to maximize + * any intermediate accumulator values. This increases the chance of + * detecting bugs that occur only in rare cases where the accumulator values + * get very large, for example the bug fixed by commit 678cce4019d746da + * ("crypto: x86/poly1305 - fix overflow during partial reduction"). + * + * Accumulator overflow bugs may be specific to particular update lengths (in + * blocks) and/or particular values of the previous acculumator. Note that the + * accumulator starts at 0 which gives the lowest chance of an overflow. Thus, + * a single all-one-bits test vector may be insufficient. + * + * Considering that, do the following test: continuously update a single + * Poly1305 context with all-one-bits data of varying lengths (0, 16, 32, ..., + * 4096 bytes). After each update, generate the MAC from the current context, + * and feed that MAC into a separate Poly1305 context. Repeat that entire + * sequence of updates 32 times without re-initializing either context, + * resulting in a total of 8224 MAC computations from a long-running, cumulative + * context. Finally, generate and verify the MAC of all the MACs. + */ +static void test_poly1305_allones_keys_and_message(struct kunit *test) +{ + struct poly1305_desc_ctx mac_ctx, macofmacs_ctx; + u8 mac[POLY1305_DIGEST_SIZE]; + + static_assert(TEST_BUF_LEN >= 4096); + memset(test_buf, 0xff, 4096); + + poly1305_init(&mac_ctx, test_buf); + poly1305_init(&macofmacs_ctx, test_buf); + for (int i = 0; i < 32; i++) { + for (size_t len = 0; len <= 4096; len += 16) { + struct poly1305_desc_ctx tmp_ctx; + + poly1305_update(&mac_ctx, test_buf, len); + tmp_ctx = mac_ctx; + poly1305_final(&tmp_ctx, mac); + poly1305_update(&macofmacs_ctx, mac, + POLY1305_DIGEST_SIZE); + } + } + poly1305_final(&macofmacs_ctx, mac); + KUNIT_ASSERT_MEMEQ(test, mac, poly1305_allones_macofmacs, + POLY1305_DIGEST_SIZE); +} + +/* + * Poly1305 test case which uses r_key=1, s_key=0, and a 48-byte message + * consisting of three blocks with integer values [2**128 - i, 0, 0]. In this + * case, the result of the polynomial evaluation is 2**130 - i. For small + * values of i, this is very close to the modulus 2**130 - 5, which helps catch + * edge case bugs in the modular reduction logic. + */ +static void test_poly1305_reduction_edge_cases(struct kunit *test) +{ + static const u8 key[POLY1305_KEY_SIZE] = { 1 }; /* r_key=1, s_key=0 */ + u8 data[3 * POLY1305_BLOCK_SIZE] = {}; + u8 expected_mac[POLY1305_DIGEST_SIZE]; + u8 actual_mac[POLY1305_DIGEST_SIZE]; + + for (int i = 1; i <= 10; i++) { + /* Set the first data block to 2**128 - i. */ + data[0] = -i; + memset(&data[1], 0xff, POLY1305_BLOCK_SIZE - 1); + + /* + * Assuming s_key=0, the expected MAC as an integer is + * (2**130 - i mod 2**130 - 5) + 0 mod 2**128. If 1 <= i <= 5, + * that's 5 - i. If 6 <= i <= 10, that's 2**128 - i. + */ + if (i <= 5) { + expected_mac[0] = 5 - i; + memset(&expected_mac[1], 0, POLY1305_DIGEST_SIZE - 1); + } else { + expected_mac[0] = -i; + memset(&expected_mac[1], 0xff, + POLY1305_DIGEST_SIZE - 1); + } + + /* Compute and verify the MAC. */ + poly1305(key, data, sizeof(data), actual_mac); + KUNIT_ASSERT_MEMEQ(test, actual_mac, expected_mac, + POLY1305_DIGEST_SIZE); + } +} + +static struct kunit_case poly1305_test_cases[] = { + HASH_KUNIT_CASES, + KUNIT_CASE(test_poly1305_allones_keys_and_message), + KUNIT_CASE(test_poly1305_reduction_edge_cases), + KUNIT_CASE(benchmark_hash), + {}, +}; + +static struct kunit_suite poly1305_test_suite = { + .name = "poly1305", + .test_cases = poly1305_test_cases, + .suite_init = poly1305_suite_init, + .suite_exit = poly1305_suite_exit, +}; +kunit_test_suite(poly1305_test_suite); + +MODULE_DESCRIPTION("KUnit tests and benchmark for Poly1305"); +MODULE_LICENSE("GPL"); diff --git a/scripts/crypto/gen-hash-testvecs.py b/scripts/crypto/gen-hash-testvecs.py index 55f1010339a6..4ac927d40cf5 100755 --- a/scripts/crypto/gen-hash-testvecs.py +++ b/scripts/crypto/gen-hash-testvecs.py @@ -24,7 +24,37 @@ def rand_bytes(length): out.append((seed >> 16) % 256) return bytes(out) +POLY1305_KEY_SIZE = 32 + +# A straightforward, unoptimized implementation of Poly1305. +# Reference: https://cr.yp.to/mac/poly1305-20050329.pdf +class Poly1305: + def __init__(self, key): + assert len(key) == POLY1305_KEY_SIZE + self.h = 0 + rclamp = 0x0ffffffc0ffffffc0ffffffc0fffffff + self.r = int.from_bytes(key[:16], byteorder='little') & rclamp + self.s = int.from_bytes(key[16:], byteorder='little') + + # Note: this supports partial blocks only at the end. + def update(self, data): + for i in range(0, len(data), 16): + chunk = data[i:i+16] + c = int.from_bytes(chunk, byteorder='little') + 2**(8 * len(chunk)) + self.h = ((self.h + c) * self.r) % (2**130 - 5) + return self + + # Note: gen_additional_poly1305_testvecs() relies on this being + # nondestructive, i.e. not changing any field of self. + def digest(self): + m = (self.h + self.s) % 2**128 + return m.to_bytes(16, byteorder='little') + def hash_init(alg): + if alg == 'poly1305': + # Use a fixed random key here, to present Poly1305 as an unkeyed hash. + # This allows all the test cases for unkeyed hashes to work on Poly1305. + return Poly1305(rand_bytes(POLY1305_KEY_SIZE)) return hashlib.new(alg) def hash_update(ctx, data): @@ -89,9 +119,21 @@ def gen_hmac_testvecs(alg): f'hmac_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]', ctx.digest()) +def gen_additional_poly1305_testvecs(): + key = b'\xff' * POLY1305_KEY_SIZE + data = b'' + ctx = Poly1305(key) + for _ in range(32): + for j in range(0, 4097, 16): + ctx.update(b'\xff' * j) + data += ctx.digest() + print_static_u8_array_definition( + 'poly1305_allones_macofmacs[POLY1305_DIGEST_SIZE]', + Poly1305(key).update(data).digest()) + if len(sys.argv) != 2: sys.stderr.write('Usage: gen-hash-testvecs.py ALGORITHM\n') - sys.stderr.write('ALGORITHM may be any supported by Python hashlib.\n') + sys.stderr.write('ALGORITHM may be any supported by Python hashlib, or poly1305.\n') sys.stderr.write('Example: gen-hash-testvecs.py sha512\n') sys.exit(1) @@ -99,4 +141,7 @@ alg = sys.argv[1] print('/* SPDX-License-Identifier: GPL-2.0-or-later */') print(f'/* This file was generated by: {sys.argv[0]} {" ".join(sys.argv[1:])} */') gen_unkeyed_testvecs(alg) -gen_hmac_testvecs(alg) +if alg == 'poly1305': + gen_additional_poly1305_testvecs() +else: + gen_hmac_testvecs(alg) -- cgit v1.2.3 From 7498159226772d66f150dd406be462d75964a366 Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Sat, 12 Jul 2025 18:01:03 +0200 Subject: rust: use `#[used(compiler)]` to fix build and `modpost` with Rust >= 1.89.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Starting with Rust 1.89.0 (expected 2025-08-07), the Rust compiler fails to build the `rusttest` target due to undefined references such as: kernel...-cgu.0:(.text....+0x116): undefined reference to `rust_helper_kunit_get_current_test' Moreover, tooling like `modpost` gets confused: WARNING: modpost: missing MODULE_DESCRIPTION() in drivers/gpu/drm/nova/nova.o ERROR: modpost: missing MODULE_LICENSE() in drivers/gpu/nova-core/nova_core.o The reason behind both issues is that the Rust compiler will now [1] treat `#[used]` as `#[used(linker)]` instead of `#[used(compiler)]` for our targets. This means that the retain section flag (`R`, `SHF_GNU_RETAIN`) will be used and that they will be marked as `unique` too, with different IDs. In turn, that means we end up with undefined references that did not get discarded in `rusttest` and that multiple `.modinfo` sections are generated, which confuse tooling like `modpost` because they only expect one. Thus start using `#[used(compiler)]` to keep the previous behavior and to be explicit about what we want. Sadly, it is an unstable feature (`used_with_arg`) [2] -- we will talk to upstream Rust about it. The good news is that it has been available for a long time (Rust >= 1.60) [3]. The changes should also be fine for previous Rust versions, since they behave the same way as before [4]. Alternatively, we could use `#[no_mangle]` or `#[export_name = ...]` since those still behave like `#[used(compiler)]`, but of course it is not really what we want to express, and it requires other changes to avoid symbol conflicts. Cc: David Wood Cc: Wesley Wiser Cc: stable@vger.kernel.org # Needed in 6.12.y and later (Rust is pinned in older LTSs). Link: https://github.com/rust-lang/rust/pull/140872 [1] Link: https://github.com/rust-lang/rust/issues/93798 [2] Link: https://github.com/rust-lang/rust/pull/91504 [3] Link: https://godbolt.org/z/sxzWTMfzW [4] Reviewed-by: Alice Ryhl Acked-by: Björn Roy Baron Link: https://lore.kernel.org/r/20250712160103.1244945-3-ojeda@kernel.org Signed-off-by: Miguel Ojeda --- rust/Makefile | 1 + rust/kernel/firmware.rs | 2 +- rust/kernel/kunit.rs | 2 +- rust/kernel/lib.rs | 3 +++ rust/macros/module.rs | 10 +++++----- scripts/Makefile.build | 3 ++- 6 files changed, 13 insertions(+), 8 deletions(-) (limited to 'scripts') diff --git a/rust/Makefile b/rust/Makefile index 27dec7904c3a..115b63b7d1e3 100644 --- a/rust/Makefile +++ b/rust/Makefile @@ -194,6 +194,7 @@ quiet_cmd_rustdoc_test = RUSTDOC T $< RUST_MODFILE=test.rs \ OBJTREE=$(abspath $(objtree)) \ $(RUSTDOC) --test $(rust_common_flags) \ + -Zcrate-attr='feature(used_with_arg)' \ @$(objtree)/include/generated/rustc_cfg \ $(rustc_target_flags) $(rustdoc_test_target_flags) \ $(rustdoc_test_quiet) \ diff --git a/rust/kernel/firmware.rs b/rust/kernel/firmware.rs index 2494c96e105f..4fe621f35716 100644 --- a/rust/kernel/firmware.rs +++ b/rust/kernel/firmware.rs @@ -202,7 +202,7 @@ macro_rules! module_firmware { }; #[link_section = ".modinfo"] - #[used] + #[used(compiler)] static __MODULE_FIRMWARE: [u8; $($builder)*::create(__MODULE_FIRMWARE_PREFIX) .build_length()] = $($builder)*::create(__MODULE_FIRMWARE_PREFIX).build(); }; diff --git a/rust/kernel/kunit.rs b/rust/kernel/kunit.rs index 4b8cdcb21e77..b9e65905e121 100644 --- a/rust/kernel/kunit.rs +++ b/rust/kernel/kunit.rs @@ -302,7 +302,7 @@ macro_rules! kunit_unsafe_test_suite { is_init: false, }; - #[used] + #[used(compiler)] #[allow(unused_unsafe)] #[cfg_attr(not(target_os = "macos"), link_section = ".kunit_test_suites")] static mut KUNIT_TEST_SUITE_ENTRY: *const ::kernel::bindings::kunit_suite = diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 6b4774b2b1c3..e13d6ed88fa6 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -34,6 +34,9 @@ // Expected to become stable. #![feature(arbitrary_self_types)] // +// To be determined. +#![feature(used_with_arg)] +// // `feature(derive_coerce_pointee)` is expected to become stable. Before Rust // 1.84.0, it did not exist, so enable the predecessor features. #![cfg_attr(CONFIG_RUSTC_HAS_COERCE_POINTEE, feature(derive_coerce_pointee))] diff --git a/rust/macros/module.rs b/rust/macros/module.rs index 2ddd2eeb2852..75efc6eeeafc 100644 --- a/rust/macros/module.rs +++ b/rust/macros/module.rs @@ -57,7 +57,7 @@ impl<'a> ModInfoBuilder<'a> { {cfg} #[doc(hidden)] #[cfg_attr(not(target_os = \"macos\"), link_section = \".modinfo\")] - #[used] + #[used(compiler)] pub static __{module}_{counter}: [u8; {length}] = *{string}; ", cfg = if builtin { @@ -249,7 +249,7 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { // key or a new section. For the moment, keep it simple. #[cfg(MODULE)] #[doc(hidden)] - #[used] + #[used(compiler)] static __IS_RUST_MODULE: () = (); static mut __MOD: ::core::mem::MaybeUninit<{type_}> = @@ -273,7 +273,7 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { #[cfg(MODULE)] #[doc(hidden)] - #[used] + #[used(compiler)] #[link_section = \".init.data\"] static __UNIQUE_ID___addressable_init_module: unsafe extern \"C\" fn() -> i32 = init_module; @@ -293,7 +293,7 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { #[cfg(MODULE)] #[doc(hidden)] - #[used] + #[used(compiler)] #[link_section = \".exit.data\"] static __UNIQUE_ID___addressable_cleanup_module: extern \"C\" fn() = cleanup_module; @@ -303,7 +303,7 @@ pub(crate) fn module(ts: TokenStream) -> TokenStream { #[cfg(not(CONFIG_HAVE_ARCH_PREL32_RELOCATIONS))] #[doc(hidden)] #[link_section = \"{initcall_section}\"] - #[used] + #[used(compiler)] pub static __{ident}_initcall: extern \"C\" fn() -> ::kernel::ffi::c_int = __{ident}_init; diff --git a/scripts/Makefile.build b/scripts/Makefile.build index a6461ea411f7..ba71b27aa363 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -312,10 +312,11 @@ $(obj)/%.lst: $(obj)/%.c FORCE # - Stable since Rust 1.82.0: `feature(asm_const)`, `feature(raw_ref_op)`. # - Stable since Rust 1.87.0: `feature(asm_goto)`. # - Expected to become stable: `feature(arbitrary_self_types)`. +# - To be determined: `feature(used_with_arg)`. # # Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on # the unstable features in use. -rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,raw_ref_op +rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,raw_ref_op,used_with_arg # `--out-dir` is required to avoid temporaries being created by `rustc` in the # current working directory, which may be not accessible in the out-of-tree -- cgit v1.2.3 From 8d9d122915492ea6984f32e5df30cef5c582f062 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Tue, 1 Jul 2025 16:21:24 -0600 Subject: docs: kdoc: drop "sectionlist" Python dicts (as of 3.7) are guaranteed to remember the insertion order of items, so we do not need a separate list for that purpose. Drop the per-entry sectionlist variable and just rely on native dict ordering. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_output.py | 18 ++++++------------ scripts/lib/kdoc/kdoc_parser.py | 13 +------------ 2 files changed, 7 insertions(+), 24 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output.py index 86102e628d91..4895c80e4b81 100644 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -339,11 +339,10 @@ class RestFormat(OutputFormat): tends to duplicate a header already in the template file. """ - sectionlist = args.get('sectionlist', []) sections = args.get('sections', {}) section_start_lines = args.get('section_start_lines', {}) - for section in sectionlist: + for section in sections: # Skip sections that are in the nosymbol_table if section in self.nosymbol: continue @@ -636,7 +635,6 @@ class ManFormat(OutputFormat): self.data += line + "\n" def out_doc(self, fname, name, args): - sectionlist = args.get('sectionlist', []) sections = args.get('sections', {}) if not self.check_doc(name, args): @@ -644,7 +642,7 @@ class ManFormat(OutputFormat): self.data += f'.TH "{self.modulename}" 9 "{self.modulename}" "{self.man_date}" "API Manual" LINUX' + "\n" - for section in sectionlist: + for section in sections: self.data += f'.SH "{section}"' + "\n" self.output_highlight(sections.get(section)) @@ -653,7 +651,6 @@ class ManFormat(OutputFormat): parameterlist = args.get('parameterlist', []) parameterdescs = args.get('parameterdescs', {}) - sectionlist = args.get('sectionlist', []) sections = args.get('sections', {}) self.data += f'.TH "{args["function"]}" 9 "{args["function"]}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n" @@ -695,7 +692,7 @@ class ManFormat(OutputFormat): self.data += f'.IP "{parameter}" 12' + "\n" self.output_highlight(parameterdescs.get(parameter_name, "")) - for section in sectionlist: + for section in sections: self.data += f'.SH "{section.upper()}"' + "\n" self.output_highlight(sections[section]) @@ -703,7 +700,6 @@ class ManFormat(OutputFormat): name = args.get('enum', '') parameterlist = args.get('parameterlist', []) - sectionlist = args.get('sectionlist', []) sections = args.get('sections', {}) self.data += f'.TH "{self.modulename}" 9 "enum {args["enum"]}" "{self.man_date}" "API Manual" LINUX' + "\n" @@ -731,7 +727,7 @@ class ManFormat(OutputFormat): self.data += f'.IP "{parameter}" 12' + "\n" self.output_highlight(args['parameterdescs'].get(parameter_name, "")) - for section in sectionlist: + for section in sections: self.data += f'.SH "{section}"' + "\n" self.output_highlight(sections[section]) @@ -739,7 +735,6 @@ class ManFormat(OutputFormat): module = self.modulename typedef = args.get('typedef') purpose = args.get('purpose') - sectionlist = args.get('sectionlist', []) sections = args.get('sections', {}) self.data += f'.TH "{module}" 9 "{typedef}" "{self.man_date}" "API Manual" LINUX' + "\n" @@ -747,7 +742,7 @@ class ManFormat(OutputFormat): self.data += ".SH NAME\n" self.data += f"typedef {typedef} \\- {purpose}\n" - for section in sectionlist: + for section in sections: self.data += f'.SH "{section}"' + "\n" self.output_highlight(sections.get(section)) @@ -757,7 +752,6 @@ class ManFormat(OutputFormat): struct_name = args.get('struct') purpose = args.get('purpose') definition = args.get('definition') - sectionlist = args.get('sectionlist', []) parameterlist = args.get('parameterlist', []) sections = args.get('sections', {}) parameterdescs = args.get('parameterdescs', {}) @@ -788,6 +782,6 @@ class ManFormat(OutputFormat): self.data += f'.IP "{parameter}" 12' + "\n" self.output_highlight(parameterdescs.get(parameter_name)) - for section in sectionlist: + for section in sections: self.data += f'.SH "{section}"' + "\n" self.output_highlight(sections.get(section)) diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 97380ff30a0d..2e00c8b3a5f2 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -127,7 +127,6 @@ class KernelEntry: self.parameterdesc_start_lines = {} self.section_start_lines = {} - self.sectionlist = [] self.sections = {} self.anon_struct_union = False @@ -202,7 +201,6 @@ class KernelEntry: self.sections[name] += '\n' + contents else: self.sections[name] = contents - self.sectionlist.append(name) self.section_start_lines[name] = self.new_start_line self.new_start_line = 0 @@ -275,14 +273,12 @@ class KernelDoc: item.warnings = self.entry.warnings sections = item.get('sections', {}) - sectionlist = item.get('sectionlist', []) # Drop empty sections # TODO: improve empty sections logic to emit warnings for section in ["Description", "Return"]: - if section in sectionlist and not sections[section].rstrip(): + if section in sections and not sections[section].rstrip(): del sections[section] - sectionlist.remove(section) self.entries.append(item) @@ -828,7 +824,6 @@ class KernelDoc: parameterdescs=self.entry.parameterdescs, parametertypes=self.entry.parametertypes, parameterdesc_start_lines=self.entry.parameterdesc_start_lines, - sectionlist=self.entry.sectionlist, sections=self.entry.sections, section_start_lines=self.entry.section_start_lines, purpose=self.entry.declaration_purpose) @@ -913,7 +908,6 @@ class KernelDoc: parameterlist=self.entry.parameterlist, parameterdescs=self.entry.parameterdescs, parameterdesc_start_lines=self.entry.parameterdesc_start_lines, - sectionlist=self.entry.sectionlist, sections=self.entry.sections, section_start_lines=self.entry.section_start_lines, purpose=self.entry.declaration_purpose) @@ -1085,7 +1079,6 @@ class KernelDoc: parameterdescs=self.entry.parameterdescs, parametertypes=self.entry.parametertypes, parameterdesc_start_lines=self.entry.parameterdesc_start_lines, - sectionlist=self.entry.sectionlist, sections=self.entry.sections, section_start_lines=self.entry.section_start_lines, purpose=self.entry.declaration_purpose, @@ -1099,7 +1092,6 @@ class KernelDoc: parameterdescs=self.entry.parameterdescs, parametertypes=self.entry.parametertypes, parameterdesc_start_lines=self.entry.parameterdesc_start_lines, - sectionlist=self.entry.sectionlist, sections=self.entry.sections, section_start_lines=self.entry.section_start_lines, purpose=self.entry.declaration_purpose, @@ -1145,7 +1137,6 @@ class KernelDoc: parameterdescs=self.entry.parameterdescs, parametertypes=self.entry.parametertypes, parameterdesc_start_lines=self.entry.parameterdesc_start_lines, - sectionlist=self.entry.sectionlist, sections=self.entry.sections, section_start_lines=self.entry.section_start_lines, purpose=self.entry.declaration_purpose) @@ -1168,7 +1159,6 @@ class KernelDoc: self.output_declaration('typedef', declaration_name, typedef=declaration_name, - sectionlist=self.entry.sectionlist, sections=self.entry.sections, section_start_lines=self.entry.section_start_lines, purpose=self.entry.declaration_purpose) @@ -1653,7 +1643,6 @@ class KernelDoc: if doc_end.search(line): self.dump_section() self.output_declaration("doc", self.entry.identifier, - sectionlist=self.entry.sectionlist, sections=self.entry.sections, section_start_lines=self.entry.section_start_lines) self.reset_state(ln) -- cgit v1.2.3 From 8d7338752d76c3854a5c54cf7df976c539baab5b Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Tue, 1 Jul 2025 16:47:59 -0600 Subject: docs: kdoc: Centralize handling of the item section list The section list always comes directly from the under-construction entry and is used uniformly. Formalize section handling in the KdocItem class, and have output_declaration() load the sections directly from the entry, eliminating a lot of duplicated, verbose parameters. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_item.py | 9 +++++++++ scripts/lib/kdoc/kdoc_output.py | 36 +++++++++++++----------------------- scripts/lib/kdoc/kdoc_parser.py | 20 +++----------------- 3 files changed, 25 insertions(+), 40 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_item.py b/scripts/lib/kdoc/kdoc_item.py index add2cc772fec..f0b2b9082c56 100644 --- a/scripts/lib/kdoc/kdoc_item.py +++ b/scripts/lib/kdoc/kdoc_item.py @@ -9,6 +9,8 @@ class KdocItem: self.name = name self.type = type self.declaration_start_line = start_line + self.sections = {} + self.sections_start_lines = {} # # Just save everything else into our own dict so that the output # side can grab it directly as before. As we move things into more @@ -24,3 +26,10 @@ class KdocItem: def __getitem__(self, key): return self.get(key) + + # + # Tracking of section information. + # + def set_sections(self, sections, start_lines): + self.sections = sections + self.section_start_lines = start_lines diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output.py index 4895c80e4b81..15cb89f91987 100644 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -338,11 +338,7 @@ class RestFormat(OutputFormat): starts by putting out the name of the doc section itself, but that tends to duplicate a header already in the template file. """ - - sections = args.get('sections', {}) - section_start_lines = args.get('section_start_lines', {}) - - for section in sections: + for section, text in args.sections.items(): # Skip sections that are in the nosymbol_table if section in self.nosymbol: continue @@ -354,8 +350,8 @@ class RestFormat(OutputFormat): else: self.data += f'{self.lineprefix}**{section}**\n\n' - self.print_lineno(section_start_lines.get(section, 0)) - self.output_highlight(sections[section]) + self.print_lineno(args.section_start_lines.get(section, 0)) + self.output_highlight(text) self.data += "\n" self.data += "\n" @@ -635,23 +631,20 @@ class ManFormat(OutputFormat): self.data += line + "\n" def out_doc(self, fname, name, args): - sections = args.get('sections', {}) - if not self.check_doc(name, args): return self.data += f'.TH "{self.modulename}" 9 "{self.modulename}" "{self.man_date}" "API Manual" LINUX' + "\n" - for section in sections: + for section, text in args.sections.items(): self.data += f'.SH "{section}"' + "\n" - self.output_highlight(sections.get(section)) + self.output_highlight(text) def out_function(self, fname, name, args): """output function in man""" parameterlist = args.get('parameterlist', []) parameterdescs = args.get('parameterdescs', {}) - sections = args.get('sections', {}) self.data += f'.TH "{args["function"]}" 9 "{args["function"]}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n" @@ -692,15 +685,14 @@ class ManFormat(OutputFormat): self.data += f'.IP "{parameter}" 12' + "\n" self.output_highlight(parameterdescs.get(parameter_name, "")) - for section in sections: + for section, text in args.sections.items(): self.data += f'.SH "{section.upper()}"' + "\n" - self.output_highlight(sections[section]) + self.output_highlight(text) def out_enum(self, fname, name, args): name = args.get('enum', '') parameterlist = args.get('parameterlist', []) - sections = args.get('sections', {}) self.data += f'.TH "{self.modulename}" 9 "enum {args["enum"]}" "{self.man_date}" "API Manual" LINUX' + "\n" @@ -727,24 +719,23 @@ class ManFormat(OutputFormat): self.data += f'.IP "{parameter}" 12' + "\n" self.output_highlight(args['parameterdescs'].get(parameter_name, "")) - for section in sections: + for section, text in args.sections.items(): self.data += f'.SH "{section}"' + "\n" - self.output_highlight(sections[section]) + self.output_highlight(text) def out_typedef(self, fname, name, args): module = self.modulename typedef = args.get('typedef') purpose = args.get('purpose') - sections = args.get('sections', {}) self.data += f'.TH "{module}" 9 "{typedef}" "{self.man_date}" "API Manual" LINUX' + "\n" self.data += ".SH NAME\n" self.data += f"typedef {typedef} \\- {purpose}\n" - for section in sections: + for section, text in args.sections.items(): self.data += f'.SH "{section}"' + "\n" - self.output_highlight(sections.get(section)) + self.output_highlight(text) def out_struct(self, fname, name, args): module = self.modulename @@ -753,7 +744,6 @@ class ManFormat(OutputFormat): purpose = args.get('purpose') definition = args.get('definition') parameterlist = args.get('parameterlist', []) - sections = args.get('sections', {}) parameterdescs = args.get('parameterdescs', {}) self.data += f'.TH "{module}" 9 "{struct_type} {struct_name}" "{self.man_date}" "API Manual" LINUX' + "\n" @@ -782,6 +772,6 @@ class ManFormat(OutputFormat): self.data += f'.IP "{parameter}" 12' + "\n" self.output_highlight(parameterdescs.get(parameter_name)) - for section in sections: + for section, text in args.sections.items(): self.data += f'.SH "{section}"' + "\n" - self.output_highlight(sections.get(section)) + self.output_highlight(text) diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 2e00c8b3a5f2..608f3a1045dc 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -272,13 +272,13 @@ class KernelDoc: item = KdocItem(name, dtype, self.entry.declaration_start_line, **args) item.warnings = self.entry.warnings - sections = item.get('sections', {}) - # Drop empty sections # TODO: improve empty sections logic to emit warnings + sections = self.entry.sections for section in ["Description", "Return"]: if section in sections and not sections[section].rstrip(): del sections[section] + item.set_sections(sections, self.entry.section_start_lines) self.entries.append(item) @@ -824,8 +824,6 @@ class KernelDoc: parameterdescs=self.entry.parameterdescs, parametertypes=self.entry.parametertypes, parameterdesc_start_lines=self.entry.parameterdesc_start_lines, - sections=self.entry.sections, - section_start_lines=self.entry.section_start_lines, purpose=self.entry.declaration_purpose) def dump_enum(self, ln, proto): @@ -908,8 +906,6 @@ class KernelDoc: parameterlist=self.entry.parameterlist, parameterdescs=self.entry.parameterdescs, parameterdesc_start_lines=self.entry.parameterdesc_start_lines, - sections=self.entry.sections, - section_start_lines=self.entry.section_start_lines, purpose=self.entry.declaration_purpose) def dump_declaration(self, ln, prototype): @@ -1079,8 +1075,6 @@ class KernelDoc: parameterdescs=self.entry.parameterdescs, parametertypes=self.entry.parametertypes, parameterdesc_start_lines=self.entry.parameterdesc_start_lines, - sections=self.entry.sections, - section_start_lines=self.entry.section_start_lines, purpose=self.entry.declaration_purpose, func_macro=func_macro) else: @@ -1092,8 +1086,6 @@ class KernelDoc: parameterdescs=self.entry.parameterdescs, parametertypes=self.entry.parametertypes, parameterdesc_start_lines=self.entry.parameterdesc_start_lines, - sections=self.entry.sections, - section_start_lines=self.entry.section_start_lines, purpose=self.entry.declaration_purpose, func_macro=func_macro) @@ -1137,8 +1129,6 @@ class KernelDoc: parameterdescs=self.entry.parameterdescs, parametertypes=self.entry.parametertypes, parameterdesc_start_lines=self.entry.parameterdesc_start_lines, - sections=self.entry.sections, - section_start_lines=self.entry.section_start_lines, purpose=self.entry.declaration_purpose) return @@ -1159,8 +1149,6 @@ class KernelDoc: self.output_declaration('typedef', declaration_name, typedef=declaration_name, - sections=self.entry.sections, - section_start_lines=self.entry.section_start_lines, purpose=self.entry.declaration_purpose) return @@ -1642,9 +1630,7 @@ class KernelDoc: if doc_end.search(line): self.dump_section() - self.output_declaration("doc", self.entry.identifier, - sections=self.entry.sections, - section_start_lines=self.entry.section_start_lines) + self.output_declaration("doc", self.entry.identifier) self.reset_state(ln) elif doc_content.search(line): -- cgit v1.2.3 From 172bee3376ab29fbf38b09bf01d6f06f7f6c39e1 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Wed, 2 Jul 2025 11:04:43 -0600 Subject: docs: kdoc: remove the "struct_actual" machinery The code goes out of its way to create a special list of parameters in entry.struct_actual that is just like entry.parameterlist, but with extra junk. The only use of that information, in check_sections(), promptly strips all the extra junk back out. Drop all that extra work and just use parameterlist. No output changes. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 32 ++------------------------------ 1 file changed, 2 insertions(+), 30 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 608f3a1045dc..b28f056365cb 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -116,7 +116,6 @@ class KernelEntry: self._contents = [] self.sectcheck = "" - self.struct_actual = "" self.prototype = "" self.warnings = [] @@ -366,15 +365,6 @@ class KernelDoc: org_arg = KernRe(r'\s\s+').sub(' ', org_arg) self.entry.parametertypes[param] = org_arg - def save_struct_actual(self, actual): - """ - Strip all spaces from the actual param so that it looks like - one string item. - """ - - actual = KernRe(r'\s*').sub("", actual, count=1) - - self.entry.struct_actual += actual + " " def create_parameter_list(self, ln, decl_type, args, splitter, declaration_name): @@ -420,7 +410,6 @@ class KernelDoc: param = arg dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) - self.save_struct_actual(param) self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name) @@ -437,7 +426,6 @@ class KernelDoc: dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) - self.save_struct_actual(param) self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name) @@ -470,7 +458,6 @@ class KernelDoc: param = r.group(1) - self.save_struct_actual(r.group(2)) self.push_parameter(ln, decl_type, r.group(2), f"{dtype} {r.group(1)}", arg, declaration_name) @@ -482,12 +469,10 @@ class KernelDoc: continue if dtype != "": # Skip unnamed bit-fields - self.save_struct_actual(r.group(1)) self.push_parameter(ln, decl_type, r.group(1), f"{dtype}:{r.group(2)}", arg, declaration_name) else: - self.save_struct_actual(param) self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name) @@ -499,24 +484,11 @@ class KernelDoc: sects = sectcheck.split() prms = prmscheck.split() - err = False for sx in range(len(sects)): # pylint: disable=C0200 err = True for px in range(len(prms)): # pylint: disable=C0200 - prm_clean = prms[px] - prm_clean = KernRe(r'\[.*\]').sub('', prm_clean) - prm_clean = attribute.sub('', prm_clean) - - # ignore array size in a parameter string; - # however, the original param string may contain - # spaces, e.g.: addr[6 + 2] - # and this appears in @prms as "addr[6" since the - # parameter list is split at spaces; - # hence just ignore "[..." for the sections check; - prm_clean = KernRe(r'\[.*').sub('', prm_clean) - - if prm_clean == sects[sx]: + if prms[px] == sects[sx]: err = False break @@ -782,7 +754,7 @@ class KernelDoc: self.create_parameter_list(ln, decl_type, members, ';', declaration_name) self.check_sections(ln, declaration_name, decl_type, - self.entry.sectcheck, self.entry.struct_actual) + self.entry.sectcheck, ' '.join(self.entry.parameterlist)) # Adjust declaration for better display declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration) -- cgit v1.2.3 From efacdf85135ae02a8c25452e40547b773bb1b6b3 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Wed, 2 Jul 2025 11:12:27 -0600 Subject: docs: kdoc: use self.entry.parameterlist directly in check_sections() Callers of check_sections() join parameterlist into a single string, which is then immediately split back into the original list. Rather than do all that, just use parameterlist directly in check_sections(). Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index b28f056365cb..ffd49f9395ae 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -476,19 +476,18 @@ class KernelDoc: self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name) - def check_sections(self, ln, decl_name, decl_type, sectcheck, prmscheck): + def check_sections(self, ln, decl_name, decl_type, sectcheck): """ Check for errors inside sections, emitting warnings if not found parameters are described. """ sects = sectcheck.split() - prms = prmscheck.split() for sx in range(len(sects)): # pylint: disable=C0200 err = True - for px in range(len(prms)): # pylint: disable=C0200 - if prms[px] == sects[sx]: + for param in self.entry.parameterlist: + if param == sects[sx]: err = False break @@ -753,8 +752,7 @@ class KernelDoc: self.create_parameter_list(ln, decl_type, members, ';', declaration_name) - self.check_sections(ln, declaration_name, decl_type, - self.entry.sectcheck, ' '.join(self.entry.parameterlist)) + self.check_sections(ln, declaration_name, decl_type, self.entry.sectcheck) # Adjust declaration for better display declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration) @@ -1032,9 +1030,7 @@ class KernelDoc: f"expecting prototype for {self.entry.identifier}(). Prototype was for {declaration_name}() instead") return - prms = " ".join(self.entry.parameterlist) - self.check_sections(ln, declaration_name, "function", - self.entry.sectcheck, prms) + self.check_sections(ln, declaration_name, "function", self.entry.sectcheck) self.check_return_section(ln, declaration_name, return_type) -- cgit v1.2.3 From de6f7ac91a08d723a6eaa9c5bbce30c5a126c861 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Wed, 2 Jul 2025 13:05:56 -0600 Subject: docs: kdoc: Coalesce parameter-list handling Callers to output_declaration() always pass the parameter information from self.entry; remove all of the boilerplate arguments and just get at that information directly. Formalize its placement in the KdocItem class. It would be nice to get rid of parameterlist as well, but that has the effect of reordering the output of function parameters and struct fields to match the order in the kerneldoc comment rather than in the declaration. One could argue about which is more correct, but the ordering has been left unchanged for now. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_item.py | 12 ++++++- scripts/lib/kdoc/kdoc_output.py | 75 ++++++++++++++++------------------------- scripts/lib/kdoc/kdoc_parser.py | 23 ++----------- 3 files changed, 43 insertions(+), 67 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_item.py b/scripts/lib/kdoc/kdoc_item.py index f0b2b9082c56..ccabb448b5d4 100644 --- a/scripts/lib/kdoc/kdoc_item.py +++ b/scripts/lib/kdoc/kdoc_item.py @@ -11,6 +11,10 @@ class KdocItem: self.declaration_start_line = start_line self.sections = {} self.sections_start_lines = {} + self.parameterlist = [] + self.parameterdesc_start_lines = [] + self.parameterdescs = {} + self.parametertypes = {} # # Just save everything else into our own dict so that the output # side can grab it directly as before. As we move things into more @@ -28,8 +32,14 @@ class KdocItem: return self.get(key) # - # Tracking of section information. + # Tracking of section and parameter information. # def set_sections(self, sections, start_lines): self.sections = sections self.section_start_lines = start_lines + + def set_params(self, names, descs, types, starts): + self.parameterlist = names + self.parameterdescs = descs + self.parametertypes = types + self.parameterdesc_start_lines = starts diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output.py index 15cb89f91987..d6f4d9e7173b 100644 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -373,18 +373,13 @@ class RestFormat(OutputFormat): signature = args['functiontype'] + " " signature += args['function'] + " (" - parameterlist = args.get('parameterlist', []) - parameterdescs = args.get('parameterdescs', {}) - parameterdesc_start_lines = args.get('parameterdesc_start_lines', {}) - ln = args.get('declaration_start_line', 0) - count = 0 - for parameter in parameterlist: + for parameter in args.parameterlist: if count != 0: signature += ", " count += 1 - dtype = args['parametertypes'].get(parameter, "") + dtype = args.parametertypes.get(parameter, "") if function_pointer.search(dtype): signature += function_pointer.group(1) + parameter + function_pointer.group(3) @@ -419,26 +414,26 @@ class RestFormat(OutputFormat): # function prototypes apart self.lineprefix = " " - if parameterlist: + if args.parameterlist: self.data += ".. container:: kernelindent\n\n" self.data += f"{self.lineprefix}**Parameters**\n\n" - for parameter in parameterlist: + for parameter in args.parameterlist: parameter_name = KernRe(r'\[.*').sub('', parameter) - dtype = args['parametertypes'].get(parameter, "") + dtype = args.parametertypes.get(parameter, "") if dtype: self.data += f"{self.lineprefix}``{dtype}``\n" else: self.data += f"{self.lineprefix}``{parameter}``\n" - self.print_lineno(parameterdesc_start_lines.get(parameter_name, 0)) + self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0)) self.lineprefix = " " - if parameter_name in parameterdescs and \ - parameterdescs[parameter_name] != KernelDoc.undescribed: + if parameter_name in args.parameterdescs and \ + args.parameterdescs[parameter_name] != KernelDoc.undescribed: - self.output_highlight(parameterdescs[parameter_name]) + self.output_highlight(args.parameterdescs[parameter_name]) self.data += "\n" else: self.data += f"{self.lineprefix}*undescribed*\n\n" @@ -451,8 +446,6 @@ class RestFormat(OutputFormat): oldprefix = self.lineprefix name = args.get('enum', '') - parameterlist = args.get('parameterlist', []) - parameterdescs = args.get('parameterdescs', {}) ln = args.get('declaration_start_line', 0) self.data += f"\n\n.. c:enum:: {name}\n\n" @@ -467,11 +460,11 @@ class RestFormat(OutputFormat): self.lineprefix = outer + " " self.data += f"{outer}**Constants**\n\n" - for parameter in parameterlist: + for parameter in args.parameterlist: self.data += f"{outer}``{parameter}``\n" - if parameterdescs.get(parameter, '') != KernelDoc.undescribed: - self.output_highlight(parameterdescs[parameter]) + if args.parameterdescs.get(parameter, '') != KernelDoc.undescribed: + self.output_highlight(args.parameterdescs[parameter]) else: self.data += f"{self.lineprefix}*undescribed*\n\n" self.data += "\n" @@ -505,10 +498,6 @@ class RestFormat(OutputFormat): dtype = args.get('type', "struct") ln = args.get('declaration_start_line', 0) - parameterlist = args.get('parameterlist', []) - parameterdescs = args.get('parameterdescs', {}) - parameterdesc_start_lines = args.get('parameterdesc_start_lines', {}) - self.data += f"\n\n.. c:{dtype}:: {name}\n\n" self.print_lineno(ln) @@ -531,21 +520,21 @@ class RestFormat(OutputFormat): self.lineprefix = " " self.data += f"{self.lineprefix}**Members**\n\n" - for parameter in parameterlist: + for parameter in args.parameterlist: if not parameter or parameter.startswith("#"): continue parameter_name = parameter.split("[", maxsplit=1)[0] - if parameterdescs.get(parameter_name) == KernelDoc.undescribed: + if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed: continue - self.print_lineno(parameterdesc_start_lines.get(parameter_name, 0)) + self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0)) self.data += f"{self.lineprefix}``{parameter}``\n" self.lineprefix = " " - self.output_highlight(parameterdescs[parameter_name]) + self.output_highlight(args.parameterdescs[parameter_name]) self.lineprefix = " " self.data += "\n" @@ -643,9 +632,6 @@ class ManFormat(OutputFormat): def out_function(self, fname, name, args): """output function in man""" - parameterlist = args.get('parameterlist', []) - parameterdescs = args.get('parameterdescs', {}) - self.data += f'.TH "{args["function"]}" 9 "{args["function"]}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n" self.data += ".SH NAME\n" @@ -661,11 +647,11 @@ class ManFormat(OutputFormat): parenth = "(" post = "," - for parameter in parameterlist: - if count == len(parameterlist) - 1: + for parameter in args.parameterlist: + if count == len(args.parameterlist) - 1: post = ");" - dtype = args['parametertypes'].get(parameter, "") + dtype = args.parametertypes.get(parameter, "") if function_pointer.match(dtype): # Pointer-to-function self.data += f'".BI "{parenth}{function_pointer.group(1)}" " ") ({function_pointer.group(2)}){post}"' + "\n" @@ -676,14 +662,14 @@ class ManFormat(OutputFormat): count += 1 parenth = "" - if parameterlist: + if args.parameterlist: self.data += ".SH ARGUMENTS\n" - for parameter in parameterlist: + for parameter in args.parameterlist: parameter_name = re.sub(r'\[.*', '', parameter) self.data += f'.IP "{parameter}" 12' + "\n" - self.output_highlight(parameterdescs.get(parameter_name, "")) + self.output_highlight(args.parameterdescs.get(parameter_name, "")) for section, text in args.sections.items(): self.data += f'.SH "{section.upper()}"' + "\n" @@ -692,7 +678,6 @@ class ManFormat(OutputFormat): def out_enum(self, fname, name, args): name = args.get('enum', '') - parameterlist = args.get('parameterlist', []) self.data += f'.TH "{self.modulename}" 9 "enum {args["enum"]}" "{self.man_date}" "API Manual" LINUX' + "\n" @@ -703,9 +688,9 @@ class ManFormat(OutputFormat): self.data += f"enum {args['enum']}" + " {\n" count = 0 - for parameter in parameterlist: + for parameter in args.parameterlist: self.data += f'.br\n.BI " {parameter}"' + "\n" - if count == len(parameterlist) - 1: + if count == len(args.parameterlist) - 1: self.data += "\n};\n" else: self.data += ", \n.br\n" @@ -714,10 +699,10 @@ class ManFormat(OutputFormat): self.data += ".SH Constants\n" - for parameter in parameterlist: + for parameter in args.parameterlist: parameter_name = KernRe(r'\[.*').sub('', parameter) self.data += f'.IP "{parameter}" 12' + "\n" - self.output_highlight(args['parameterdescs'].get(parameter_name, "")) + self.output_highlight(args.parameterdescs.get(parameter_name, "")) for section, text in args.sections.items(): self.data += f'.SH "{section}"' + "\n" @@ -743,8 +728,6 @@ class ManFormat(OutputFormat): struct_name = args.get('struct') purpose = args.get('purpose') definition = args.get('definition') - parameterlist = args.get('parameterlist', []) - parameterdescs = args.get('parameterdescs', {}) self.data += f'.TH "{module}" 9 "{struct_type} {struct_name}" "{self.man_date}" "API Manual" LINUX' + "\n" @@ -760,17 +743,17 @@ class ManFormat(OutputFormat): self.data += f'.BI "{declaration}\n' + "};\n.br\n\n" self.data += ".SH Members\n" - for parameter in parameterlist: + for parameter in args.parameterlist: if parameter.startswith("#"): continue parameter_name = re.sub(r"\[.*", "", parameter) - if parameterdescs.get(parameter_name) == KernelDoc.undescribed: + if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed: continue self.data += f'.IP "{parameter}" 12' + "\n" - self.output_highlight(parameterdescs.get(parameter_name)) + self.output_highlight(args.parameterdescs.get(parameter_name)) for section, text in args.sections.items(): self.data += f'.SH "{section}"' + "\n" diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index ffd49f9395ae..298abd260264 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -278,7 +278,9 @@ class KernelDoc: if section in sections and not sections[section].rstrip(): del sections[section] item.set_sections(sections, self.entry.section_start_lines) - + item.set_params(self.entry.parameterlist, self.entry.parameterdescs, + self.entry.parametertypes, + self.entry.parameterdesc_start_lines) self.entries.append(item) self.config.log.debug("Output: %s:%s = %s", dtype, name, pformat(args)) @@ -790,10 +792,6 @@ class KernelDoc: self.output_declaration(decl_type, declaration_name, struct=declaration_name, definition=declaration, - parameterlist=self.entry.parameterlist, - parameterdescs=self.entry.parameterdescs, - parametertypes=self.entry.parametertypes, - parameterdesc_start_lines=self.entry.parameterdesc_start_lines, purpose=self.entry.declaration_purpose) def dump_enum(self, ln, proto): @@ -873,9 +871,6 @@ class KernelDoc: self.output_declaration('enum', declaration_name, enum=declaration_name, - parameterlist=self.entry.parameterlist, - parameterdescs=self.entry.parameterdescs, - parameterdesc_start_lines=self.entry.parameterdesc_start_lines, purpose=self.entry.declaration_purpose) def dump_declaration(self, ln, prototype): @@ -1039,10 +1034,6 @@ class KernelDoc: function=declaration_name, typedef=True, functiontype=return_type, - parameterlist=self.entry.parameterlist, - parameterdescs=self.entry.parameterdescs, - parametertypes=self.entry.parametertypes, - parameterdesc_start_lines=self.entry.parameterdesc_start_lines, purpose=self.entry.declaration_purpose, func_macro=func_macro) else: @@ -1050,10 +1041,6 @@ class KernelDoc: function=declaration_name, typedef=False, functiontype=return_type, - parameterlist=self.entry.parameterlist, - parameterdescs=self.entry.parameterdescs, - parametertypes=self.entry.parametertypes, - parameterdesc_start_lines=self.entry.parameterdesc_start_lines, purpose=self.entry.declaration_purpose, func_macro=func_macro) @@ -1093,10 +1080,6 @@ class KernelDoc: function=declaration_name, typedef=True, functiontype=return_type, - parameterlist=self.entry.parameterlist, - parameterdescs=self.entry.parameterdescs, - parametertypes=self.entry.parametertypes, - parameterdesc_start_lines=self.entry.parameterdesc_start_lines, purpose=self.entry.declaration_purpose) return -- cgit v1.2.3 From a0db2051d7e1fca9a63a8643f1f187ff0b5931f1 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Wed, 2 Jul 2025 13:17:59 -0600 Subject: docs: kdoc: Regularize the use of the declaration name Each declaration type passes through the name in a unique field of the "args" blob - even though we have always just passed the name separately. Get rid of all the weird names and just use the common version. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_output.py | 39 +++++++++++++++------------------------ scripts/lib/kdoc/kdoc_parser.py | 6 ------ 2 files changed, 15 insertions(+), 30 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output.py index d6f4d9e7173b..8a31b637ffd2 100644 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -367,11 +367,11 @@ class RestFormat(OutputFormat): func_macro = args.get('func_macro', False) if func_macro: - signature = args['function'] + signature = name else: if args.get('functiontype'): signature = args['functiontype'] + " " - signature += args['function'] + " (" + signature += name + " (" ln = args.get('declaration_start_line', 0) count = 0 @@ -391,7 +391,7 @@ class RestFormat(OutputFormat): self.print_lineno(ln) if args.get('typedef') or not args.get('functiontype'): - self.data += f".. c:macro:: {args['function']}\n\n" + self.data += f".. c:macro:: {name}\n\n" if args.get('typedef'): self.data += " **Typedef**: " @@ -445,7 +445,6 @@ class RestFormat(OutputFormat): def out_enum(self, fname, name, args): oldprefix = self.lineprefix - name = args.get('enum', '') ln = args.get('declaration_start_line', 0) self.data += f"\n\n.. c:enum:: {name}\n\n" @@ -475,7 +474,6 @@ class RestFormat(OutputFormat): def out_typedef(self, fname, name, args): oldprefix = self.lineprefix - name = args.get('typedef', '') ln = args.get('declaration_start_line', 0) self.data += f"\n\n.. c:type:: {name}\n\n" @@ -492,7 +490,6 @@ class RestFormat(OutputFormat): def out_struct(self, fname, name, args): - name = args.get('struct', "") purpose = args.get('purpose', "") declaration = args.get('definition', "") dtype = args.get('type', "struct") @@ -632,16 +629,16 @@ class ManFormat(OutputFormat): def out_function(self, fname, name, args): """output function in man""" - self.data += f'.TH "{args["function"]}" 9 "{args["function"]}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n" + self.data += f'.TH "{name}" 9 "{name}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n" self.data += ".SH NAME\n" - self.data += f"{args['function']} \\- {args['purpose']}\n" + self.data += f"{name} \\- {args['purpose']}\n" self.data += ".SH SYNOPSIS\n" if args.get('functiontype', ''): - self.data += f'.B "{args["functiontype"]}" {args["function"]}' + "\n" + self.data += f'.B "{args["functiontype"]}" {name}' + "\n" else: - self.data += f'.B "{args["function"]}' + "\n" + self.data += f'.B "{name}' + "\n" count = 0 parenth = "(" @@ -676,16 +673,13 @@ class ManFormat(OutputFormat): self.output_highlight(text) def out_enum(self, fname, name, args): - - name = args.get('enum', '') - - self.data += f'.TH "{self.modulename}" 9 "enum {args["enum"]}" "{self.man_date}" "API Manual" LINUX' + "\n" + self.data += f'.TH "{self.modulename}" 9 "enum {name}" "{self.man_date}" "API Manual" LINUX' + "\n" self.data += ".SH NAME\n" - self.data += f"enum {args['enum']} \\- {args['purpose']}\n" + self.data += f"enum {name} \\- {args['purpose']}\n" self.data += ".SH SYNOPSIS\n" - self.data += f"enum {args['enum']}" + " {\n" + self.data += f"enum {name}" + " {\n" count = 0 for parameter in args.parameterlist: @@ -710,13 +704,12 @@ class ManFormat(OutputFormat): def out_typedef(self, fname, name, args): module = self.modulename - typedef = args.get('typedef') purpose = args.get('purpose') - self.data += f'.TH "{module}" 9 "{typedef}" "{self.man_date}" "API Manual" LINUX' + "\n" + self.data += f'.TH "{module}" 9 "{name}" "{self.man_date}" "API Manual" LINUX' + "\n" self.data += ".SH NAME\n" - self.data += f"typedef {typedef} \\- {purpose}\n" + self.data += f"typedef {name} \\- {purpose}\n" for section, text in args.sections.items(): self.data += f'.SH "{section}"' + "\n" @@ -724,22 +717,20 @@ class ManFormat(OutputFormat): def out_struct(self, fname, name, args): module = self.modulename - struct_type = args.get('type') - struct_name = args.get('struct') purpose = args.get('purpose') definition = args.get('definition') - self.data += f'.TH "{module}" 9 "{struct_type} {struct_name}" "{self.man_date}" "API Manual" LINUX' + "\n" + self.data += f'.TH "{module}" 9 "{args.type} {name}" "{self.man_date}" "API Manual" LINUX' + "\n" self.data += ".SH NAME\n" - self.data += f"{struct_type} {struct_name} \\- {purpose}\n" + self.data += f"{args.type} {name} \\- {purpose}\n" # Replace tabs with two spaces and handle newlines declaration = definition.replace("\t", " ") declaration = KernRe(r"\n").sub('"\n.br\n.BI "', declaration) self.data += ".SH SYNOPSIS\n" - self.data += f"{struct_type} {struct_name} " + "{" + "\n.br\n" + self.data += f"{args.type} {name} " + "{" + "\n.br\n" self.data += f'.BI "{declaration}\n' + "};\n.br\n\n" self.data += ".SH Members\n" diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 298abd260264..6e35e508608b 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -790,7 +790,6 @@ class KernelDoc: level += 1 self.output_declaration(decl_type, declaration_name, - struct=declaration_name, definition=declaration, purpose=self.entry.declaration_purpose) @@ -870,7 +869,6 @@ class KernelDoc: f"Excess enum value '%{k}' description in '{declaration_name}'") self.output_declaration('enum', declaration_name, - enum=declaration_name, purpose=self.entry.declaration_purpose) def dump_declaration(self, ln, prototype): @@ -1031,14 +1029,12 @@ class KernelDoc: if 'typedef' in return_type: self.output_declaration(decl_type, declaration_name, - function=declaration_name, typedef=True, functiontype=return_type, purpose=self.entry.declaration_purpose, func_macro=func_macro) else: self.output_declaration(decl_type, declaration_name, - function=declaration_name, typedef=False, functiontype=return_type, purpose=self.entry.declaration_purpose, @@ -1077,7 +1073,6 @@ class KernelDoc: self.create_parameter_list(ln, decl_type, args, ',', declaration_name) self.output_declaration(decl_type, declaration_name, - function=declaration_name, typedef=True, functiontype=return_type, purpose=self.entry.declaration_purpose) @@ -1099,7 +1094,6 @@ class KernelDoc: return self.output_declaration('typedef', declaration_name, - typedef=declaration_name, purpose=self.entry.declaration_purpose) return -- cgit v1.2.3 From 08b8dc43d18d5d0c4791cc630d5cddf98eaa51ea Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Wed, 2 Jul 2025 13:34:40 -0600 Subject: docs: kdoc: straighten up dump_declaration() Get rid of the excess "return" statements in dump_declaration(), along with a line of never-executed dead code. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 6e35e508608b..7191fa94e17a 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -878,18 +878,13 @@ class KernelDoc: if self.entry.decl_type == "enum": self.dump_enum(ln, prototype) - return - - if self.entry.decl_type == "typedef": + elif self.entry.decl_type == "typedef": self.dump_typedef(ln, prototype) - return - - if self.entry.decl_type in ["union", "struct"]: + elif self.entry.decl_type in ["union", "struct"]: self.dump_struct(ln, prototype) - return - - self.output_declaration(self.entry.decl_type, prototype, - entry=self.entry) + else: + # This would be a bug + self.emit_message(ln, f'Unknown declaration type: {self.entry.decl_type}') def dump_function(self, ln, prototype): """ -- cgit v1.2.3 From bd5628bf60abd6f283d1fd8bdcff45c272971c6b Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Wed, 2 Jul 2025 13:55:56 -0600 Subject: docs: kdoc: directly access the always-there KdocItem fields They are part of the interface, so use them directly. This allows the removal of the transitional __dict__ hack in KdocItem. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_item.py | 5 +---- scripts/lib/kdoc/kdoc_output.py | 16 +++++++--------- 2 files changed, 8 insertions(+), 13 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_item.py b/scripts/lib/kdoc/kdoc_item.py index ccabb448b5d4..b3b225764550 100644 --- a/scripts/lib/kdoc/kdoc_item.py +++ b/scripts/lib/kdoc/kdoc_item.py @@ -23,10 +23,7 @@ class KdocItem: self.other_stuff = other_stuff def get(self, key, default = None): - ret = self.other_stuff.get(key, default) - if ret == default: - return self.__dict__.get(key, default) - return ret + return self.other_stuff.get(key, default) def __getitem__(self, key): return self.get(key) diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output.py index 8a31b637ffd2..ea8914537ba0 100644 --- a/scripts/lib/kdoc/kdoc_output.py +++ b/scripts/lib/kdoc/kdoc_output.py @@ -124,9 +124,7 @@ class OutputFormat: Output warnings for identifiers that will be displayed. """ - warnings = args.get('warnings', []) - - for log_msg in warnings: + for log_msg in args.warnings: self.config.warning(log_msg) def check_doc(self, name, args): @@ -184,7 +182,7 @@ class OutputFormat: self.data = "" - dtype = args.get('type', "") + dtype = args.type if dtype == "doc": self.out_doc(fname, name, args) @@ -373,7 +371,7 @@ class RestFormat(OutputFormat): signature = args['functiontype'] + " " signature += name + " (" - ln = args.get('declaration_start_line', 0) + ln = args.declaration_start_line count = 0 for parameter in args.parameterlist: if count != 0: @@ -445,7 +443,7 @@ class RestFormat(OutputFormat): def out_enum(self, fname, name, args): oldprefix = self.lineprefix - ln = args.get('declaration_start_line', 0) + ln = args.declaration_start_line self.data += f"\n\n.. c:enum:: {name}\n\n" @@ -474,7 +472,7 @@ class RestFormat(OutputFormat): def out_typedef(self, fname, name, args): oldprefix = self.lineprefix - ln = args.get('declaration_start_line', 0) + ln = args.declaration_start_line self.data += f"\n\n.. c:type:: {name}\n\n" @@ -492,8 +490,8 @@ class RestFormat(OutputFormat): purpose = args.get('purpose', "") declaration = args.get('definition', "") - dtype = args.get('type', "struct") - ln = args.get('declaration_start_line', 0) + dtype = args.type + ln = args.declaration_start_line self.data += f"\n\n.. c:{dtype}:: {name}\n\n" -- cgit v1.2.3 From 636d4d9ec641025b98e8df4623a77ecc09026209 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Wed, 2 Jul 2025 14:53:32 -0600 Subject: docs: kdoc: clean up check_sections() entry.sectcheck is just a duplicate of our list of sections that is only passed to check_sections(); its main purpose seems to be to avoid checking the special named sections. Rework check_sections() to not use that field (which is then deleted), tocheck for the known sections directly, and tighten up the logic in general. Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 7191fa94e17a..fdde14b045fe 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -42,9 +42,11 @@ doc_decl = doc_com + KernRe(r'(\w+)', cache=False) # @{section-name}: # while trying to not match literal block starts like "example::" # +known_section_names = 'description|context|returns?|notes?|examples?' +known_sections = KernRe(known_section_names, flags = re.I) doc_sect = doc_com + \ - KernRe(r'\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:([^:].*)?$', - flags=re.I, cache=False) + KernRe(r'\s*(\@[.\w]+|\@\.\.\.|' + known_section_names + r')\s*:([^:].*)?$', + flags=re.I, cache=False) doc_content = doc_com_body + KernRe(r'(.*)', cache=False) doc_inline_start = KernRe(r'^\s*/\*\*\s*$', cache=False) @@ -115,7 +117,6 @@ class KernelEntry: self.config = config self._contents = [] - self.sectcheck = "" self.prototype = "" self.warnings = [] @@ -187,7 +188,6 @@ class KernelEntry: self.parameterdescs[name] = contents self.parameterdesc_start_lines[name] = self.new_start_line - self.sectcheck += name + " " self.new_start_line = 0 else: @@ -478,29 +478,20 @@ class KernelDoc: self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name) - def check_sections(self, ln, decl_name, decl_type, sectcheck): + def check_sections(self, ln, decl_name, decl_type): """ Check for errors inside sections, emitting warnings if not found parameters are described. """ - - sects = sectcheck.split() - - for sx in range(len(sects)): # pylint: disable=C0200 - err = True - for param in self.entry.parameterlist: - if param == sects[sx]: - err = False - break - - if err: + for section in self.entry.sections: + if section not in self.entry.parameterlist and \ + not known_sections.search(section): if decl_type == 'function': dname = f"{decl_type} parameter" else: dname = f"{decl_type} member" - self.emit_msg(ln, - f"Excess {dname} '{sects[sx]}' description in '{decl_name}'") + f"Excess {dname} '{section}' description in '{decl_name}'") def check_return_section(self, ln, declaration_name, return_type): """ @@ -754,7 +745,7 @@ class KernelDoc: self.create_parameter_list(ln, decl_type, members, ';', declaration_name) - self.check_sections(ln, declaration_name, decl_type, self.entry.sectcheck) + self.check_sections(ln, declaration_name, decl_type) # Adjust declaration for better display declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration) @@ -1018,7 +1009,7 @@ class KernelDoc: f"expecting prototype for {self.entry.identifier}(). Prototype was for {declaration_name}() instead") return - self.check_sections(ln, declaration_name, "function", self.entry.sectcheck) + self.check_sections(ln, declaration_name, "function") self.check_return_section(ln, declaration_name, return_type) -- cgit v1.2.3 From 40020fe8e3a4038ed6fb4b3115ad4c60fd354ab3 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 10 Jul 2025 17:24:07 -0600 Subject: docs: kdoc: emit a warning for ancient versions of Python Versions of Python prior to 3.7 do not guarantee to remember the insertion order of dicts; since kernel-doc depends on that guarantee, running with such older versions could result in output with reordered sections. Python 3.9 is the minimum for the kernel as a whole, so this should not be a problem, but put in a warning just in case somebody tries to use something older. Suggested-by: Mauro Carvalho Chehab Reviewed-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet --- scripts/lib/kdoc/kdoc_parser.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index fdde14b045fe..06f55f38d4a7 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -12,6 +12,7 @@ Read a C language source or header FILE and extract embedded documentation comments """ +import sys import re from pprint import pformat @@ -238,6 +239,14 @@ class KernelDoc: # Place all potential outputs into an array self.entries = [] + # + # We need Python 3.7 for its "dicts remember the insertion + # order" guarantee + # + if sys.version_info.major == 3 and sys.version_info.minor < 7: + self.emit_msg(0, + 'Python 3.7 or later is required for correct results') + def emit_msg(self, ln, msg, warning=True): """Emit a message""" -- cgit v1.2.3 From 2a73ebf267fe26fb1fb5c8f085be986dfe9faf83 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 1 Jul 2025 17:23:30 -0700 Subject: checkpatch: Remove SRCU-lite deprecation Now that SRCU-lite has been removed from the kernel, let's remove the now-redundant deprecation from checkpatch.pl. Signed-off-by: "Paul E. McKenney" Signed-off-by: Neeraj Upadhyay (AMD) --- scripts/checkpatch.pl | 2 -- 1 file changed, 2 deletions(-) (limited to 'scripts') diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl index 664f7b7a622c..867c7b6fd839 100755 --- a/scripts/checkpatch.pl +++ b/scripts/checkpatch.pl @@ -857,8 +857,6 @@ our %deprecated_apis = ( "kunmap" => "kunmap_local", "kmap_atomic" => "kmap_local_page", "kunmap_atomic" => "kunmap_local", - "srcu_read_lock_lite" => "srcu_read_lock_fast", - "srcu_read_unlock_lite" => "srcu_read_unlock_fast", ); #Create a search pattern for all these strings to speed up a loop below -- cgit v1.2.3 From 7740f9dbe2a96f6e7eb138dbdcb414aa78e83c10 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 11 Jul 2025 09:27:08 +0200 Subject: docs: kernel-doc: emit warnings for ancient versions of Python Kernel-doc requires at least version 3.6 to run, as it uses f-string. Yet, Kernel build currently calls kernel-doc with -none on some places. Better not to bail out when older versions are found. Versions of Python prior to 3.7 do not guarantee to remember the insertion order of dicts; since kernel-doc depends on that guarantee, running with such older versions could result in output with reordered sections. Check Python version when called via command line. Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/7d7fa3a3aa1fafa0cc9ea29c889de4c7d377dca6.1752218291.git.mchehab+huawei@kernel.org --- scripts/kernel-doc.py | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'scripts') diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py index 12ae66f40bd7..fc3d46ef519f 100755 --- a/scripts/kernel-doc.py +++ b/scripts/kernel-doc.py @@ -271,6 +271,16 @@ def main(): logger.addHandler(handler) + python_ver = sys.version_info[:2] + if python_ver < (3,6): + logger.warning("Python 3.6 or later is required by kernel-doc") + + # Return 0 here to avoid breaking compilation + sys.exit(0) + + if python_ver < (3,7): + logger.warning("Python 3.7 or later is required for correct results") + if args.man: out_style = ManFormat(modulename=args.modulename) elif args.none: -- cgit v1.2.3 From 39e39af70d066029c788800ee07e0491e07eb081 Mon Sep 17 00:00:00 2001 From: Mauro Carvalho Chehab Date: Fri, 11 Jul 2025 09:27:09 +0200 Subject: scripts: kdoc: make it backward-compatible with Python 3.7 There was a change at kdoc that ended breaking compatibility with Python 3.7: str.removesuffix() was introduced on version 3.9. Restore backward compatibility. Reported-by: Akira Yokosawa Closes: https://lore.kernel.org/linux-doc/57be9f77-9a94-4cde-aacb-184cae111506@gmail.com/ Fixes: 27ad33b6b349 ("kernel-doc: Fix symbol matching for dropped suffixes") Signed-off-by: Mauro Carvalho Chehab Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/d13058d285838ac2bc04c492e60531c013a8a919.1752218291.git.mchehab+huawei@kernel.org --- scripts/lib/kdoc/kdoc_parser.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py index 06f55f38d4a7..c3fe4bd5eab4 100644 --- a/scripts/lib/kdoc/kdoc_parser.py +++ b/scripts/lib/kdoc/kdoc_parser.py @@ -1120,7 +1120,9 @@ class KernelDoc: # Found an export, trim out any special suffixes # for suffix in suffixes: - symbol = symbol.removesuffix(suffix) + # Be backward compatible with Python < 3.9 + if symbol.endswith(suffix): + symbol = symbol[:-len(suffix)] function_set.add(symbol) return True -- cgit v1.2.3 From 5d840b4c4935cd5100be97b6df565b4b159970a5 Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Wed, 9 Jul 2025 15:31:15 -0400 Subject: rust: list: add `impl_list_item!` examples There's a comprehensive example in `rust/kernel/list.rs` but it doesn't exercise the `using ListLinksSelfPtr` variant nor the generic cases. Add that here. Generalize `impl_has_list_links_self_ptr` to handle nested fields in the same manner as `impl_has_list_links`. Tested-by: Alice Ryhl Reviewed-by: Alice Ryhl Signed-off-by: Tamir Duberstein Link: https://lore.kernel.org/r/20250709-list-no-offset-v4-5-a429e75840a9@gmail.com [ Fixed Rust < 1.82 build by enabling the `offset_of_nested` feature. - Miguel ] Signed-off-by: Miguel Ojeda --- rust/kernel/list/impl_list_item_mod.rs | 96 ++++++++++++++++++++++++++++++++-- scripts/Makefile.build | 5 +- 2 files changed, 96 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/rust/kernel/list/impl_list_item_mod.rs b/rust/kernel/list/impl_list_item_mod.rs index 6717b614e896..374b3dd812d0 100644 --- a/rust/kernel/list/impl_list_item_mod.rs +++ b/rust/kernel/list/impl_list_item_mod.rs @@ -82,20 +82,20 @@ macro_rules! impl_has_list_links_self_ptr { ($(impl$({$($generics:tt)*})? HasSelfPtr<$item_type:ty $(, $id:tt)?> for $self:ty - { self.$field:ident } + { self$(.$field:ident)* } )*) => {$( // SAFETY: The implementation of `raw_get_list_links` only compiles if the field has the // right type. unsafe impl$(<$($generics)*>)? $crate::list::HasSelfPtr<$item_type $(, $id)?> for $self {} unsafe impl$(<$($generics)*>)? $crate::list::HasListLinks$(<$id>)? for $self { - const OFFSET: usize = ::core::mem::offset_of!(Self, $field) as usize; + const OFFSET: usize = ::core::mem::offset_of!(Self, $($field).*) as usize; #[inline] unsafe fn raw_get_list_links(ptr: *mut Self) -> *mut $crate::list::ListLinks$(<$id>)? { // SAFETY: The caller promises that the pointer is not dangling. let ptr: *mut $crate::list::ListLinksSelfPtr<$item_type $(, $id)?> = - unsafe { ::core::ptr::addr_of_mut!((*ptr).$field) }; + unsafe { ::core::ptr::addr_of_mut!((*ptr)$(.$field)*) }; ptr.cast() } } @@ -109,6 +109,96 @@ pub use impl_has_list_links_self_ptr; /// implement that trait. /// /// [`ListItem`]: crate::list::ListItem +/// +/// # Examples +/// +/// ``` +/// #[pin_data] +/// struct SimpleListItem { +/// value: u32, +/// #[pin] +/// links: kernel::list::ListLinks, +/// } +/// +/// kernel::list::impl_has_list_links! { +/// impl HasListLinks<0> for SimpleListItem { self.links } +/// } +/// +/// kernel::list::impl_list_arc_safe! { +/// impl ListArcSafe<0> for SimpleListItem { untracked; } +/// } +/// +/// kernel::list::impl_list_item! { +/// impl ListItem<0> for SimpleListItem { using ListLinks; } +/// } +/// +/// struct ListLinksHolder { +/// inner: kernel::list::ListLinks, +/// } +/// +/// #[pin_data] +/// struct ComplexListItem { +/// value: Result, +/// #[pin] +/// links: ListLinksHolder, +/// } +/// +/// kernel::list::impl_has_list_links! { +/// impl{T, U} HasListLinks<0> for ComplexListItem { self.links.inner } +/// } +/// +/// kernel::list::impl_list_arc_safe! { +/// impl{T, U} ListArcSafe<0> for ComplexListItem { untracked; } +/// } +/// +/// kernel::list::impl_list_item! { +/// impl{T, U} ListItem<0> for ComplexListItem { using ListLinks; } +/// } +/// ``` +/// +/// ``` +/// #[pin_data] +/// struct SimpleListItem { +/// value: u32, +/// #[pin] +/// links: kernel::list::ListLinksSelfPtr, +/// } +/// +/// kernel::list::impl_list_arc_safe! { +/// impl ListArcSafe<0> for SimpleListItem { untracked; } +/// } +/// +/// kernel::list::impl_has_list_links_self_ptr! { +/// impl HasSelfPtr for SimpleListItem { self.links } +/// } +/// +/// kernel::list::impl_list_item! { +/// impl ListItem<0> for SimpleListItem { using ListLinksSelfPtr; } +/// } +/// +/// struct ListLinksSelfPtrHolder { +/// inner: kernel::list::ListLinksSelfPtr>, +/// } +/// +/// #[pin_data] +/// struct ComplexListItem { +/// value: Result, +/// #[pin] +/// links: ListLinksSelfPtrHolder, +/// } +/// +/// kernel::list::impl_list_arc_safe! { +/// impl{T, U} ListArcSafe<0> for ComplexListItem { untracked; } +/// } +/// +/// kernel::list::impl_has_list_links_self_ptr! { +/// impl{T, U} HasSelfPtr> for ComplexListItem { self.links.inner } +/// } +/// +/// kernel::list::impl_list_item! { +/// impl{T, U} ListItem<0> for ComplexListItem { using ListLinksSelfPtr; } +/// } +/// ``` #[macro_export] macro_rules! impl_list_item { ( diff --git a/scripts/Makefile.build b/scripts/Makefile.build index a6461ea411f7..79c40af6f399 100644 --- a/scripts/Makefile.build +++ b/scripts/Makefile.build @@ -309,13 +309,14 @@ $(obj)/%.lst: $(obj)/%.c FORCE # The features in this list are the ones allowed for non-`rust/` code. # # - Stable since Rust 1.81.0: `feature(lint_reasons)`. -# - Stable since Rust 1.82.0: `feature(asm_const)`, `feature(raw_ref_op)`. +# - Stable since Rust 1.82.0: `feature(asm_const)`, +# `feature(offset_of_nested)`, `feature(raw_ref_op)`. # - Stable since Rust 1.87.0: `feature(asm_goto)`. # - Expected to become stable: `feature(arbitrary_self_types)`. # # Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on # the unstable features in use. -rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,raw_ref_op +rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,offset_of_nested,raw_ref_op # `--out-dir` is required to avoid temporaries being created by `rustc` in the # current working directory, which may be not accessible in the out-of-tree -- cgit v1.2.3 From 249e7ced7271d8a7e733b1fe7ea7a221eb46698f Mon Sep 17 00:00:00 2001 From: Easwar Hariharan Date: Thu, 3 Jul 2025 15:51:32 -0700 Subject: coccinelle: misc: secs_to_jiffies: implement context and report modes As requested by Ricardo and Jakub, implement report and context modes for the secs_to_jiffies Coccinelle script. While here, add the option to look for opportunities to use secs_to_jiffies() in headers. Link: https://lkml.kernel.org/r/20250703225145.152288-1-eahariha@linux.microsoft.com Signed-off-by: Easwar Hariharan Closes: https://lore.kernel.org/all/20250129-secs_to_jiffles-v1-1-35a5e16b9f03@chromium.org/ Closes: https://lore.kernel.org/all/20250221162107.409ae333@kernel.org/ Tested-by: Ricardo Ribalda Cc: Julia Lawall Cc: Nicolas Palix Cc: Jakub Kicinski Cc: Ricardo Ribalda Signed-off-by: Andrew Morton --- scripts/coccinelle/misc/secs_to_jiffies.cocci | 49 ++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/coccinelle/misc/secs_to_jiffies.cocci b/scripts/coccinelle/misc/secs_to_jiffies.cocci index 416f348174ca..f3241ce75a7b 100644 --- a/scripts/coccinelle/misc/secs_to_jiffies.cocci +++ b/scripts/coccinelle/misc/secs_to_jiffies.cocci @@ -7,26 +7,65 @@ // Confidence: High // Copyright: (C) 2024 Easwar Hariharan, Microsoft // Keywords: secs, seconds, jiffies -// +// Options: --include-headers virtual patch +virtual report +virtual context -@depends on patch@ constant C; @@ +@pconst depends on patch@ constant C; @@ - msecs_to_jiffies(C * 1000) + secs_to_jiffies(C) -@depends on patch@ constant C; @@ +@pconstms depends on patch@ constant C; @@ - msecs_to_jiffies(C * MSEC_PER_SEC) + secs_to_jiffies(C) -@depends on patch@ expression E; @@ +@pexpr depends on patch@ expression E; @@ - msecs_to_jiffies(E * 1000) + secs_to_jiffies(E) -@depends on patch@ expression E; @@ +@pexprms depends on patch@ expression E; @@ - msecs_to_jiffies(E * MSEC_PER_SEC) + secs_to_jiffies(E) + +@r depends on report && !patch@ +constant C; +expression E; +position p; +@@ + +( + msecs_to_jiffies(C@p * 1000) +| + msecs_to_jiffies(C@p * MSEC_PER_SEC) +| + msecs_to_jiffies(E@p * 1000) +| + msecs_to_jiffies(E@p * MSEC_PER_SEC) +) + +@c depends on context && !patch@ +constant C; +expression E; +@@ + +( +* msecs_to_jiffies(C * 1000) +| +* msecs_to_jiffies(C * MSEC_PER_SEC) +| +* msecs_to_jiffies(E * 1000) +| +* msecs_to_jiffies(E * MSEC_PER_SEC) +) + +@script:python depends on report@ +p << r.p; +@@ + +coccilib.report.print_report(p[0], "WARNING opportunity for secs_to_jiffies()") -- cgit v1.2.3 From 8b097b5ac68b0fd2a7a251e101a84175b2ed585d Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Thu, 29 May 2025 09:14:58 -0400 Subject: scripts: rust: replace length checks with match Use a match expression with slice patterns instead of length checks and indexing. The result is more idiomatic, which is a better example for future Rust code authors. Reviewed-by: Alice Ryhl Signed-off-by: Tamir Duberstein Link: https://lore.kernel.org/r/20250529-idiomatic-match-slice-v2-1-4925ca2f1550@gmail.com [ Reworded title. - Miguel ] Signed-off-by: Miguel Ojeda --- scripts/rustdoc_test_gen.rs | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) (limited to 'scripts') diff --git a/scripts/rustdoc_test_gen.rs b/scripts/rustdoc_test_gen.rs index 1ca253594d38..d796481f4359 100644 --- a/scripts/rustdoc_test_gen.rs +++ b/scripts/rustdoc_test_gen.rs @@ -85,24 +85,23 @@ fn find_real_path<'a>(srctree: &Path, valid_paths: &'a mut Vec, file: & } } - assert!( - valid_paths.len() > 0, - "No path candidates found for `{file}`. This is likely a bug in the build system, or some \ - files went away while compiling." - ); - - if valid_paths.len() > 1 { - eprintln!("Several path candidates found:"); - for path in valid_paths { - eprintln!(" {path:?}"); + match valid_paths.as_slice() { + [] => panic!( + "No path candidates found for `{file}`. This is likely a bug in the build system, or \ + some files went away while compiling." + ), + [valid_path] => valid_path.to_str().unwrap(), + valid_paths => { + eprintln!("Several path candidates found:"); + for path in valid_paths { + eprintln!(" {path:?}"); + } + panic!( + "Several path candidates found for `{file}`, please resolve the ambiguity by \ + renaming a file or folder." + ); } - panic!( - "Several path candidates found for `{file}`, please resolve the ambiguity by renaming \ - a file or folder." - ); } - - valid_paths[0].to_str().unwrap() } fn main() { -- cgit v1.2.3 From 2254991d5b573662f841998c1d263118a15f067a Mon Sep 17 00:00:00 2001 From: Tamir Duberstein Date: Thu, 29 May 2025 09:14:59 -0400 Subject: scripts: rust: emit path candidates in panic message Include all information in the panic message rather than emit fragments to stderr to avoid possible interleaving with other output. Signed-off-by: Tamir Duberstein Link: https://lore.kernel.org/r/20250529-idiomatic-match-slice-v2-2-4925ca2f1550@gmail.com [ Kept newlines using `writeln!`. Used new message from Tamir. Reworded title. - Miguel ] Signed-off-by: Miguel Ojeda --- scripts/rustdoc_test_gen.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/rustdoc_test_gen.rs b/scripts/rustdoc_test_gen.rs index d796481f4359..abb34ada2508 100644 --- a/scripts/rustdoc_test_gen.rs +++ b/scripts/rustdoc_test_gen.rs @@ -92,13 +92,15 @@ fn find_real_path<'a>(srctree: &Path, valid_paths: &'a mut Vec, file: & ), [valid_path] => valid_path.to_str().unwrap(), valid_paths => { - eprintln!("Several path candidates found:"); + use std::fmt::Write; + + let mut candidates = String::new(); for path in valid_paths { - eprintln!(" {path:?}"); + writeln!(&mut candidates, " {path:?}").unwrap(); } panic!( "Several path candidates found for `{file}`, please resolve the ambiguity by \ - renaming a file or folder." + renaming a file or folder. Candidates:\n{candidates}", ); } } -- cgit v1.2.3 From 57fbad15c2eee77276a541c616589b32976d2b8e Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 17 Jul 2025 16:25:06 -0700 Subject: stackleak: Rename STACKLEAK to KSTACK_ERASE In preparation for adding Clang sanitizer coverage stack depth tracking that can support stack depth callbacks: - Add the new top-level CONFIG_KSTACK_ERASE option which will be implemented either with the stackleak GCC plugin, or with the Clang stack depth callback support. - Rename CONFIG_GCC_PLUGIN_STACKLEAK as needed to CONFIG_KSTACK_ERASE, but keep it for anything specific to the GCC plugin itself. - Rename all exposed "STACKLEAK" names and files to "KSTACK_ERASE" (named for what it does rather than what it protects against), but leave as many of the internals alone as possible to avoid even more churn. While here, also split "prev_lowest_stack" into CONFIG_KSTACK_ERASE_METRICS, since that's the only place it is referenced from. Suggested-by: Ingo Molnar Link: https://lore.kernel.org/r/20250717232519.2984886-1-kees@kernel.org Signed-off-by: Kees Cook --- Documentation/admin-guide/sysctl/kernel.rst | 4 +- Documentation/arch/x86/x86_64/mm.rst | 2 +- Documentation/security/self-protection.rst | 2 +- .../zh_CN/security/self-protection.rst | 2 +- MAINTAINERS | 4 +- arch/Kconfig | 4 +- arch/arm/Kconfig | 2 +- arch/arm/boot/compressed/Makefile | 2 +- arch/arm/kernel/entry-common.S | 2 +- arch/arm64/Kconfig | 2 +- arch/arm64/kernel/entry.S | 2 +- arch/arm64/kernel/pi/Makefile | 2 +- arch/arm64/kvm/hyp/nvhe/Makefile | 2 +- arch/loongarch/Kconfig | 2 +- arch/riscv/Kconfig | 2 +- arch/riscv/kernel/entry.S | 2 +- arch/riscv/kernel/pi/Makefile | 2 +- arch/riscv/purgatory/Makefile | 2 +- arch/s390/Kconfig | 2 +- arch/s390/kernel/entry.S | 2 +- arch/x86/Kconfig | 2 +- arch/x86/entry/calling.h | 4 +- arch/x86/purgatory/Makefile | 2 +- drivers/firmware/efi/libstub/Makefile | 8 +- drivers/misc/lkdtm/Makefile | 2 +- drivers/misc/lkdtm/kstack_erase.c | 150 +++++++++++++++++ drivers/misc/lkdtm/stackleak.c | 150 ----------------- fs/proc/base.c | 6 +- include/linux/kstack_erase.h | 89 +++++++++++ include/linux/sched.h | 4 +- include/linux/stackleak.h | 89 ----------- kernel/Makefile | 10 +- kernel/fork.c | 2 +- kernel/kstack_erase.c | 177 +++++++++++++++++++++ kernel/stackleak.c | 177 --------------------- lib/Makefile | 2 +- scripts/Makefile.gcc-plugins | 6 +- security/Kconfig.hardening | 36 +++-- tools/objtool/check.c | 2 +- tools/testing/selftests/lkdtm/config | 2 +- 40 files changed, 486 insertions(+), 480 deletions(-) create mode 100644 drivers/misc/lkdtm/kstack_erase.c delete mode 100644 drivers/misc/lkdtm/stackleak.c create mode 100644 include/linux/kstack_erase.h delete mode 100644 include/linux/stackleak.h create mode 100644 kernel/kstack_erase.c delete mode 100644 kernel/stackleak.c (limited to 'scripts') diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index dd49a89a62d3..19224eeac1c2 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -1465,7 +1465,7 @@ stack_erasing ============= This parameter can be used to control kernel stack erasing at the end -of syscalls for kernels built with ``CONFIG_GCC_PLUGIN_STACKLEAK``. +of syscalls for kernels built with ``CONFIG_KSTACK_ERASE``. That erasing reduces the information which kernel stack leak bugs can reveal and blocks some uninitialized stack variable attacks. @@ -1473,7 +1473,7 @@ The tradeoff is the performance impact: on a single CPU system kernel compilation sees a 1% slowdown, other systems and workloads may vary. = ==================================================================== -0 Kernel stack erasing is disabled, STACKLEAK_METRICS are not updated. +0 Kernel stack erasing is disabled, KSTACK_ERASE_METRICS are not updated. 1 Kernel stack erasing is enabled (default), it is performed before returning to the userspace at the end of syscalls. = ==================================================================== diff --git a/Documentation/arch/x86/x86_64/mm.rst b/Documentation/arch/x86/x86_64/mm.rst index f2db178b353f..a6cf05d51bd8 100644 --- a/Documentation/arch/x86/x86_64/mm.rst +++ b/Documentation/arch/x86/x86_64/mm.rst @@ -176,5 +176,5 @@ Be very careful vs. KASLR when changing anything here. The KASLR address range must not overlap with anything except the KASAN shadow area, which is correct as KASAN disables KASLR. -For both 4- and 5-level layouts, the STACKLEAK_POISON value in the last 2MB +For both 4- and 5-level layouts, the KSTACK_ERASE_POISON value in the last 2MB hole: ffffffffffff4111 diff --git a/Documentation/security/self-protection.rst b/Documentation/security/self-protection.rst index 910668e665cb..a32ca23c21b0 100644 --- a/Documentation/security/self-protection.rst +++ b/Documentation/security/self-protection.rst @@ -303,7 +303,7 @@ Memory poisoning When releasing memory, it is best to poison the contents, to avoid reuse attacks that rely on the old contents of memory. E.g., clear stack on a -syscall return (``CONFIG_GCC_PLUGIN_STACKLEAK``), wipe heap memory on a +syscall return (``CONFIG_KSTACK_ERASE``), wipe heap memory on a free. This frustrates many uninitialized variable attacks, stack content exposures, heap content exposures, and use-after-free attacks. diff --git a/Documentation/translations/zh_CN/security/self-protection.rst b/Documentation/translations/zh_CN/security/self-protection.rst index 3c8a68b1e1be..93de9cee5c1a 100644 --- a/Documentation/translations/zh_CN/security/self-protection.rst +++ b/Documentation/translations/zh_CN/security/self-protection.rst @@ -259,7 +259,7 @@ KALLSYSM,则会直接打印原始地址。 -------- 在释放内存时,最好对内存内容进行清除处理,以防止攻击者重用内存中以前 -的内容。例如,在系统调用返回时清除堆栈(CONFIG_GCC_PLUGIN_STACKLEAK), +的内容。例如,在系统调用返回时清除堆栈(CONFIG_KSTACK_ERASE), 在释放堆内容是清除其内容。这有助于防止许多未初始化变量攻击、堆栈内容 泄露、堆内容泄露以及使用后释放攻击(user-after-free)。 diff --git a/MAINTAINERS b/MAINTAINERS index 0c1d245bf7b8..470d159d8fea 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -9992,8 +9992,6 @@ L: linux-hardening@vger.kernel.org S: Maintained T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/hardening F: Documentation/kbuild/gcc-plugins.rst -F: include/linux/stackleak.h -F: kernel/stackleak.c F: scripts/Makefile.gcc-plugins F: scripts/gcc-plugins/ @@ -13087,10 +13085,12 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/kees/linux.git for-next/har F: Documentation/ABI/testing/sysfs-kernel-oops_count F: Documentation/ABI/testing/sysfs-kernel-warn_count F: arch/*/configs/hardening.config +F: include/linux/kstack_erase.h F: include/linux/overflow.h F: include/linux/randomize_kstack.h F: include/linux/ucopysize.h F: kernel/configs/hardening.config +F: kernel/kstack_erase.c F: lib/tests/randstruct_kunit.c F: lib/tests/usercopy_kunit.c F: mm/usercopy.c diff --git a/arch/Kconfig b/arch/Kconfig index a3308a220f86..4d1908f6f084 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -630,11 +630,11 @@ config SECCOMP_CACHE_DEBUG If unsure, say N. -config HAVE_ARCH_STACKLEAK +config HAVE_ARCH_KSTACK_ERASE bool help An architecture should select this if it has the code which - fills the used part of the kernel stack with the STACKLEAK_POISON + fills the used part of the kernel stack with the KSTACK_ERASE_POISON value before returning from system calls. config HAVE_STACKPROTECTOR diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 3072731fe09c..cb0b2e2211ca 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -87,11 +87,11 @@ config ARM select HAVE_ARCH_KGDB if !CPU_ENDIAN_BE32 && MMU select HAVE_ARCH_KASAN if MMU && !XIP_KERNEL select HAVE_ARCH_KASAN_VMALLOC if HAVE_ARCH_KASAN + select HAVE_ARCH_KSTACK_ERASE select HAVE_ARCH_MMAP_RND_BITS if MMU select HAVE_ARCH_PFN_VALID select HAVE_ARCH_SECCOMP select HAVE_ARCH_SECCOMP_FILTER if AEABI && !OABI_COMPAT - select HAVE_ARCH_STACKLEAK select HAVE_ARCH_THREAD_STRUCT_WHITELIST select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRANSPARENT_HUGEPAGE if ARM_LPAE diff --git a/arch/arm/boot/compressed/Makefile b/arch/arm/boot/compressed/Makefile index d61369b1eabe..f9075edfd773 100644 --- a/arch/arm/boot/compressed/Makefile +++ b/arch/arm/boot/compressed/Makefile @@ -9,7 +9,7 @@ OBJS = HEAD = head.o OBJS += misc.o decompress.o -CFLAGS_decompress.o += $(DISABLE_STACKLEAK_PLUGIN) +CFLAGS_decompress.o += $(DISABLE_KSTACK_ERASE) ifeq ($(CONFIG_DEBUG_UNCOMPRESS),y) OBJS += debug.o AFLAGS_head.o += -DDEBUG diff --git a/arch/arm/kernel/entry-common.S b/arch/arm/kernel/entry-common.S index f379c852dcb7..88336a1292bb 100644 --- a/arch/arm/kernel/entry-common.S +++ b/arch/arm/kernel/entry-common.S @@ -119,7 +119,7 @@ no_work_pending: ct_user_enter save = 0 -#ifdef CONFIG_GCC_PLUGIN_STACKLEAK +#ifdef CONFIG_KSTACK_ERASE bl stackleak_erase_on_task_stack #endif restore_user_regs fast = 0, offset = 0 diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 55fc331af337..e2a9e013b6a9 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -187,12 +187,12 @@ config ARM64 select HAVE_ARCH_KCSAN if EXPERT select HAVE_ARCH_KFENCE select HAVE_ARCH_KGDB + select HAVE_ARCH_KSTACK_ERASE select HAVE_ARCH_MMAP_RND_BITS select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT select HAVE_ARCH_PREL32_RELOCATIONS select HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET select HAVE_ARCH_SECCOMP_FILTER - select HAVE_ARCH_STACKLEAK select HAVE_ARCH_THREAD_STRUCT_WHITELIST select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRANSPARENT_HUGEPAGE diff --git a/arch/arm64/kernel/entry.S b/arch/arm64/kernel/entry.S index 5ae2a34b50bd..67331437b2aa 100644 --- a/arch/arm64/kernel/entry.S +++ b/arch/arm64/kernel/entry.S @@ -614,7 +614,7 @@ SYM_CODE_END(ret_to_kernel) SYM_CODE_START_LOCAL(ret_to_user) ldr x19, [tsk, #TSK_TI_FLAGS] // re-check for single-step enable_step_tsk x19, x2 -#ifdef CONFIG_GCC_PLUGIN_STACKLEAK +#ifdef CONFIG_KSTACK_ERASE bl stackleak_erase_on_task_stack #endif kernel_exit 0 diff --git a/arch/arm64/kernel/pi/Makefile b/arch/arm64/kernel/pi/Makefile index 4d11a8c29181..f440bf57b1a5 100644 --- a/arch/arm64/kernel/pi/Makefile +++ b/arch/arm64/kernel/pi/Makefile @@ -2,7 +2,7 @@ # Copyright 2022 Google LLC KBUILD_CFLAGS := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) -fpie \ - -Os -DDISABLE_BRANCH_PROFILING $(DISABLE_STACKLEAK_PLUGIN) \ + -Os -DDISABLE_BRANCH_PROFILING $(DISABLE_KSTACK_ERASE) \ $(DISABLE_LATENT_ENTROPY_PLUGIN) \ $(call cc-option,-mbranch-protection=none) \ -I$(srctree)/scripts/dtc/libfdt -fno-stack-protector \ diff --git a/arch/arm64/kvm/hyp/nvhe/Makefile b/arch/arm64/kvm/hyp/nvhe/Makefile index a76522d63c3e..0b0a68b663d4 100644 --- a/arch/arm64/kvm/hyp/nvhe/Makefile +++ b/arch/arm64/kvm/hyp/nvhe/Makefile @@ -12,7 +12,7 @@ asflags-y := -D__KVM_NVHE_HYPERVISOR__ -D__DISABLE_EXPORTS ccflags-y := -D__KVM_NVHE_HYPERVISOR__ -D__DISABLE_EXPORTS -D__DISABLE_TRACE_MMIO__ ccflags-y += -fno-stack-protector \ -DDISABLE_BRANCH_PROFILING \ - $(DISABLE_STACKLEAK_PLUGIN) + $(DISABLE_KSTACK_ERASE) hostprogs := gen-hyprel HOST_EXTRACFLAGS += -I$(objtree)/include diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 4b19f93379a1..1514789bea4a 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -120,11 +120,11 @@ config LOONGARCH select HAVE_ARCH_KASAN select HAVE_ARCH_KFENCE select HAVE_ARCH_KGDB if PERF_EVENTS + select HAVE_ARCH_KSTACK_ERASE select HAVE_ARCH_MMAP_RND_BITS if MMU select HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET select HAVE_ARCH_SECCOMP select HAVE_ARCH_SECCOMP_FILTER - select HAVE_ARCH_STACKLEAK select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRANSPARENT_HUGEPAGE select HAVE_ARCH_USERFAULTFD_MINOR if USERFAULTFD diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index 36061f4732b7..cfc084fc9e6f 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -135,13 +135,13 @@ config RISCV select HAVE_ARCH_KASAN if MMU && 64BIT select HAVE_ARCH_KASAN_VMALLOC if MMU && 64BIT select HAVE_ARCH_KFENCE if MMU && 64BIT + select HAVE_ARCH_KSTACK_ERASE select HAVE_ARCH_KGDB if !XIP_KERNEL select HAVE_ARCH_KGDB_QXFER_PKT select HAVE_ARCH_MMAP_RND_BITS if MMU select HAVE_ARCH_MMAP_RND_COMPAT_BITS if COMPAT select HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET select HAVE_ARCH_SECCOMP_FILTER - select HAVE_ARCH_STACKLEAK select HAVE_ARCH_THREAD_STRUCT_WHITELIST select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRANSPARENT_HUGEPAGE if 64BIT && MMU diff --git a/arch/riscv/kernel/entry.S b/arch/riscv/kernel/entry.S index 75656afa2d6b..3a0ec6fd5956 100644 --- a/arch/riscv/kernel/entry.S +++ b/arch/riscv/kernel/entry.S @@ -220,7 +220,7 @@ SYM_CODE_START_NOALIGN(ret_from_exception) #endif bnez s0, 1f -#ifdef CONFIG_GCC_PLUGIN_STACKLEAK +#ifdef CONFIG_KSTACK_ERASE call stackleak_erase_on_task_stack #endif diff --git a/arch/riscv/kernel/pi/Makefile b/arch/riscv/kernel/pi/Makefile index 81d69d45c06c..7dd15be69c90 100644 --- a/arch/riscv/kernel/pi/Makefile +++ b/arch/riscv/kernel/pi/Makefile @@ -2,7 +2,7 @@ # This file was copied from arm64/kernel/pi/Makefile. KBUILD_CFLAGS := $(subst $(CC_FLAGS_FTRACE),,$(KBUILD_CFLAGS)) -fpie \ - -Os -DDISABLE_BRANCH_PROFILING $(DISABLE_STACKLEAK_PLUGIN) \ + -Os -DDISABLE_BRANCH_PROFILING $(DISABLE_KSTACK_ERASE) \ $(call cc-option,-mbranch-protection=none) \ -I$(srctree)/scripts/dtc/libfdt -fno-stack-protector \ -include $(srctree)/include/linux/hidden.h \ diff --git a/arch/riscv/purgatory/Makefile b/arch/riscv/purgatory/Makefile index fb9c917c9b45..240592e3f5c2 100644 --- a/arch/riscv/purgatory/Makefile +++ b/arch/riscv/purgatory/Makefile @@ -53,7 +53,7 @@ targets += purgatory.ro purgatory.chk PURGATORY_CFLAGS_REMOVE := -mcmodel=kernel PURGATORY_CFLAGS := -mcmodel=medany -ffreestanding -fno-zero-initialized-in-bss -PURGATORY_CFLAGS += $(DISABLE_STACKLEAK_PLUGIN) -DDISABLE_BRANCH_PROFILING +PURGATORY_CFLAGS += $(DISABLE_KSTACK_ERASE) -DDISABLE_BRANCH_PROFILING PURGATORY_CFLAGS += -fno-stack-protector -g0 # Default KBUILD_CFLAGS can have -pg option set when FTRACE is enabled. That diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 0c16dc443e2f..a8e74ed8e3cc 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -176,10 +176,10 @@ config S390 select HAVE_ARCH_KCSAN select HAVE_ARCH_KMSAN select HAVE_ARCH_KFENCE + select HAVE_ARCH_KSTACK_ERASE select HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET select HAVE_ARCH_SECCOMP_FILTER select HAVE_ARCH_SOFT_DIRTY - select HAVE_ARCH_STACKLEAK select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRANSPARENT_HUGEPAGE select HAVE_ARCH_VMAP_STACK diff --git a/arch/s390/kernel/entry.S b/arch/s390/kernel/entry.S index 0f00f4b06d51..75b0fbb236d0 100644 --- a/arch/s390/kernel/entry.S +++ b/arch/s390/kernel/entry.S @@ -124,7 +124,7 @@ _LPP_OFFSET = __LC_LPP #endif .macro STACKLEAK_ERASE -#ifdef CONFIG_GCC_PLUGIN_STACKLEAK +#ifdef CONFIG_KSTACK_ERASE brasl %r14,stackleak_erase_on_task_stack #endif .endm diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 340e5468980e..bc3708cad46b 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -204,13 +204,13 @@ config X86 select HAVE_ARCH_KFENCE select HAVE_ARCH_KMSAN if X86_64 select HAVE_ARCH_KGDB + select HAVE_ARCH_KSTACK_ERASE select HAVE_ARCH_MMAP_RND_BITS if MMU select HAVE_ARCH_MMAP_RND_COMPAT_BITS if MMU && COMPAT select HAVE_ARCH_COMPAT_MMAP_BASES if MMU && COMPAT select HAVE_ARCH_PREL32_RELOCATIONS select HAVE_ARCH_SECCOMP_FILTER select HAVE_ARCH_THREAD_STRUCT_WHITELIST - select HAVE_ARCH_STACKLEAK select HAVE_ARCH_TRACEHOOK select HAVE_ARCH_TRANSPARENT_HUGEPAGE select HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD if X86_64 diff --git a/arch/x86/entry/calling.h b/arch/x86/entry/calling.h index d83236b96f22..94519688b007 100644 --- a/arch/x86/entry/calling.h +++ b/arch/x86/entry/calling.h @@ -369,7 +369,7 @@ For 32-bit we have the following conventions - kernel is built with .endm .macro STACKLEAK_ERASE_NOCLOBBER -#ifdef CONFIG_GCC_PLUGIN_STACKLEAK +#ifdef CONFIG_KSTACK_ERASE PUSH_AND_CLEAR_REGS call stackleak_erase POP_REGS @@ -388,7 +388,7 @@ For 32-bit we have the following conventions - kernel is built with #endif /* !CONFIG_X86_64 */ .macro STACKLEAK_ERASE -#ifdef CONFIG_GCC_PLUGIN_STACKLEAK +#ifdef CONFIG_KSTACK_ERASE call stackleak_erase #endif .endm diff --git a/arch/x86/purgatory/Makefile b/arch/x86/purgatory/Makefile index ebdfd7b84feb..e0a607a14e7e 100644 --- a/arch/x86/purgatory/Makefile +++ b/arch/x86/purgatory/Makefile @@ -35,7 +35,7 @@ targets += purgatory.ro purgatory.chk PURGATORY_CFLAGS_REMOVE := -mcmodel=kernel PURGATORY_CFLAGS := -mcmodel=small -ffreestanding -fno-zero-initialized-in-bss -g0 PURGATORY_CFLAGS += -fpic -fvisibility=hidden -PURGATORY_CFLAGS += $(DISABLE_STACKLEAK_PLUGIN) -DDISABLE_BRANCH_PROFILING +PURGATORY_CFLAGS += $(DISABLE_KSTACK_ERASE) -DDISABLE_BRANCH_PROFILING PURGATORY_CFLAGS += -fno-stack-protector # Default KBUILD_CFLAGS can have -pg option set when FTRACE is enabled. That diff --git a/drivers/firmware/efi/libstub/Makefile b/drivers/firmware/efi/libstub/Makefile index 939a4955e00b..94b05e4451dd 100644 --- a/drivers/firmware/efi/libstub/Makefile +++ b/drivers/firmware/efi/libstub/Makefile @@ -22,16 +22,16 @@ cflags-$(CONFIG_X86) += -m$(BITS) -D__KERNEL__ -std=gnu11 \ # arm64 uses the full KBUILD_CFLAGS so it's necessary to explicitly # disable the stackleak plugin -cflags-$(CONFIG_ARM64) += -fpie $(DISABLE_STACKLEAK_PLUGIN) \ +cflags-$(CONFIG_ARM64) += -fpie $(DISABLE_KSTACK_ERASE) \ -fno-unwind-tables -fno-asynchronous-unwind-tables cflags-$(CONFIG_ARM) += -DEFI_HAVE_STRLEN -DEFI_HAVE_STRNLEN \ -DEFI_HAVE_MEMCHR -DEFI_HAVE_STRRCHR \ -DEFI_HAVE_STRCMP -fno-builtin -fpic \ $(call cc-option,-mno-single-pic-base) \ - $(DISABLE_STACKLEAK_PLUGIN) + $(DISABLE_KSTACK_ERASE) cflags-$(CONFIG_RISCV) += -fpic -DNO_ALTERNATIVE -mno-relax \ - $(DISABLE_STACKLEAK_PLUGIN) -cflags-$(CONFIG_LOONGARCH) += -fpie $(DISABLE_STACKLEAK_PLUGIN) + $(DISABLE_KSTACK_ERASE) +cflags-$(CONFIG_LOONGARCH) += -fpie $(DISABLE_KSTACK_ERASE) cflags-$(CONFIG_EFI_PARAMS_FROM_FDT) += -I$(srctree)/scripts/dtc/libfdt diff --git a/drivers/misc/lkdtm/Makefile b/drivers/misc/lkdtm/Makefile index 39468bd27b85..03ebe33185f9 100644 --- a/drivers/misc/lkdtm/Makefile +++ b/drivers/misc/lkdtm/Makefile @@ -8,7 +8,7 @@ lkdtm-$(CONFIG_LKDTM) += perms.o lkdtm-$(CONFIG_LKDTM) += refcount.o lkdtm-$(CONFIG_LKDTM) += rodata_objcopy.o lkdtm-$(CONFIG_LKDTM) += usercopy.o -lkdtm-$(CONFIG_LKDTM) += stackleak.o +lkdtm-$(CONFIG_LKDTM) += kstack_erase.o lkdtm-$(CONFIG_LKDTM) += cfi.o lkdtm-$(CONFIG_LKDTM) += fortify.o lkdtm-$(CONFIG_PPC_64S_HASH_MMU) += powerpc.o diff --git a/drivers/misc/lkdtm/kstack_erase.c b/drivers/misc/lkdtm/kstack_erase.c new file mode 100644 index 000000000000..4fd9b0bfb874 --- /dev/null +++ b/drivers/misc/lkdtm/kstack_erase.c @@ -0,0 +1,150 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This code tests that the current task stack is properly erased (filled + * with KSTACK_ERASE_POISON). + * + * Authors: + * Alexander Popov + * Tycho Andersen + */ + +#include "lkdtm.h" +#include + +#if defined(CONFIG_KSTACK_ERASE) +/* + * Check that stackleak tracks the lowest stack pointer and erases the stack + * below this as expected. + * + * To prevent the lowest stack pointer changing during the test, IRQs are + * masked and instrumentation of this function is disabled. We assume that the + * compiler will create a fixed-size stack frame for this function. + * + * Any non-inlined function may make further use of the stack, altering the + * lowest stack pointer and/or clobbering poison values. To avoid spurious + * failures we must avoid printing until the end of the test or have already + * encountered a failure condition. + */ +static void noinstr check_stackleak_irqoff(void) +{ + const unsigned long task_stack_base = (unsigned long)task_stack_page(current); + const unsigned long task_stack_low = stackleak_task_low_bound(current); + const unsigned long task_stack_high = stackleak_task_high_bound(current); + const unsigned long current_sp = current_stack_pointer; + const unsigned long lowest_sp = current->lowest_stack; + unsigned long untracked_high; + unsigned long poison_high, poison_low; + bool test_failed = false; + + /* + * Check that the current and lowest recorded stack pointer values fall + * within the expected task stack boundaries. These tests should never + * fail unless the boundaries are incorrect or we're clobbering the + * STACK_END_MAGIC, and in either casee something is seriously wrong. + */ + if (current_sp < task_stack_low || current_sp >= task_stack_high) { + instrumentation_begin(); + pr_err("FAIL: current_stack_pointer (0x%lx) outside of task stack bounds [0x%lx..0x%lx]\n", + current_sp, task_stack_low, task_stack_high - 1); + test_failed = true; + goto out; + } + if (lowest_sp < task_stack_low || lowest_sp >= task_stack_high) { + instrumentation_begin(); + pr_err("FAIL: current->lowest_stack (0x%lx) outside of task stack bounds [0x%lx..0x%lx]\n", + lowest_sp, task_stack_low, task_stack_high - 1); + test_failed = true; + goto out; + } + + /* + * Depending on what has run prior to this test, the lowest recorded + * stack pointer could be above or below the current stack pointer. + * Start from the lowest of the two. + * + * Poison values are naturally-aligned unsigned longs. As the current + * stack pointer might not be sufficiently aligned, we must align + * downwards to find the lowest known stack pointer value. This is the + * high boundary for a portion of the stack which may have been used + * without being tracked, and has to be scanned for poison. + */ + untracked_high = min(current_sp, lowest_sp); + untracked_high = ALIGN_DOWN(untracked_high, sizeof(unsigned long)); + + /* + * Find the top of the poison in the same way as the erasing code. + */ + poison_high = stackleak_find_top_of_poison(task_stack_low, untracked_high); + + /* + * Check whether the poisoned portion of the stack (if any) consists + * entirely of poison. This verifies the entries that + * stackleak_find_top_of_poison() should have checked. + */ + poison_low = poison_high; + while (poison_low > task_stack_low) { + poison_low -= sizeof(unsigned long); + + if (*(unsigned long *)poison_low == KSTACK_ERASE_POISON) + continue; + + instrumentation_begin(); + pr_err("FAIL: non-poison value %lu bytes below poison boundary: 0x%lx\n", + poison_high - poison_low, *(unsigned long *)poison_low); + test_failed = true; + goto out; + } + + instrumentation_begin(); + pr_info("kstack erase stack usage:\n" + " high offset: %lu bytes\n" + " current: %lu bytes\n" + " lowest: %lu bytes\n" + " tracked: %lu bytes\n" + " untracked: %lu bytes\n" + " poisoned: %lu bytes\n" + " low offset: %lu bytes\n", + task_stack_base + THREAD_SIZE - task_stack_high, + task_stack_high - current_sp, + task_stack_high - lowest_sp, + task_stack_high - untracked_high, + untracked_high - poison_high, + poison_high - task_stack_low, + task_stack_low - task_stack_base); + +out: + if (test_failed) { + pr_err("FAIL: the thread stack is NOT properly erased!\n"); + } else { + pr_info("OK: the rest of the thread stack is properly erased\n"); + } + instrumentation_end(); +} + +static void lkdtm_KSTACK_ERASE(void) +{ + unsigned long flags; + + local_irq_save(flags); + check_stackleak_irqoff(); + local_irq_restore(flags); +} +#else /* defined(CONFIG_KSTACK_ERASE) */ +static void lkdtm_KSTACK_ERASE(void) +{ + if (IS_ENABLED(CONFIG_HAVE_ARCH_KSTACK_ERASE)) { + pr_err("XFAIL: stackleak is not enabled (CONFIG_KSTACK_ERASE=n)\n"); + } else { + pr_err("XFAIL: stackleak is not supported on this arch (HAVE_ARCH_KSTACK_ERASE=n)\n"); + } +} +#endif /* defined(CONFIG_KSTACK_ERASE) */ + +static struct crashtype crashtypes[] = { + CRASHTYPE(KSTACK_ERASE), +}; + +struct crashtype_category stackleak_crashtypes = { + .crashtypes = crashtypes, + .len = ARRAY_SIZE(crashtypes), +}; diff --git a/drivers/misc/lkdtm/stackleak.c b/drivers/misc/lkdtm/stackleak.c deleted file mode 100644 index f1d022160913..000000000000 --- a/drivers/misc/lkdtm/stackleak.c +++ /dev/null @@ -1,150 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * This code tests that the current task stack is properly erased (filled - * with STACKLEAK_POISON). - * - * Authors: - * Alexander Popov - * Tycho Andersen - */ - -#include "lkdtm.h" -#include - -#if defined(CONFIG_GCC_PLUGIN_STACKLEAK) -/* - * Check that stackleak tracks the lowest stack pointer and erases the stack - * below this as expected. - * - * To prevent the lowest stack pointer changing during the test, IRQs are - * masked and instrumentation of this function is disabled. We assume that the - * compiler will create a fixed-size stack frame for this function. - * - * Any non-inlined function may make further use of the stack, altering the - * lowest stack pointer and/or clobbering poison values. To avoid spurious - * failures we must avoid printing until the end of the test or have already - * encountered a failure condition. - */ -static void noinstr check_stackleak_irqoff(void) -{ - const unsigned long task_stack_base = (unsigned long)task_stack_page(current); - const unsigned long task_stack_low = stackleak_task_low_bound(current); - const unsigned long task_stack_high = stackleak_task_high_bound(current); - const unsigned long current_sp = current_stack_pointer; - const unsigned long lowest_sp = current->lowest_stack; - unsigned long untracked_high; - unsigned long poison_high, poison_low; - bool test_failed = false; - - /* - * Check that the current and lowest recorded stack pointer values fall - * within the expected task stack boundaries. These tests should never - * fail unless the boundaries are incorrect or we're clobbering the - * STACK_END_MAGIC, and in either casee something is seriously wrong. - */ - if (current_sp < task_stack_low || current_sp >= task_stack_high) { - instrumentation_begin(); - pr_err("FAIL: current_stack_pointer (0x%lx) outside of task stack bounds [0x%lx..0x%lx]\n", - current_sp, task_stack_low, task_stack_high - 1); - test_failed = true; - goto out; - } - if (lowest_sp < task_stack_low || lowest_sp >= task_stack_high) { - instrumentation_begin(); - pr_err("FAIL: current->lowest_stack (0x%lx) outside of task stack bounds [0x%lx..0x%lx]\n", - lowest_sp, task_stack_low, task_stack_high - 1); - test_failed = true; - goto out; - } - - /* - * Depending on what has run prior to this test, the lowest recorded - * stack pointer could be above or below the current stack pointer. - * Start from the lowest of the two. - * - * Poison values are naturally-aligned unsigned longs. As the current - * stack pointer might not be sufficiently aligned, we must align - * downwards to find the lowest known stack pointer value. This is the - * high boundary for a portion of the stack which may have been used - * without being tracked, and has to be scanned for poison. - */ - untracked_high = min(current_sp, lowest_sp); - untracked_high = ALIGN_DOWN(untracked_high, sizeof(unsigned long)); - - /* - * Find the top of the poison in the same way as the erasing code. - */ - poison_high = stackleak_find_top_of_poison(task_stack_low, untracked_high); - - /* - * Check whether the poisoned portion of the stack (if any) consists - * entirely of poison. This verifies the entries that - * stackleak_find_top_of_poison() should have checked. - */ - poison_low = poison_high; - while (poison_low > task_stack_low) { - poison_low -= sizeof(unsigned long); - - if (*(unsigned long *)poison_low == STACKLEAK_POISON) - continue; - - instrumentation_begin(); - pr_err("FAIL: non-poison value %lu bytes below poison boundary: 0x%lx\n", - poison_high - poison_low, *(unsigned long *)poison_low); - test_failed = true; - goto out; - } - - instrumentation_begin(); - pr_info("stackleak stack usage:\n" - " high offset: %lu bytes\n" - " current: %lu bytes\n" - " lowest: %lu bytes\n" - " tracked: %lu bytes\n" - " untracked: %lu bytes\n" - " poisoned: %lu bytes\n" - " low offset: %lu bytes\n", - task_stack_base + THREAD_SIZE - task_stack_high, - task_stack_high - current_sp, - task_stack_high - lowest_sp, - task_stack_high - untracked_high, - untracked_high - poison_high, - poison_high - task_stack_low, - task_stack_low - task_stack_base); - -out: - if (test_failed) { - pr_err("FAIL: the thread stack is NOT properly erased!\n"); - } else { - pr_info("OK: the rest of the thread stack is properly erased\n"); - } - instrumentation_end(); -} - -static void lkdtm_STACKLEAK_ERASING(void) -{ - unsigned long flags; - - local_irq_save(flags); - check_stackleak_irqoff(); - local_irq_restore(flags); -} -#else /* defined(CONFIG_GCC_PLUGIN_STACKLEAK) */ -static void lkdtm_STACKLEAK_ERASING(void) -{ - if (IS_ENABLED(CONFIG_HAVE_ARCH_STACKLEAK)) { - pr_err("XFAIL: stackleak is not enabled (CONFIG_GCC_PLUGIN_STACKLEAK=n)\n"); - } else { - pr_err("XFAIL: stackleak is not supported on this arch (HAVE_ARCH_STACKLEAK=n)\n"); - } -} -#endif /* defined(CONFIG_GCC_PLUGIN_STACKLEAK) */ - -static struct crashtype crashtypes[] = { - CRASHTYPE(STACKLEAK_ERASING), -}; - -struct crashtype_category stackleak_crashtypes = { - .crashtypes = crashtypes, - .len = ARRAY_SIZE(crashtypes), -}; diff --git a/fs/proc/base.c b/fs/proc/base.c index c667702dc69b..be34612af8b6 100644 --- a/fs/proc/base.c +++ b/fs/proc/base.c @@ -3291,7 +3291,7 @@ static int proc_pid_ksm_stat(struct seq_file *m, struct pid_namespace *ns, } #endif /* CONFIG_KSM */ -#ifdef CONFIG_STACKLEAK_METRICS +#ifdef CONFIG_KSTACK_ERASE_METRICS static int proc_stack_depth(struct seq_file *m, struct pid_namespace *ns, struct pid *pid, struct task_struct *task) { @@ -3304,7 +3304,7 @@ static int proc_stack_depth(struct seq_file *m, struct pid_namespace *ns, prev_depth, depth); return 0; } -#endif /* CONFIG_STACKLEAK_METRICS */ +#endif /* CONFIG_KSTACK_ERASE_METRICS */ /* * Thread groups @@ -3411,7 +3411,7 @@ static const struct pid_entry tgid_base_stuff[] = { #ifdef CONFIG_LIVEPATCH ONE("patch_state", S_IRUSR, proc_pid_patch_state), #endif -#ifdef CONFIG_STACKLEAK_METRICS +#ifdef CONFIG_KSTACK_ERASE_METRICS ONE("stack_depth", S_IRUGO, proc_stack_depth), #endif #ifdef CONFIG_PROC_PID_ARCH_STATUS diff --git a/include/linux/kstack_erase.h b/include/linux/kstack_erase.h new file mode 100644 index 000000000000..4e432eefa4d0 --- /dev/null +++ b/include/linux/kstack_erase.h @@ -0,0 +1,89 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_KSTACK_ERASE_H +#define _LINUX_KSTACK_ERASE_H + +#include +#include + +/* + * Check that the poison value points to the unused hole in the + * virtual memory map for your platform. + */ +#define KSTACK_ERASE_POISON -0xBEEF +#define KSTACK_ERASE_SEARCH_DEPTH 128 + +#ifdef CONFIG_KSTACK_ERASE +#include +#include + +/* + * The lowest address on tsk's stack which we can plausibly erase. + */ +static __always_inline unsigned long +stackleak_task_low_bound(const struct task_struct *tsk) +{ + /* + * The lowest unsigned long on the task stack contains STACK_END_MAGIC, + * which we must not corrupt. + */ + return (unsigned long)end_of_stack(tsk) + sizeof(unsigned long); +} + +/* + * The address immediately after the highest address on tsk's stack which we + * can plausibly erase. + */ +static __always_inline unsigned long +stackleak_task_high_bound(const struct task_struct *tsk) +{ + /* + * The task's pt_regs lives at the top of the task stack and will be + * overwritten by exception entry, so there's no need to erase them. + */ + return (unsigned long)task_pt_regs(tsk); +} + +/* + * Find the address immediately above the poisoned region of the stack, where + * that region falls between 'low' (inclusive) and 'high' (exclusive). + */ +static __always_inline unsigned long +stackleak_find_top_of_poison(const unsigned long low, const unsigned long high) +{ + const unsigned int depth = KSTACK_ERASE_SEARCH_DEPTH / sizeof(unsigned long); + unsigned int poison_count = 0; + unsigned long poison_high = high; + unsigned long sp = high; + + while (sp > low && poison_count < depth) { + sp -= sizeof(unsigned long); + + if (*(unsigned long *)sp == KSTACK_ERASE_POISON) { + poison_count++; + } else { + poison_count = 0; + poison_high = sp; + } + } + + return poison_high; +} + +static inline void stackleak_task_init(struct task_struct *t) +{ + t->lowest_stack = stackleak_task_low_bound(t); +# ifdef CONFIG_KSTACK_ERASE_METRICS + t->prev_lowest_stack = t->lowest_stack; +# endif +} + +asmlinkage void noinstr stackleak_erase(void); +asmlinkage void noinstr stackleak_erase_on_task_stack(void); +asmlinkage void noinstr stackleak_erase_off_task_stack(void); +void __no_caller_saved_registers noinstr stackleak_track_stack(void); + +#else /* !CONFIG_KSTACK_ERASE */ +static inline void stackleak_task_init(struct task_struct *t) { } +#endif + +#endif diff --git a/include/linux/sched.h b/include/linux/sched.h index 4f78a64beb52..b7d2f2fd4cd4 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1607,8 +1607,10 @@ struct task_struct { /* Used by BPF for per-TASK xdp storage */ struct bpf_net_context *bpf_net_context; -#ifdef CONFIG_GCC_PLUGIN_STACKLEAK +#ifdef CONFIG_KSTACK_ERASE unsigned long lowest_stack; +#endif +#ifdef CONFIG_KSTACK_ERASE_METRICS unsigned long prev_lowest_stack; #endif diff --git a/include/linux/stackleak.h b/include/linux/stackleak.h deleted file mode 100644 index 3be2cb564710..000000000000 --- a/include/linux/stackleak.h +++ /dev/null @@ -1,89 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef _LINUX_STACKLEAK_H -#define _LINUX_STACKLEAK_H - -#include -#include - -/* - * Check that the poison value points to the unused hole in the - * virtual memory map for your platform. - */ -#define STACKLEAK_POISON -0xBEEF -#define STACKLEAK_SEARCH_DEPTH 128 - -#ifdef CONFIG_GCC_PLUGIN_STACKLEAK -#include -#include - -/* - * The lowest address on tsk's stack which we can plausibly erase. - */ -static __always_inline unsigned long -stackleak_task_low_bound(const struct task_struct *tsk) -{ - /* - * The lowest unsigned long on the task stack contains STACK_END_MAGIC, - * which we must not corrupt. - */ - return (unsigned long)end_of_stack(tsk) + sizeof(unsigned long); -} - -/* - * The address immediately after the highest address on tsk's stack which we - * can plausibly erase. - */ -static __always_inline unsigned long -stackleak_task_high_bound(const struct task_struct *tsk) -{ - /* - * The task's pt_regs lives at the top of the task stack and will be - * overwritten by exception entry, so there's no need to erase them. - */ - return (unsigned long)task_pt_regs(tsk); -} - -/* - * Find the address immediately above the poisoned region of the stack, where - * that region falls between 'low' (inclusive) and 'high' (exclusive). - */ -static __always_inline unsigned long -stackleak_find_top_of_poison(const unsigned long low, const unsigned long high) -{ - const unsigned int depth = STACKLEAK_SEARCH_DEPTH / sizeof(unsigned long); - unsigned int poison_count = 0; - unsigned long poison_high = high; - unsigned long sp = high; - - while (sp > low && poison_count < depth) { - sp -= sizeof(unsigned long); - - if (*(unsigned long *)sp == STACKLEAK_POISON) { - poison_count++; - } else { - poison_count = 0; - poison_high = sp; - } - } - - return poison_high; -} - -static inline void stackleak_task_init(struct task_struct *t) -{ - t->lowest_stack = stackleak_task_low_bound(t); -# ifdef CONFIG_STACKLEAK_METRICS - t->prev_lowest_stack = t->lowest_stack; -# endif -} - -asmlinkage void noinstr stackleak_erase(void); -asmlinkage void noinstr stackleak_erase_on_task_stack(void); -asmlinkage void noinstr stackleak_erase_off_task_stack(void); -void __no_caller_saved_registers noinstr stackleak_track_stack(void); - -#else /* !CONFIG_GCC_PLUGIN_STACKLEAK */ -static inline void stackleak_task_init(struct task_struct *t) { } -#endif - -#endif diff --git a/kernel/Makefile b/kernel/Makefile index 32e80dd626af..e4f01f1d4d0c 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -139,11 +139,11 @@ obj-$(CONFIG_WATCH_QUEUE) += watch_queue.o obj-$(CONFIG_RESOURCE_KUNIT_TEST) += resource_kunit.o obj-$(CONFIG_SYSCTL_KUNIT_TEST) += sysctl-test.o -CFLAGS_stackleak.o += $(DISABLE_STACKLEAK_PLUGIN) -obj-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak.o -KASAN_SANITIZE_stackleak.o := n -KCSAN_SANITIZE_stackleak.o := n -KCOV_INSTRUMENT_stackleak.o := n +CFLAGS_kstack_erase.o += $(DISABLE_KSTACK_ERASE) +obj-$(CONFIG_KSTACK_ERASE) += kstack_erase.o +KASAN_SANITIZE_kstack_erase.o := n +KCSAN_SANITIZE_kstack_erase.o := n +KCOV_INSTRUMENT_kstack_erase.o := n obj-$(CONFIG_SCF_TORTURE_TEST) += scftorture.o diff --git a/kernel/fork.c b/kernel/fork.c index 1ee8eb11f38b..1ec66911f6f6 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -93,7 +93,7 @@ #include #include #include -#include +#include #include #include #include diff --git a/kernel/kstack_erase.c b/kernel/kstack_erase.c new file mode 100644 index 000000000000..201b846f8345 --- /dev/null +++ b/kernel/kstack_erase.c @@ -0,0 +1,177 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * This code fills the used part of the kernel stack with a poison value + * before returning to userspace. It's part of the STACKLEAK feature + * ported from grsecurity/PaX. + * + * Author: Alexander Popov + * + * KSTACK_ERASE reduces the information which kernel stack leak bugs can + * reveal and blocks some uninitialized stack variable attacks. + */ + +#include +#include + +#ifdef CONFIG_KSTACK_ERASE_RUNTIME_DISABLE +#include +#include +#include +#include + +static DEFINE_STATIC_KEY_FALSE(stack_erasing_bypass); + +#ifdef CONFIG_SYSCTL +static int stack_erasing_sysctl(const struct ctl_table *table, int write, + void __user *buffer, size_t *lenp, loff_t *ppos) +{ + int ret = 0; + int state = !static_branch_unlikely(&stack_erasing_bypass); + int prev_state = state; + struct ctl_table table_copy = *table; + + table_copy.data = &state; + ret = proc_dointvec_minmax(&table_copy, write, buffer, lenp, ppos); + state = !!state; + if (ret || !write || state == prev_state) + return ret; + + if (state) + static_branch_disable(&stack_erasing_bypass); + else + static_branch_enable(&stack_erasing_bypass); + + pr_warn("stackleak: kernel stack erasing is %s\n", + str_enabled_disabled(state)); + return ret; +} +static const struct ctl_table stackleak_sysctls[] = { + { + .procname = "stack_erasing", + .data = NULL, + .maxlen = sizeof(int), + .mode = 0600, + .proc_handler = stack_erasing_sysctl, + .extra1 = SYSCTL_ZERO, + .extra2 = SYSCTL_ONE, + }, +}; + +static int __init stackleak_sysctls_init(void) +{ + register_sysctl_init("kernel", stackleak_sysctls); + return 0; +} +late_initcall(stackleak_sysctls_init); +#endif /* CONFIG_SYSCTL */ + +#define skip_erasing() static_branch_unlikely(&stack_erasing_bypass) +#else +#define skip_erasing() false +#endif /* CONFIG_KSTACK_ERASE_RUNTIME_DISABLE */ + +#ifndef __stackleak_poison +static __always_inline void __stackleak_poison(unsigned long erase_low, + unsigned long erase_high, + unsigned long poison) +{ + while (erase_low < erase_high) { + *(unsigned long *)erase_low = poison; + erase_low += sizeof(unsigned long); + } +} +#endif + +static __always_inline void __stackleak_erase(bool on_task_stack) +{ + const unsigned long task_stack_low = stackleak_task_low_bound(current); + const unsigned long task_stack_high = stackleak_task_high_bound(current); + unsigned long erase_low, erase_high; + + erase_low = stackleak_find_top_of_poison(task_stack_low, + current->lowest_stack); + +#ifdef CONFIG_KSTACK_ERASE_METRICS + current->prev_lowest_stack = erase_low; +#endif + + /* + * Write poison to the task's stack between 'erase_low' and + * 'erase_high'. + * + * If we're running on a different stack (e.g. an entry trampoline + * stack) we can erase everything below the pt_regs at the top of the + * task stack. + * + * If we're running on the task stack itself, we must not clobber any + * stack used by this function and its caller. We assume that this + * function has a fixed-size stack frame, and the current stack pointer + * doesn't change while we write poison. + */ + if (on_task_stack) + erase_high = current_stack_pointer; + else + erase_high = task_stack_high; + + __stackleak_poison(erase_low, erase_high, KSTACK_ERASE_POISON); + + /* Reset the 'lowest_stack' value for the next syscall */ + current->lowest_stack = task_stack_high; +} + +/* + * Erase and poison the portion of the task stack used since the last erase. + * Can be called from the task stack or an entry stack when the task stack is + * no longer in use. + */ +asmlinkage void noinstr stackleak_erase(void) +{ + if (skip_erasing()) + return; + + __stackleak_erase(on_thread_stack()); +} + +/* + * Erase and poison the portion of the task stack used since the last erase. + * Can only be called from the task stack. + */ +asmlinkage void noinstr stackleak_erase_on_task_stack(void) +{ + if (skip_erasing()) + return; + + __stackleak_erase(true); +} + +/* + * Erase and poison the portion of the task stack used since the last erase. + * Can only be called from a stack other than the task stack. + */ +asmlinkage void noinstr stackleak_erase_off_task_stack(void) +{ + if (skip_erasing()) + return; + + __stackleak_erase(false); +} + +void __used __no_caller_saved_registers noinstr stackleak_track_stack(void) +{ + unsigned long sp = current_stack_pointer; + + /* + * Having CONFIG_KSTACK_ERASE_TRACK_MIN_SIZE larger than + * KSTACK_ERASE_SEARCH_DEPTH makes the poison search in + * stackleak_erase() unreliable. Let's prevent that. + */ + BUILD_BUG_ON(CONFIG_KSTACK_ERASE_TRACK_MIN_SIZE > KSTACK_ERASE_SEARCH_DEPTH); + + /* 'lowest_stack' should be aligned on the register width boundary */ + sp = ALIGN(sp, sizeof(unsigned long)); + if (sp < current->lowest_stack && + sp >= stackleak_task_low_bound(current)) { + current->lowest_stack = sp; + } +} +EXPORT_SYMBOL(stackleak_track_stack); diff --git a/kernel/stackleak.c b/kernel/stackleak.c deleted file mode 100644 index bb65321761b4..000000000000 --- a/kernel/stackleak.c +++ /dev/null @@ -1,177 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * This code fills the used part of the kernel stack with a poison value - * before returning to userspace. It's part of the STACKLEAK feature - * ported from grsecurity/PaX. - * - * Author: Alexander Popov - * - * STACKLEAK reduces the information which kernel stack leak bugs can - * reveal and blocks some uninitialized stack variable attacks. - */ - -#include -#include - -#ifdef CONFIG_STACKLEAK_RUNTIME_DISABLE -#include -#include -#include -#include - -static DEFINE_STATIC_KEY_FALSE(stack_erasing_bypass); - -#ifdef CONFIG_SYSCTL -static int stack_erasing_sysctl(const struct ctl_table *table, int write, - void __user *buffer, size_t *lenp, loff_t *ppos) -{ - int ret = 0; - int state = !static_branch_unlikely(&stack_erasing_bypass); - int prev_state = state; - struct ctl_table table_copy = *table; - - table_copy.data = &state; - ret = proc_dointvec_minmax(&table_copy, write, buffer, lenp, ppos); - state = !!state; - if (ret || !write || state == prev_state) - return ret; - - if (state) - static_branch_disable(&stack_erasing_bypass); - else - static_branch_enable(&stack_erasing_bypass); - - pr_warn("stackleak: kernel stack erasing is %s\n", - str_enabled_disabled(state)); - return ret; -} -static const struct ctl_table stackleak_sysctls[] = { - { - .procname = "stack_erasing", - .data = NULL, - .maxlen = sizeof(int), - .mode = 0600, - .proc_handler = stack_erasing_sysctl, - .extra1 = SYSCTL_ZERO, - .extra2 = SYSCTL_ONE, - }, -}; - -static int __init stackleak_sysctls_init(void) -{ - register_sysctl_init("kernel", stackleak_sysctls); - return 0; -} -late_initcall(stackleak_sysctls_init); -#endif /* CONFIG_SYSCTL */ - -#define skip_erasing() static_branch_unlikely(&stack_erasing_bypass) -#else -#define skip_erasing() false -#endif /* CONFIG_STACKLEAK_RUNTIME_DISABLE */ - -#ifndef __stackleak_poison -static __always_inline void __stackleak_poison(unsigned long erase_low, - unsigned long erase_high, - unsigned long poison) -{ - while (erase_low < erase_high) { - *(unsigned long *)erase_low = poison; - erase_low += sizeof(unsigned long); - } -} -#endif - -static __always_inline void __stackleak_erase(bool on_task_stack) -{ - const unsigned long task_stack_low = stackleak_task_low_bound(current); - const unsigned long task_stack_high = stackleak_task_high_bound(current); - unsigned long erase_low, erase_high; - - erase_low = stackleak_find_top_of_poison(task_stack_low, - current->lowest_stack); - -#ifdef CONFIG_STACKLEAK_METRICS - current->prev_lowest_stack = erase_low; -#endif - - /* - * Write poison to the task's stack between 'erase_low' and - * 'erase_high'. - * - * If we're running on a different stack (e.g. an entry trampoline - * stack) we can erase everything below the pt_regs at the top of the - * task stack. - * - * If we're running on the task stack itself, we must not clobber any - * stack used by this function and its caller. We assume that this - * function has a fixed-size stack frame, and the current stack pointer - * doesn't change while we write poison. - */ - if (on_task_stack) - erase_high = current_stack_pointer; - else - erase_high = task_stack_high; - - __stackleak_poison(erase_low, erase_high, STACKLEAK_POISON); - - /* Reset the 'lowest_stack' value for the next syscall */ - current->lowest_stack = task_stack_high; -} - -/* - * Erase and poison the portion of the task stack used since the last erase. - * Can be called from the task stack or an entry stack when the task stack is - * no longer in use. - */ -asmlinkage void noinstr stackleak_erase(void) -{ - if (skip_erasing()) - return; - - __stackleak_erase(on_thread_stack()); -} - -/* - * Erase and poison the portion of the task stack used since the last erase. - * Can only be called from the task stack. - */ -asmlinkage void noinstr stackleak_erase_on_task_stack(void) -{ - if (skip_erasing()) - return; - - __stackleak_erase(true); -} - -/* - * Erase and poison the portion of the task stack used since the last erase. - * Can only be called from a stack other than the task stack. - */ -asmlinkage void noinstr stackleak_erase_off_task_stack(void) -{ - if (skip_erasing()) - return; - - __stackleak_erase(false); -} - -void __used __no_caller_saved_registers noinstr stackleak_track_stack(void) -{ - unsigned long sp = current_stack_pointer; - - /* - * Having CONFIG_STACKLEAK_TRACK_MIN_SIZE larger than - * STACKLEAK_SEARCH_DEPTH makes the poison search in - * stackleak_erase() unreliable. Let's prevent that. - */ - BUILD_BUG_ON(CONFIG_STACKLEAK_TRACK_MIN_SIZE > STACKLEAK_SEARCH_DEPTH); - - /* 'lowest_stack' should be aligned on the register width boundary */ - sp = ALIGN(sp, sizeof(unsigned long)); - if (sp < current->lowest_stack && - sp >= stackleak_task_low_bound(current)) { - current->lowest_stack = sp; - } -} -EXPORT_SYMBOL(stackleak_track_stack); diff --git a/lib/Makefile b/lib/Makefile index c38582f187dd..632e69d25feb 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -337,7 +337,7 @@ obj-$(CONFIG_UBSAN) += ubsan.o UBSAN_SANITIZE_ubsan.o := n KASAN_SANITIZE_ubsan.o := n KCSAN_SANITIZE_ubsan.o := n -CFLAGS_ubsan.o := -fno-stack-protector $(DISABLE_STACKLEAK_PLUGIN) +CFLAGS_ubsan.o := -fno-stack-protector $(DISABLE_KSTACK_ERASE) obj-$(CONFIG_SBITMAP) += sbitmap.o diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins index 435ab3f0ec44..28b8867c4e84 100644 --- a/scripts/Makefile.gcc-plugins +++ b/scripts/Makefile.gcc-plugins @@ -12,15 +12,15 @@ gcc-plugin-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak_plugin.so gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \ += -DSTACKLEAK_PLUGIN gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \ - += -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_STACKLEAK_TRACK_MIN_SIZE) + += -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_KSTACK_ERASE_TRACK_MIN_SIZE) gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \ += -fplugin-arg-stackleak_plugin-arch=$(SRCARCH) gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK_VERBOSE) \ += -fplugin-arg-stackleak_plugin-verbose ifdef CONFIG_GCC_PLUGIN_STACKLEAK - DISABLE_STACKLEAK_PLUGIN += -fplugin-arg-stackleak_plugin-disable + DISABLE_KSTACK_ERASE += -fplugin-arg-stackleak_plugin-disable endif -export DISABLE_STACKLEAK_PLUGIN +export DISABLE_KSTACK_ERASE # All the plugin CFLAGS are collected here in case a build target needs to # filter them out of the KBUILD_CFLAGS. diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening index fd1238753cad..125b35e2ef0f 100644 --- a/security/Kconfig.hardening +++ b/security/Kconfig.hardening @@ -82,10 +82,10 @@ choice endchoice -config GCC_PLUGIN_STACKLEAK +config KSTACK_ERASE bool "Poison kernel stack before returning from syscalls" + depends on HAVE_ARCH_KSTACK_ERASE depends on GCC_PLUGINS - depends on HAVE_ARCH_STACKLEAK help This option makes the kernel erase the kernel stack before returning from system calls. This has the effect of leaving @@ -103,6 +103,10 @@ config GCC_PLUGIN_STACKLEAK are advised to test this feature on your expected workload before deploying it. +config GCC_PLUGIN_STACKLEAK + def_bool KSTACK_ERASE + depends on GCC_PLUGINS + help This plugin was ported from grsecurity/PaX. More information at: * https://grsecurity.net/ * https://pax.grsecurity.net/ @@ -117,37 +121,37 @@ config GCC_PLUGIN_STACKLEAK_VERBOSE instrumented. This is useful for comparing coverage between builds. -config STACKLEAK_TRACK_MIN_SIZE - int "Minimum stack frame size of functions tracked by STACKLEAK" +config KSTACK_ERASE_TRACK_MIN_SIZE + int "Minimum stack frame size of functions tracked by KSTACK_ERASE" default 100 range 0 4096 - depends on GCC_PLUGIN_STACKLEAK + depends on KSTACK_ERASE help - The STACKLEAK gcc plugin instruments the kernel code for tracking + The KSTACK_ERASE option instruments the kernel code for tracking the lowest border of the kernel stack (and for some other purposes). It inserts the stackleak_track_stack() call for the functions with a stack frame size greater than or equal to this parameter. If unsure, leave the default value 100. -config STACKLEAK_METRICS - bool "Show STACKLEAK metrics in the /proc file system" - depends on GCC_PLUGIN_STACKLEAK +config KSTACK_ERASE_METRICS + bool "Show KSTACK_ERASE metrics in the /proc file system" + depends on KSTACK_ERASE depends on PROC_FS help - If this is set, STACKLEAK metrics for every task are available in - the /proc file system. In particular, /proc//stack_depth + If this is set, KSTACK_ERASE metrics for every task are available + in the /proc file system. In particular, /proc//stack_depth shows the maximum kernel stack consumption for the current and previous syscalls. Although this information is not precise, it - can be useful for estimating the STACKLEAK performance impact for - your workloads. + can be useful for estimating the KSTACK_ERASE performance impact + for your workloads. -config STACKLEAK_RUNTIME_DISABLE +config KSTACK_ERASE_RUNTIME_DISABLE bool "Allow runtime disabling of kernel stack erasing" - depends on GCC_PLUGIN_STACKLEAK + depends on KSTACK_ERASE help This option provides 'stack_erasing' sysctl, which can be used in runtime to control kernel stack erasing for kernels built with - CONFIG_GCC_PLUGIN_STACKLEAK. + CONFIG_KSTACK_ERASE. config INIT_ON_ALLOC_DEFAULT_ON bool "Enable heap memory zeroing on allocation by default" diff --git a/tools/objtool/check.c b/tools/objtool/check.c index f23bdda737aa..5451bdbcf84a 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1192,7 +1192,7 @@ static const char *uaccess_safe_builtin[] = { "__ubsan_handle_type_mismatch_v1", "__ubsan_handle_shift_out_of_bounds", "__ubsan_handle_load_invalid_value", - /* STACKLEAK */ + /* KSTACK_ERASE */ "stackleak_track_stack", /* TRACE_BRANCH_PROFILING */ "ftrace_likely_update", diff --git a/tools/testing/selftests/lkdtm/config b/tools/testing/selftests/lkdtm/config index 7afe05e8c4d7..bd09fdaf53e0 100644 --- a/tools/testing/selftests/lkdtm/config +++ b/tools/testing/selftests/lkdtm/config @@ -2,7 +2,7 @@ CONFIG_LKDTM=y CONFIG_DEBUG_LIST=y CONFIG_SLAB_FREELIST_HARDENED=y CONFIG_FORTIFY_SOURCE=y -CONFIG_GCC_PLUGIN_STACKLEAK=y +CONFIG_KSTACK_ERASE=y CONFIG_HARDENED_USERCOPY=y CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT=y CONFIG_INIT_ON_FREE_DEFAULT_ON=y -- cgit v1.2.3 From 9ea1e8d28add49ab3c1ecfa43f08d92ee23f3e33 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 17 Jul 2025 16:25:07 -0700 Subject: stackleak: Rename stackleak_track_stack to __sanitizer_cov_stack_depth The Clang stack depth tracking implementation has a fixed name for the stack depth tracking callback, "__sanitizer_cov_stack_depth", so rename the GCC plugin function to match since the plugin has no external dependencies on naming. Link: https://lore.kernel.org/r/20250717232519.2984886-2-kees@kernel.org Signed-off-by: Kees Cook --- include/linux/kstack_erase.h | 2 +- kernel/kstack_erase.c | 4 +-- scripts/gcc-plugins/stackleak_plugin.c | 52 +++++++++++++++++----------------- security/Kconfig.hardening | 4 +-- tools/objtool/check.c | 2 +- 5 files changed, 32 insertions(+), 32 deletions(-) (limited to 'scripts') diff --git a/include/linux/kstack_erase.h b/include/linux/kstack_erase.h index 4e432eefa4d0..bf3bf1905557 100644 --- a/include/linux/kstack_erase.h +++ b/include/linux/kstack_erase.h @@ -80,7 +80,7 @@ static inline void stackleak_task_init(struct task_struct *t) asmlinkage void noinstr stackleak_erase(void); asmlinkage void noinstr stackleak_erase_on_task_stack(void); asmlinkage void noinstr stackleak_erase_off_task_stack(void); -void __no_caller_saved_registers noinstr stackleak_track_stack(void); +void __no_caller_saved_registers noinstr __sanitizer_cov_stack_depth(void); #else /* !CONFIG_KSTACK_ERASE */ static inline void stackleak_task_init(struct task_struct *t) { } diff --git a/kernel/kstack_erase.c b/kernel/kstack_erase.c index 201b846f8345..e49bb88b4f0a 100644 --- a/kernel/kstack_erase.c +++ b/kernel/kstack_erase.c @@ -156,7 +156,7 @@ asmlinkage void noinstr stackleak_erase_off_task_stack(void) __stackleak_erase(false); } -void __used __no_caller_saved_registers noinstr stackleak_track_stack(void) +void __used __no_caller_saved_registers noinstr __sanitizer_cov_stack_depth(void) { unsigned long sp = current_stack_pointer; @@ -174,4 +174,4 @@ void __used __no_caller_saved_registers noinstr stackleak_track_stack(void) current->lowest_stack = sp; } } -EXPORT_SYMBOL(stackleak_track_stack); +EXPORT_SYMBOL(__sanitizer_cov_stack_depth); diff --git a/scripts/gcc-plugins/stackleak_plugin.c b/scripts/gcc-plugins/stackleak_plugin.c index d20c47d21ad8..e486488c867d 100644 --- a/scripts/gcc-plugins/stackleak_plugin.c +++ b/scripts/gcc-plugins/stackleak_plugin.c @@ -9,7 +9,7 @@ * any of the gcc libraries * * This gcc plugin is needed for tracking the lowest border of the kernel stack. - * It instruments the kernel code inserting stackleak_track_stack() calls: + * It instruments the kernel code inserting __sanitizer_cov_stack_depth() calls: * - after alloca(); * - for the functions with a stack frame size greater than or equal * to the "track-min-size" plugin parameter. @@ -33,7 +33,7 @@ __visible int plugin_is_GPL_compatible; static int track_frame_size = -1; static bool build_for_x86 = false; -static const char track_function[] = "stackleak_track_stack"; +static const char track_function[] = "__sanitizer_cov_stack_depth"; static bool disable = false; static bool verbose = false; @@ -58,7 +58,7 @@ static void add_stack_tracking_gcall(gimple_stmt_iterator *gsi, bool after) cgraph_node_ptr node; basic_block bb; - /* Insert calling stackleak_track_stack() */ + /* Insert calling __sanitizer_cov_stack_depth() */ stmt = gimple_build_call(track_function_decl, 0); gimple_call = as_a_gcall(stmt); if (after) @@ -120,12 +120,12 @@ static void add_stack_tracking_gasm(gimple_stmt_iterator *gsi, bool after) gcc_assert(build_for_x86); /* - * Insert calling stackleak_track_stack() in asm: - * asm volatile("call stackleak_track_stack" + * Insert calling __sanitizer_cov_stack_depth() in asm: + * asm volatile("call __sanitizer_cov_stack_depth" * :: "r" (current_stack_pointer)) * Use ASM_CALL_CONSTRAINT trick from arch/x86/include/asm/asm.h. * This constraint is taken into account during gcc shrink-wrapping - * optimization. It is needed to be sure that stackleak_track_stack() + * optimization. It is needed to be sure that __sanitizer_cov_stack_depth() * call is inserted after the prologue of the containing function, * when the stack frame is prepared. */ @@ -137,7 +137,7 @@ static void add_stack_tracking_gasm(gimple_stmt_iterator *gsi, bool after) input = build_tree_list(NULL_TREE, build_const_char_string(2, "r")); input = chainon(NULL_TREE, build_tree_list(input, sp_decl)); vec_safe_push(inputs, input); - asm_call = gimple_build_asm_vec("call stackleak_track_stack", + asm_call = gimple_build_asm_vec("call __sanitizer_cov_stack_depth", inputs, NULL, NULL, NULL); gimple_asm_set_volatile(asm_call, true); if (after) @@ -151,11 +151,11 @@ static void add_stack_tracking(gimple_stmt_iterator *gsi, bool after) { /* * The 'no_caller_saved_registers' attribute is used for - * stackleak_track_stack(). If the compiler supports this attribute for - * the target arch, we can add calling stackleak_track_stack() in asm. + * __sanitizer_cov_stack_depth(). If the compiler supports this attribute for + * the target arch, we can add calling __sanitizer_cov_stack_depth() in asm. * That improves performance: we avoid useless operations with the * caller-saved registers in the functions from which we will remove - * stackleak_track_stack() call during the stackleak_cleanup pass. + * __sanitizer_cov_stack_depth() call during the stackleak_cleanup pass. */ if (lookup_attribute_spec(get_identifier("no_caller_saved_registers"))) add_stack_tracking_gasm(gsi, after); @@ -165,7 +165,7 @@ static void add_stack_tracking(gimple_stmt_iterator *gsi, bool after) /* * Work with the GIMPLE representation of the code. Insert the - * stackleak_track_stack() call after alloca() and into the beginning + * __sanitizer_cov_stack_depth() call after alloca() and into the beginning * of the function if it is not instrumented. */ static unsigned int stackleak_instrument_execute(void) @@ -205,7 +205,7 @@ static unsigned int stackleak_instrument_execute(void) DECL_NAME_POINTER(current_function_decl)); } - /* Insert stackleak_track_stack() call after alloca() */ + /* Insert __sanitizer_cov_stack_depth() call after alloca() */ add_stack_tracking(&gsi, true); if (bb == entry_bb) prologue_instrumented = true; @@ -241,7 +241,7 @@ static unsigned int stackleak_instrument_execute(void) return 0; } - /* Insert stackleak_track_stack() call at the function beginning */ + /* Insert __sanitizer_cov_stack_depth() call at the function beginning */ bb = entry_bb; if (!single_pred_p(bb)) { /* gcc_assert(bb_loop_depth(bb) || @@ -270,15 +270,15 @@ static void remove_stack_tracking_gcall(void) rtx_insn *insn, *next; /* - * Find stackleak_track_stack() calls. Loop through the chain of insns, + * Find __sanitizer_cov_stack_depth() calls. Loop through the chain of insns, * which is an RTL representation of the code for a function. * * The example of a matching insn: - * (call_insn 8 4 10 2 (call (mem (symbol_ref ("stackleak_track_stack") - * [flags 0x41] ) - * [0 stackleak_track_stack S1 A8]) (0)) 675 {*call} (expr_list - * (symbol_ref ("stackleak_track_stack") [flags 0x41] ) (expr_list (0) (nil))) (nil)) + * (call_insn 8 4 10 2 (call (mem (symbol_ref ("__sanitizer_cov_stack_depth") + * [flags 0x41] ) + * [0 __sanitizer_cov_stack_depth S1 A8]) (0)) 675 {*call} (expr_list + * (symbol_ref ("__sanitizer_cov_stack_depth") [flags 0x41] ) (expr_list (0) (nil))) (nil)) */ for (insn = get_insns(); insn; insn = next) { rtx body; @@ -318,7 +318,7 @@ static void remove_stack_tracking_gcall(void) if (SYMBOL_REF_DECL(body) != track_function_decl) continue; - /* Delete the stackleak_track_stack() call */ + /* Delete the __sanitizer_cov_stack_depth() call */ delete_insn_and_edges(insn); #if BUILDING_GCC_VERSION < 8000 if (GET_CODE(next) == NOTE && @@ -340,12 +340,12 @@ static bool remove_stack_tracking_gasm(void) gcc_assert(build_for_x86); /* - * Find stackleak_track_stack() asm calls. Loop through the chain of + * Find __sanitizer_cov_stack_depth() asm calls. Loop through the chain of * insns, which is an RTL representation of the code for a function. * * The example of a matching insn: * (insn 11 5 12 2 (parallel [ (asm_operands/v - * ("call stackleak_track_stack") ("") 0 + * ("call __sanitizer_cov_stack_depth") ("") 0 * [ (reg/v:DI 7 sp [ current_stack_pointer ]) ] * [ (asm_input:DI ("r")) ] []) * (clobber (reg:CC 17 flags)) ]) -1 (nil)) @@ -375,7 +375,7 @@ static bool remove_stack_tracking_gasm(void) continue; if (strcmp(ASM_OPERANDS_TEMPLATE(body), - "call stackleak_track_stack")) { + "call __sanitizer_cov_stack_depth")) { continue; } @@ -389,7 +389,7 @@ static bool remove_stack_tracking_gasm(void) /* * Work with the RTL representation of the code. - * Remove the unneeded stackleak_track_stack() calls from the functions + * Remove the unneeded __sanitizer_cov_stack_depth() calls from the functions * which don't call alloca() and don't have a large enough stack frame size. */ static unsigned int stackleak_cleanup_execute(void) @@ -474,13 +474,13 @@ static bool stackleak_gate(void) return track_frame_size >= 0; } -/* Build the function declaration for stackleak_track_stack() */ +/* Build the function declaration for __sanitizer_cov_stack_depth() */ static void stackleak_start_unit(void *gcc_data __unused, void *user_data __unused) { tree fntype; - /* void stackleak_track_stack(void) */ + /* void __sanitizer_cov_stack_depth(void) */ fntype = build_function_type_list(void_type_node, NULL_TREE); track_function_decl = build_fn_decl(track_function, fntype); DECL_ASSEMBLER_NAME(track_function_decl); /* for LTO */ diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening index 125b35e2ef0f..f7aa2024ab25 100644 --- a/security/Kconfig.hardening +++ b/security/Kconfig.hardening @@ -129,8 +129,8 @@ config KSTACK_ERASE_TRACK_MIN_SIZE help The KSTACK_ERASE option instruments the kernel code for tracking the lowest border of the kernel stack (and for some other purposes). - It inserts the stackleak_track_stack() call for the functions with - a stack frame size greater than or equal to this parameter. + It inserts the __sanitizer_cov_stack_depth() call for the functions + with a stack frame size greater than or equal to this parameter. If unsure, leave the default value 100. config KSTACK_ERASE_METRICS diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 5451bdbcf84a..b7e24684f592 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1193,7 +1193,7 @@ static const char *uaccess_safe_builtin[] = { "__ubsan_handle_shift_out_of_bounds", "__ubsan_handle_load_invalid_value", /* KSTACK_ERASE */ - "stackleak_track_stack", + "__sanitizer_cov_stack_depth", /* TRACE_BRANCH_PROFILING */ "ftrace_likely_update", /* STACKPROTECTOR */ -- cgit v1.2.3 From 76261fc7d1be3fde06efed859cb10c95b1204055 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 17 Jul 2025 16:25:08 -0700 Subject: stackleak: Split KSTACK_ERASE_CFLAGS from GCC_PLUGINS_CFLAGS In preparation for Clang stack depth tracking for KSTACK_ERASE, split the stackleak-specific cflags out of GCC_PLUGINS_CFLAGS into KSTACK_ERASE_CFLAGS. Link: https://lore.kernel.org/r/20250717232519.2984886-3-kees@kernel.org Signed-off-by: Kees Cook --- MAINTAINERS | 2 ++ Makefile | 1 + arch/arm/vdso/Makefile | 2 +- arch/arm64/kernel/vdso/Makefile | 3 ++- arch/sparc/vdso/Makefile | 3 ++- arch/x86/entry/vdso/Makefile | 3 ++- scripts/Makefile.gcc-plugins | 16 ++-------------- scripts/Makefile.kstack_erase | 15 +++++++++++++++ 8 files changed, 27 insertions(+), 18 deletions(-) create mode 100644 scripts/Makefile.kstack_erase (limited to 'scripts') diff --git a/MAINTAINERS b/MAINTAINERS index 470d159d8fea..a7e5d0a551be 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13094,6 +13094,8 @@ F: kernel/kstack_erase.c F: lib/tests/randstruct_kunit.c F: lib/tests/usercopy_kunit.c F: mm/usercopy.c +F: scripts/Makefile.kstack_erase +F: scripts/Makefile.randstruct F: security/Kconfig.hardening K: \b(add|choose)_random_kstack_offset\b K: \b__check_(object_size|heap_object)\b diff --git a/Makefile b/Makefile index ba0827a1fccd..57be26ff5f84 100644 --- a/Makefile +++ b/Makefile @@ -1086,6 +1086,7 @@ include-$(CONFIG_KMSAN) += scripts/Makefile.kmsan include-$(CONFIG_UBSAN) += scripts/Makefile.ubsan include-$(CONFIG_KCOV) += scripts/Makefile.kcov include-$(CONFIG_RANDSTRUCT) += scripts/Makefile.randstruct +include-$(CONFIG_KSTACK_ERASE) += scripts/Makefile.kstack_erase include-$(CONFIG_AUTOFDO_CLANG) += scripts/Makefile.autofdo include-$(CONFIG_PROPELLER_CLANG) += scripts/Makefile.propeller include-$(CONFIG_GCC_PLUGINS) += scripts/Makefile.gcc-plugins diff --git a/arch/arm/vdso/Makefile b/arch/arm/vdso/Makefile index cb044bfd145d..cf8cd39ab804 100644 --- a/arch/arm/vdso/Makefile +++ b/arch/arm/vdso/Makefile @@ -26,7 +26,7 @@ CPPFLAGS_vdso.lds += -P -C -U$(ARCH) CFLAGS_REMOVE_vdso.o = -pg # Force -O2 to avoid libgcc dependencies -CFLAGS_REMOVE_vgettimeofday.o = -pg -Os $(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS) +CFLAGS_REMOVE_vgettimeofday.o = -pg -Os $(RANDSTRUCT_CFLAGS) $(KSTACK_ERASE_CFLAGS) $(GCC_PLUGINS_CFLAGS) ifeq ($(c-gettimeofday-y),) CFLAGS_vgettimeofday.o = -O2 else diff --git a/arch/arm64/kernel/vdso/Makefile b/arch/arm64/kernel/vdso/Makefile index 5e27e46aa496..7dec05dd33b7 100644 --- a/arch/arm64/kernel/vdso/Makefile +++ b/arch/arm64/kernel/vdso/Makefile @@ -36,7 +36,8 @@ ccflags-y += -DDISABLE_BRANCH_PROFILING -DBUILD_VDSO # -Wmissing-prototypes and -Wmissing-declarations are removed from # the CFLAGS to make possible to build the kernel with CONFIG_WERROR enabled. CC_FLAGS_REMOVE_VDSO := $(CC_FLAGS_FTRACE) -Os $(CC_FLAGS_SCS) \ - $(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS) \ + $(RANDSTRUCT_CFLAGS) $(KSTACK_ERASE_CFLAGS) \ + $(GCC_PLUGINS_CFLAGS) \ $(CC_FLAGS_LTO) $(CC_FLAGS_CFI) \ -Wmissing-prototypes -Wmissing-declarations diff --git a/arch/sparc/vdso/Makefile b/arch/sparc/vdso/Makefile index fdc4a8f5a49c..683b2d408224 100644 --- a/arch/sparc/vdso/Makefile +++ b/arch/sparc/vdso/Makefile @@ -48,7 +48,7 @@ CFL := $(PROFILING) -mcmodel=medlow -fPIC -O2 -fasynchronous-unwind-tables -m64 SPARC_REG_CFLAGS = -ffixed-g4 -ffixed-g5 $(call cc-option,-fcall-used-g5) $(call cc-option,-fcall-used-g7) -$(vobjs): KBUILD_CFLAGS := $(filter-out $(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS) $(SPARC_REG_CFLAGS),$(KBUILD_CFLAGS)) $(CFL) +$(vobjs): KBUILD_CFLAGS := $(filter-out $(RANDSTRUCT_CFLAGS) $(KSTACK_ERASE_CFLAGS) $(GCC_PLUGINS_CFLAGS) $(SPARC_REG_CFLAGS),$(KBUILD_CFLAGS)) $(CFL) # # vDSO code runs in userspace and -pg doesn't help with profiling anyway. @@ -79,6 +79,7 @@ KBUILD_CFLAGS_32 := $(filter-out -m64,$(KBUILD_CFLAGS)) KBUILD_CFLAGS_32 := $(filter-out -mcmodel=medlow,$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out -fno-pic,$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out $(RANDSTRUCT_CFLAGS),$(KBUILD_CFLAGS_32)) +KBUILD_CFLAGS_32 := $(filter-out $(KSTACK_ERASE_CFLAGS),$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out $(GCC_PLUGINS_CFLAGS),$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out $(SPARC_REG_CFLAGS),$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 += -m32 -msoft-float -fpic diff --git a/arch/x86/entry/vdso/Makefile b/arch/x86/entry/vdso/Makefile index 54d3e9774d62..f247f5f5cb44 100644 --- a/arch/x86/entry/vdso/Makefile +++ b/arch/x86/entry/vdso/Makefile @@ -62,7 +62,7 @@ ifneq ($(RETPOLINE_VDSO_CFLAGS),) endif endif -$(vobjs): KBUILD_CFLAGS := $(filter-out $(PADDING_CFLAGS) $(CC_FLAGS_LTO) $(CC_FLAGS_CFI) $(RANDSTRUCT_CFLAGS) $(GCC_PLUGINS_CFLAGS) $(RETPOLINE_CFLAGS),$(KBUILD_CFLAGS)) $(CFL) +$(vobjs): KBUILD_CFLAGS := $(filter-out $(PADDING_CFLAGS) $(CC_FLAGS_LTO) $(CC_FLAGS_CFI) $(RANDSTRUCT_CFLAGS) $(KSTACK_ERASE_CFLAGS) $(GCC_PLUGINS_CFLAGS) $(RETPOLINE_CFLAGS),$(KBUILD_CFLAGS)) $(CFL) $(vobjs): KBUILD_AFLAGS += -DBUILD_VDSO # @@ -123,6 +123,7 @@ KBUILD_CFLAGS_32 := $(filter-out -mcmodel=kernel,$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out -fno-pic,$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out -mfentry,$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out $(RANDSTRUCT_CFLAGS),$(KBUILD_CFLAGS_32)) +KBUILD_CFLAGS_32 := $(filter-out $(KSTACK_ERASE_CFLAGS),$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out $(GCC_PLUGINS_CFLAGS),$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out $(RETPOLINE_CFLAGS),$(KBUILD_CFLAGS_32)) KBUILD_CFLAGS_32 := $(filter-out $(CC_FLAGS_LTO),$(KBUILD_CFLAGS_32)) diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins index 28b8867c4e84..b0e1423b09c2 100644 --- a/scripts/Makefile.gcc-plugins +++ b/scripts/Makefile.gcc-plugins @@ -8,20 +8,6 @@ ifdef CONFIG_GCC_PLUGIN_LATENT_ENTROPY endif export DISABLE_LATENT_ENTROPY_PLUGIN -gcc-plugin-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak_plugin.so -gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \ - += -DSTACKLEAK_PLUGIN -gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \ - += -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_KSTACK_ERASE_TRACK_MIN_SIZE) -gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \ - += -fplugin-arg-stackleak_plugin-arch=$(SRCARCH) -gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK_VERBOSE) \ - += -fplugin-arg-stackleak_plugin-verbose -ifdef CONFIG_GCC_PLUGIN_STACKLEAK - DISABLE_KSTACK_ERASE += -fplugin-arg-stackleak_plugin-disable -endif -export DISABLE_KSTACK_ERASE - # All the plugin CFLAGS are collected here in case a build target needs to # filter them out of the KBUILD_CFLAGS. GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y)) -DGCC_PLUGINS @@ -34,6 +20,8 @@ KBUILD_CFLAGS += $(GCC_PLUGINS_CFLAGS) # be included in GCC_PLUGIN so they can get built. gcc-plugin-external-$(CONFIG_GCC_PLUGIN_RANDSTRUCT) \ += randomize_layout_plugin.so +gcc-plugin-external-$(CONFIG_GCC_PLUGIN_STACKLEAK) \ + += stackleak_plugin.so # All enabled GCC plugins are collected here for building in # scripts/gcc-scripts/Makefile. diff --git a/scripts/Makefile.kstack_erase b/scripts/Makefile.kstack_erase new file mode 100644 index 000000000000..5223d3a35817 --- /dev/null +++ b/scripts/Makefile.kstack_erase @@ -0,0 +1,15 @@ +# SPDX-License-Identifier: GPL-2.0 + +ifdef CONFIG_GCC_PLUGIN_STACKLEAK +kstack-erase-cflags-y += -fplugin=$(objtree)/scripts/gcc-plugins/stackleak_plugin.so +kstack-erase-cflags-y += -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_KSTACK_ERASE_TRACK_MIN_SIZE) +kstack-erase-cflags-y += -fplugin-arg-stackleak_plugin-arch=$(SRCARCH) +kstack-erase-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK_VERBOSE) += -fplugin-arg-stackleak_plugin-verbose +DISABLE_KSTACK_ERASE := -fplugin-arg-stackleak_plugin-disable +endif + +KSTACK_ERASE_CFLAGS := $(kstack-erase-cflags-y) + +export STACKLEAK_CFLAGS DISABLE_KSTACK_ERASE + +KBUILD_CFLAGS += $(KSTACK_ERASE_CFLAGS) -- cgit v1.2.3 From 4d6d0a6263babf7c43faa55de4fa3c6637dec624 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Fri, 4 Jul 2025 10:48:38 -0400 Subject: tracing: Remove redundant config HAVE_FTRACE_MCOUNT_RECORD Ftrace is tightly coupled with architecture specific code because it requires the use of trampolines written in assembly. This means that when a new feature or optimization is made, it must be done for all architectures. To simplify the approach, CONFIG_HAVE_FTRACE_* configs are added to denote which architecture has the new enhancement so that other architectures can still function until they too have been updated. The CONFIG_HAVE_FTRACE_MCOUNT was added to help simplify the DYNAMIC_FTRACE work, but now every architecture that implements DYNAMIC_FTRACE also has HAVE_FTRACE_MCOUNT set too, making it redundant with the HAVE_DYNAMIC_FTRACE. Remove the HAVE_FTRACE_MCOUNT config and use DYNAMIC_FTRACE directly where applicable. Link: https://lore.kernel.org/all/20250703154916.48e3ada7@gandalf.local.home/ Cc: Masami Hiramatsu Cc: Mathieu Desnoyers Cc: Mark Rutland Cc: Linus Torvalds Link: https://lore.kernel.org/20250704104838.27a18690@gandalf.local.home Signed-off-by: Steven Rostedt (Google) --- Documentation/trace/ftrace-design.rst | 12 ++++-------- arch/arm/Kconfig | 1 - arch/arm64/Kconfig | 1 - arch/csky/Kconfig | 1 - arch/loongarch/Kconfig | 1 - arch/microblaze/Kconfig | 1 - arch/mips/Kconfig | 1 - arch/parisc/Kconfig | 1 - arch/powerpc/Kconfig | 1 - arch/riscv/Kconfig | 1 - arch/s390/Kconfig | 1 - arch/sh/Kconfig | 1 - arch/sparc/Kconfig | 1 - arch/x86/Kconfig | 1 - include/asm-generic/vmlinux.lds.h | 2 +- include/linux/ftrace.h | 2 +- include/linux/kernel.h | 6 +++--- include/linux/module.h | 2 +- kernel/module/main.c | 2 +- kernel/trace/Kconfig | 18 ++++-------------- kernel/trace/ftrace.c | 4 ---- scripts/recordmcount.pl | 2 +- 22 files changed, 16 insertions(+), 47 deletions(-) (limited to 'scripts') diff --git a/Documentation/trace/ftrace-design.rst b/Documentation/trace/ftrace-design.rst index dc82d64b3a44..8f4fab3f9324 100644 --- a/Documentation/trace/ftrace-design.rst +++ b/Documentation/trace/ftrace-design.rst @@ -238,19 +238,15 @@ You need very few things to get the syscalls tracing in an arch. - Tag this arch as HAVE_SYSCALL_TRACEPOINTS. -HAVE_FTRACE_MCOUNT_RECORD -------------------------- +HAVE_DYNAMIC_FTRACE +------------------- See scripts/recordmcount.pl for more info. Just fill in the arch-specific details for how to locate the addresses of mcount call sites via objdump. This option doesn't make much sense without also implementing dynamic ftrace. - -HAVE_DYNAMIC_FTRACE -------------------- - -You will first need HAVE_FTRACE_MCOUNT_RECORD and HAVE_FUNCTION_TRACER, so -scroll your reader back up if you got over eager. +You will first need HAVE_FUNCTION_TRACER, so scroll your reader back up if you +got over eager. Once those are out of the way, you will need to implement: - asm/ftrace.h: diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 3072731fe09c..33cc9dbb7f68 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -107,7 +107,6 @@ config ARM select HAVE_EFFICIENT_UNALIGNED_ACCESS if (CPU_V6 || CPU_V6K || CPU_V7) && MMU select HAVE_EXIT_THREAD select HAVE_GUP_FAST if ARM_LPAE - select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL select HAVE_FUNCTION_ERROR_INJECTION select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER if !XIP_KERNEL diff --git a/arch/arm64/Kconfig b/arch/arm64/Kconfig index 55fc331af337..f943a07db139 100644 --- a/arch/arm64/Kconfig +++ b/arch/arm64/Kconfig @@ -223,7 +223,6 @@ config ARM64 select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_GUP_FAST select HAVE_FTRACE_GRAPH_FUNC - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_ERROR_INJECTION select HAVE_FUNCTION_GRAPH_FREGS diff --git a/arch/csky/Kconfig b/arch/csky/Kconfig index acc431c331b0..4331313a42ff 100644 --- a/arch/csky/Kconfig +++ b/arch/csky/Kconfig @@ -80,7 +80,6 @@ config CSKY select HAVE_FUNCTION_TRACER select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_ERROR_INJECTION - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_KERNEL_GZIP select HAVE_KERNEL_LZO select HAVE_KERNEL_LZMA diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 4b19f93379a1..bead8266dc5c 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -144,7 +144,6 @@ config LOONGARCH select HAVE_EXIT_THREAD select HAVE_GUP_FAST select HAVE_FTRACE_GRAPH_FUNC - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_ERROR_INJECTION select HAVE_FUNCTION_GRAPH_FREGS diff --git a/arch/microblaze/Kconfig b/arch/microblaze/Kconfig index f18ec02ddeb2..484ebb3baedf 100644 --- a/arch/microblaze/Kconfig +++ b/arch/microblaze/Kconfig @@ -28,7 +28,6 @@ config MICROBLAZE select HAVE_DEBUG_KMEMLEAK select HAVE_DMA_CONTIGUOUS select HAVE_DYNAMIC_FTRACE - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER select HAVE_PAGE_SIZE_4KB diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig index 1e48184ecf1e..268730824f75 100644 --- a/arch/mips/Kconfig +++ b/arch/mips/Kconfig @@ -73,7 +73,6 @@ config MIPS select HAVE_EBPF_JIT if !CPU_MICROMIPS select HAVE_EXIT_THREAD select HAVE_GUP_FAST - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER select HAVE_GCC_PLUGINS diff --git a/arch/parisc/Kconfig b/arch/parisc/Kconfig index fcc5973f7519..2efa4b08b7b8 100644 --- a/arch/parisc/Kconfig +++ b/arch/parisc/Kconfig @@ -81,7 +81,6 @@ config PARISC select HAVE_KPROBES select HAVE_KRETPROBES select HAVE_DYNAMIC_FTRACE if $(cc-option,-fpatchable-function-entry=1,1) - select HAVE_FTRACE_MCOUNT_RECORD if HAVE_DYNAMIC_FTRACE select FTRACE_MCOUNT_USE_PATCHABLE_FUNCTION_ENTRY if DYNAMIC_FTRACE select HAVE_KPROBES_ON_FTRACE select HAVE_DYNAMIC_FTRACE_WITH_REGS diff --git a/arch/powerpc/Kconfig b/arch/powerpc/Kconfig index c3e0cc83f120..cb4fb8d73300 100644 --- a/arch/powerpc/Kconfig +++ b/arch/powerpc/Kconfig @@ -246,7 +246,6 @@ config PPC select HAVE_EFFICIENT_UNALIGNED_ACCESS select HAVE_GUP_FAST select HAVE_FTRACE_GRAPH_FUNC - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_DESCRIPTORS if PPC64_ELF_ABI_V1 select HAVE_FUNCTION_ERROR_INJECTION diff --git a/arch/riscv/Kconfig b/arch/riscv/Kconfig index d71ea0f4466f..62ec265e2962 100644 --- a/arch/riscv/Kconfig +++ b/arch/riscv/Kconfig @@ -159,7 +159,6 @@ config RISCV select HAVE_DYNAMIC_FTRACE_WITH_CALL_OPS if (DYNAMIC_FTRACE_WITH_ARGS && !CFI_CLANG) select HAVE_DYNAMIC_FTRACE_WITH_ARGS if HAVE_DYNAMIC_FTRACE select HAVE_FTRACE_GRAPH_FUNC - select HAVE_FTRACE_MCOUNT_RECORD if !XIP_KERNEL select HAVE_FUNCTION_GRAPH_TRACER if HAVE_DYNAMIC_FTRACE_WITH_ARGS select HAVE_FUNCTION_GRAPH_FREGS select HAVE_FUNCTION_TRACER if !XIP_KERNEL diff --git a/arch/s390/Kconfig b/arch/s390/Kconfig index 0c16dc443e2f..d956e85f0465 100644 --- a/arch/s390/Kconfig +++ b/arch/s390/Kconfig @@ -199,7 +199,6 @@ config S390 select HAVE_GUP_FAST select HAVE_FENTRY select HAVE_FTRACE_GRAPH_FUNC - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_ARG_ACCESS_API select HAVE_FUNCTION_ERROR_INJECTION select HAVE_FUNCTION_GRAPH_FREGS diff --git a/arch/sh/Kconfig b/arch/sh/Kconfig index 89185af7bcc9..d5795067befa 100644 --- a/arch/sh/Kconfig +++ b/arch/sh/Kconfig @@ -40,7 +40,6 @@ config SUPERH select HAVE_GUP_FAST if MMU select HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_TRACER - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_HW_BREAKPOINT select HAVE_IOREMAP_PROT if MMU && !X2TLB select HAVE_KERNEL_BZIP2 diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 0f88123925a4..f307e730446c 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -78,7 +78,6 @@ config SPARC64 select MMU_GATHER_NO_FLUSH_CACHE select HAVE_ARCH_TRANSPARENT_HUGEPAGE select HAVE_DYNAMIC_FTRACE - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_PAGE_SIZE_8KB select HAVE_SYSCALL_TRACEPOINTS select HAVE_CONTEXT_TRACKING_USER diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index 71019b3b54ea..eb07f236ce53 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -244,7 +244,6 @@ config X86 select HAVE_GUP_FAST select HAVE_FENTRY if X86_64 || DYNAMIC_FTRACE select HAVE_FTRACE_GRAPH_FUNC if HAVE_FUNCTION_GRAPH_TRACER - select HAVE_FTRACE_MCOUNT_RECORD select HAVE_FUNCTION_GRAPH_FREGS if HAVE_FUNCTION_GRAPH_TRACER select HAVE_FUNCTION_GRAPH_TRACER if X86_32 || (X86_64 && DYNAMIC_FTRACE) select HAVE_FUNCTION_TRACER diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index fa5f19b8d53a..ae2d2359b79e 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -167,7 +167,7 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG) #define FTRACE_STUB_HACK #endif -#ifdef CONFIG_FTRACE_MCOUNT_RECORD +#ifdef CONFIG_DYNAMIC_FTRACE /* * The ftrace call sites are logged to a section whose name depends on the * compiler option used. A given kernel image will only use one, AKA diff --git a/include/linux/ftrace.h b/include/linux/ftrace.h index b672ca15f265..7ded7df6e9b5 100644 --- a/include/linux/ftrace.h +++ b/include/linux/ftrace.h @@ -1108,7 +1108,7 @@ static __always_inline unsigned long get_lock_parent_ip(void) # define trace_preempt_off(a0, a1) do { } while (0) #endif -#ifdef CONFIG_FTRACE_MCOUNT_RECORD +#ifdef CONFIG_DYNAMIC_FTRACE extern void ftrace_init(void); #ifdef CC_USING_PATCHABLE_FUNCTION_ENTRY #define FTRACE_CALLSITE_SECTION "__patchable_function_entries" diff --git a/include/linux/kernel.h b/include/linux/kernel.h index 1cce1f6410a9..989315dabb86 100644 --- a/include/linux/kernel.h +++ b/include/linux/kernel.h @@ -373,9 +373,9 @@ ftrace_vprintk(const char *fmt, va_list ap) static inline void ftrace_dump(enum ftrace_dump_mode oops_dump_mode) { } #endif /* CONFIG_TRACING */ -/* Rebuild everything on CONFIG_FTRACE_MCOUNT_RECORD */ -#ifdef CONFIG_FTRACE_MCOUNT_RECORD -# define REBUILD_DUE_TO_FTRACE_MCOUNT_RECORD +/* Rebuild everything on CONFIG_DYNAMIC_FTRACE */ +#ifdef CONFIG_DYNAMIC_FTRACE +# define REBUILD_DUE_TO_DYNAMIC_FTRACE #endif /* Permissions on a sysfs file: you didn't miss the 0 prefix did you? */ diff --git a/include/linux/module.h b/include/linux/module.h index 5faa1fb1f4b4..800e6fde9bf7 100644 --- a/include/linux/module.h +++ b/include/linux/module.h @@ -539,7 +539,7 @@ struct module { struct trace_eval_map **trace_evals; unsigned int num_trace_evals; #endif -#ifdef CONFIG_FTRACE_MCOUNT_RECORD +#ifdef CONFIG_DYNAMIC_FTRACE unsigned int num_ftrace_callsites; unsigned long *ftrace_callsites; #endif diff --git a/kernel/module/main.c b/kernel/module/main.c index 413ac6ea3702..58d36f8cef0d 100644 --- a/kernel/module/main.c +++ b/kernel/module/main.c @@ -2639,7 +2639,7 @@ static int find_module_sections(struct module *mod, struct load_info *info) sizeof(*mod->trace_bprintk_fmt_start), &mod->num_trace_bprintk_fmt); #endif -#ifdef CONFIG_FTRACE_MCOUNT_RECORD +#ifdef CONFIG_DYNAMIC_FTRACE /* sechdrs[0].sh_size is always zero */ mod->ftrace_callsites = section_objs(info, FTRACE_CALLSITE_SECTION, sizeof(*mod->ftrace_callsites), diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig index 28afc6941e7a..9f2b1661a8ac 100644 --- a/kernel/trace/Kconfig +++ b/kernel/trace/Kconfig @@ -74,11 +74,6 @@ config HAVE_DYNAMIC_FTRACE_NO_PATCHABLE If the architecture generates __patchable_function_entries sections but does not want them included in the ftrace locations. -config HAVE_FTRACE_MCOUNT_RECORD - bool - help - See Documentation/trace/ftrace-design.rst - config HAVE_SYSCALL_TRACEPOINTS bool help @@ -803,27 +798,22 @@ config BPF_KPROBE_OVERRIDE Allows BPF to override the execution of a probed function and set a different return value. This is used for error injection. -config FTRACE_MCOUNT_RECORD - def_bool y - depends on DYNAMIC_FTRACE - depends on HAVE_FTRACE_MCOUNT_RECORD - config FTRACE_MCOUNT_USE_PATCHABLE_FUNCTION_ENTRY bool - depends on FTRACE_MCOUNT_RECORD + depends on DYNAMIC_FTRACE config FTRACE_MCOUNT_USE_CC def_bool y depends on $(cc-option,-mrecord-mcount) depends on !FTRACE_MCOUNT_USE_PATCHABLE_FUNCTION_ENTRY - depends on FTRACE_MCOUNT_RECORD + depends on DYNAMIC_FTRACE config FTRACE_MCOUNT_USE_OBJTOOL def_bool y depends on HAVE_OBJTOOL_MCOUNT depends on !FTRACE_MCOUNT_USE_PATCHABLE_FUNCTION_ENTRY depends on !FTRACE_MCOUNT_USE_CC - depends on FTRACE_MCOUNT_RECORD + depends on DYNAMIC_FTRACE select OBJTOOL config FTRACE_MCOUNT_USE_RECORDMCOUNT @@ -831,7 +821,7 @@ config FTRACE_MCOUNT_USE_RECORDMCOUNT depends on !FTRACE_MCOUNT_USE_PATCHABLE_FUNCTION_ENTRY depends on !FTRACE_MCOUNT_USE_CC depends on !FTRACE_MCOUNT_USE_OBJTOOL - depends on FTRACE_MCOUNT_RECORD + depends on DYNAMIC_FTRACE config TRACING_MAP bool diff --git a/kernel/trace/ftrace.c b/kernel/trace/ftrace.c index 4203fad56b6c..00b76d450a89 100644 --- a/kernel/trace/ftrace.c +++ b/kernel/trace/ftrace.c @@ -1042,10 +1042,6 @@ static struct ftrace_ops *removed_ops; */ static bool update_all_ops; -#ifndef CONFIG_FTRACE_MCOUNT_RECORD -# error Dynamic ftrace depends on MCOUNT_RECORD -#endif - struct ftrace_func_probe { struct ftrace_probe_ops *probe_ops; struct ftrace_ops ops; diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl index 0871b2e92584..861b56dda64e 100755 --- a/scripts/recordmcount.pl +++ b/scripts/recordmcount.pl @@ -359,7 +359,7 @@ if ($arch eq "x86_64") { $mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*R_CKCORE_PCREL_JSR_IMM26BY2\\s+_mcount\$"; $alignment = 2; } else { - die "Arch $arch is not supported with CONFIG_FTRACE_MCOUNT_RECORD"; + die "Arch $arch is not supported with CONFIG_DYNAMIC_FTRACE"; } my $text_found = 0; -- cgit v1.2.3 From 89b491bcf2d19516dd19b1f7a8872394a58b591b Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 1 Jul 2025 09:51:57 +0200 Subject: docs: nixify check-sysctl-docs Use "#!/usr/bin/env -S gawk -f" instead of "#!/bin/gawk". Needed for testing in nix environments as they only provide /usr/bin/env at the standard location. Signed-off-by: Joel Granados --- scripts/check-sysctl-docs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/check-sysctl-docs b/scripts/check-sysctl-docs index 20274c63e745..178dcf2888ff 100755 --- a/scripts/check-sysctl-docs +++ b/scripts/check-sysctl-docs @@ -1,4 +1,4 @@ -#!/usr/bin/gawk -f +#!/usr/bin/env -S gawk -f # SPDX-License-Identifier: GPL-2.0 # Script to check sysctl documentation against source files -- cgit v1.2.3 From be0aef10dca87a644affb087f01728386c19903a Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 1 Jul 2025 09:55:39 +0200 Subject: docs: Use skiplist when checking sysctl admin-guide Use a skiplist to "skip" the titles in the guide documentation (Documentation/admin-guide/sysctl/*) that are not sysctls. This will give a more accurate account of what sysctl are miss-documented. Signed-off-by: Joel Granados --- scripts/check-sysctl-docs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/check-sysctl-docs b/scripts/check-sysctl-docs index 178dcf2888ff..568197cb1c0a 100755 --- a/scripts/check-sysctl-docs +++ b/scripts/check-sysctl-docs @@ -17,6 +17,18 @@ BEGIN { print "Please specify the table to look for using the table variable" > "/dev/stderr" exit 1 } + + # Documentation title skiplist + skiplist[0] = "^Documentation for" + skiplist[1] = "Network core options$" + skiplist[2] = "POSIX message queues filesystem$" + skiplist[3] = "Configuration options" + skiplist[4] = ". /proc/sys/fs" + skiplist[5] = "^Introduction$" + skiplist[6] = "^seccomp$" + skiplist[7] = "^pty$" + skiplist[8] = "^firmware_config$" + skiplist[9] = "^random$" } # The following globals are used: @@ -53,10 +65,11 @@ function printentry(entry) { # Stage 1: build the list of documented entries FNR == NR && /^=+$/ { - if (prevline ~ /Documentation for/) { - # This is the main title - next - } + for (i in skiplist) { + if (prevline ~ skiplist[i]) { + next + } + } # The previous line is a section title, parse it $0 = prevline -- cgit v1.2.3 From e97a96baa527d8ce51db483d44599dff9ec62af0 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 1 Jul 2025 10:07:04 +0200 Subject: docs: Add awk section for ucount sysctl entries Adjust the sysctl table detection to include the macro pattern used for the ucount ctl_tables. This prevents falsly assigning them as non-documented ctl_tables Signed-off-by: Joel Granados --- scripts/check-sysctl-docs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'scripts') diff --git a/scripts/check-sysctl-docs b/scripts/check-sysctl-docs index 568197cb1c0a..3166012b9c6e 100755 --- a/scripts/check-sysctl-docs +++ b/scripts/check-sysctl-docs @@ -130,6 +130,14 @@ curtable && /\.procname[\t ]*=[\t ]*".+"/ { file[curentry] = FILENAME } +curtable && /UCOUNT_ENTRY.*/ { + match($0, /UCOUNT_ENTRY\("([^"]+)"\)/, names) + curentry = names[1] + if (debug) print "Adding entry " curentry " to table " curtable + entries[curtable][curentry]++ + file[curentry] = FILENAME +} + /register_sysctl.*/ { match($0, /register_sysctl(|_init|_sz)\("([^"]+)" *, *([^,)]+)/, tables) if (debug) print "Registering table " tables[3] " at " tables[2] -- cgit v1.2.3 From 999aab7f5645f8e5daf1a102a4c4e79275555cf8 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Tue, 1 Jul 2025 10:19:09 +0200 Subject: docs: Replace spaces with tabs in check-sysctl-docs Remove the combination of spaces and tabs in favor of just tabs. Signed-off-by: Joel Granados --- scripts/check-sysctl-docs | 163 +++++++++++++++++++++++----------------------- 1 file changed, 81 insertions(+), 82 deletions(-) (limited to 'scripts') diff --git a/scripts/check-sysctl-docs b/scripts/check-sysctl-docs index 3166012b9c6e..910fd8a9a268 100755 --- a/scripts/check-sysctl-docs +++ b/scripts/check-sysctl-docs @@ -13,10 +13,10 @@ # Specify -vdebug=1 to see debugging information BEGIN { - if (!table) { + if (!table) { print "Please specify the table to look for using the table variable" > "/dev/stderr" exit 1 - } + } # Documentation title skiplist skiplist[0] = "^Documentation for" @@ -43,23 +43,23 @@ BEGIN { # Remove punctuation from the given value function trimpunct(value) { - while (value ~ /^["&]/) { - value = substr(value, 2) - } - while (value ~ /[]["&,}]$/) { - value = substr(value, 1, length(value) - 1) - } - return value + while (value ~ /^["&]/) { + value = substr(value, 2) + } + while (value ~ /[]["&,}]$/) { + value = substr(value, 1, length(value) - 1) + } + return value } # Print the information for the given entry function printentry(entry) { - seen[entry]++ - printf "* %s from %s", entry, file[entry] - if (documented[entry]) { - printf " (documented)" - } - print "" + seen[entry]++ + printf "* %s from %s", entry, file[entry] + if (documented[entry]) { + printf " (documented)" + } + print "" } @@ -71,105 +71,104 @@ FNR == NR && /^=+$/ { } } - # The previous line is a section title, parse it - $0 = prevline - if (debug) print "Parsing " $0 - inbrackets = 0 - for (i = 1; i <= NF; i++) { - if (length($i) == 0) { - continue - } - if (!inbrackets && substr($i, 1, 1) == "(") { - inbrackets = 1 - } - if (!inbrackets) { - token = trimpunct($i) - if (length(token) > 0 && token != "and") { - if (debug) print trimpunct($i) - documented[trimpunct($i)]++ - } - } - if (inbrackets && substr($i, length($i), 1) == ")") { - inbrackets = 0 + # The previous line is a section title, parse it + $0 = prevline + if (debug) print "Parsing " $0 + inbrackets = 0 + for (i = 1; i <= NF; i++) { + if (length($i) == 0) { + continue + } + if (!inbrackets && substr($i, 1, 1) == "(") { + inbrackets = 1 + } + if (!inbrackets) { + token = trimpunct($i) + if (length(token) > 0 && token != "and") { + if (debug) print trimpunct($i) + documented[trimpunct($i)]++ + } + } + if (inbrackets && substr($i, length($i), 1) == ")") { + inbrackets = 0 + } } - } } FNR == NR { - prevline = $0 - next + prevline = $0 + next } # Stage 2: process each file and find all sysctl tables BEGINFILE { - delete entries - curtable = "" - curentry = "" - delete vars - if (debug) print "Processing file " FILENAME + delete entries + curtable = "" + curentry = "" + delete vars + if (debug) print "Processing file " FILENAME } /^static( const)? struct ctl_table/ { - match($0, /static( const)? struct ctl_table ([^][]+)/, tables) - curtable = tables[2] - if (debug) print "Processing table " curtable + match($0, /static( const)? struct ctl_table ([^][]+)/, tables) + curtable = tables[2] + if (debug) print "Processing table " curtable } /^};$/ { - curtable = "" - curentry = "" - delete vars + curtable = "" + curentry = "" + delete vars } curtable && /\.procname[\t ]*=[\t ]*".+"/ { - match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names) - curentry = names[1] - if (debug) print "Adding entry " curentry " to table " curtable - entries[curtable][curentry]++ - file[curentry] = FILENAME + match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names) + curentry = names[1] + if (debug) print "Adding entry " curentry " to table " curtable + entries[curtable][curentry]++ + file[curentry] = FILENAME } curtable && /UCOUNT_ENTRY.*/ { - match($0, /UCOUNT_ENTRY\("([^"]+)"\)/, names) - curentry = names[1] - if (debug) print "Adding entry " curentry " to table " curtable - entries[curtable][curentry]++ - file[curentry] = FILENAME + match($0, /UCOUNT_ENTRY\("([^"]+)"\)/, names) + curentry = names[1] + if (debug) print "Adding entry " curentry " to table " curtable + entries[curtable][curentry]++ + file[curentry] = FILENAME } /register_sysctl.*/ { - match($0, /register_sysctl(|_init|_sz)\("([^"]+)" *, *([^,)]+)/, tables) - if (debug) print "Registering table " tables[3] " at " tables[2] - if (tables[2] == table) { - for (entry in entries[tables[3]]) { - printentry(entry) - } - } + match($0, /register_sysctl(|_init|_sz)\("([^"]+)" *, *([^,)]+)/, tables) + if (debug) print "Registering table " tables[3] " at " tables[2] + if (tables[2] == table) { + for (entry in entries[tables[3]]) { + printentry(entry) + } + } } /kmemdup.*/ { - match($0, /([^ \t]+) *= *kmemdup\(([^,]+) *,/, names) - if (debug) print "Found variable " names[1] " for table " names[2] - if (names[2] in entries) { - vars[names[1]] = names[2] - } + match($0, /([^ \t]+) *= *kmemdup\(([^,]+) *,/, names) + if (debug) print "Found variable " names[1] " for table " names[2] + if (names[2] in entries) { + vars[names[1]] = names[2] + } } /__register_sysctl_table.*/ { - match($0, /__register_sysctl_table\([^,]+, *"([^"]+)" *, *([^,]+)/, tables) - if (debug) print "Registering variable table " tables[2] " at " tables[1] - if (tables[1] == table && tables[2] in vars) { - for (entry in entries[vars[tables[2]]]) { - printentry(entry) - } - } + match($0, /__register_sysctl_table\([^,]+, *"([^"]+)" *, *([^,]+)/, tables) + if (debug) print "Registering variable table " tables[2] " at " tables[1] + if (tables[1] == table && tables[2] in vars) { + for (entry in entries[vars[tables[2]]]) { + printentry(entry) + } + } } END { - for (entry in documented) { - if (!seen[entry]) { - print "No implementation for " entry + for (entry in documented) { + if (!seen[entry]) + print "No implementation for " entry } - } } -- cgit v1.2.3 From 35293ebbb65e0295d3b9357f786004ae1026d00f Mon Sep 17 00:00:00 2001 From: Zhiyu Zhang Date: Mon, 14 Jul 2025 00:34:18 +0800 Subject: scripts: add origin commit identification based on specific patterns This patch adds the functionability to smartly identify origin commit of the translation by matching the following patterns in commit log: 1) update to commit HASH 2) Update the translation through commit HASH If no such pattern is found, script will obey the original workflow. Signed-off-by: Zhiyu Zhang Reviewed-by: Dongliang Mu Signed-off-by: Jonathan Corbet Link: https://lore.kernel.org/r/20250713163418.1459-1-zhiyuzhang999@gmail.com --- scripts/checktransupdate.py | 38 +++++++++++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/checktransupdate.py b/scripts/checktransupdate.py index 578c3fecfdfd..e39529e46c3d 100755 --- a/scripts/checktransupdate.py +++ b/scripts/checktransupdate.py @@ -24,6 +24,7 @@ commit 42fb9cfd5b18 ("Documentation: dev-tools: Add link to RV docs") """ import os +import re import time import logging from argparse import ArgumentParser, ArgumentTypeError, BooleanOptionalAction @@ -69,6 +70,38 @@ def get_origin_from_trans(origin_path, t_from_head): return o_from_t +def get_origin_from_trans_smartly(origin_path, t_from_head): + """Get the latest origin commit from the formatted translation commit: + (1) update to commit HASH (TITLE) + (2) Update the translation through commit HASH (TITLE) + """ + # catch flag for 12-bit commit hash + HASH = r'([0-9a-f]{12})' + # pattern 1: contains "update to commit HASH" + pat_update_to = re.compile(rf'update to commit {HASH}') + # pattern 2: contains "Update the translation through commit HASH" + pat_update_translation = re.compile(rf'Update the translation through commit {HASH}') + + origin_commit_hash = None + for line in t_from_head["message"]: + # check if the line matches the first pattern + match = pat_update_to.search(line) + if match: + origin_commit_hash = match.group(1) + break + # check if the line matches the second pattern + match = pat_update_translation.search(line) + if match: + origin_commit_hash = match.group(1) + break + if origin_commit_hash is None: + return None + o_from_t = get_latest_commit_from(origin_path, origin_commit_hash) + if o_from_t is not None: + logging.debug("tracked origin commit id: %s", o_from_t["hash"]) + return o_from_t + + def get_commits_count_between(opath, commit1, commit2): """Get the commits count between two commits for the specified file""" command = f"git log --pretty=format:%H {commit1}...{commit2} -- {opath}" @@ -108,7 +141,10 @@ def check_per_file(file_path): logging.error("Cannot find the latest commit for %s", file_path) return - o_from_t = get_origin_from_trans(opath, t_from_head) + o_from_t = get_origin_from_trans_smartly(opath, t_from_head) + # notice, o_from_t from get_*_smartly() is always more accurate than from get_*() + if o_from_t is None: + o_from_t = get_origin_from_trans(opath, t_from_head) if o_from_t is None: logging.error("Error: Cannot find the latest origin commit for %s", file_path) -- cgit v1.2.3 From f468992936894c9ce3b1659cf38c230d33b77a16 Mon Sep 17 00:00:00 2001 From: Shankari Anand Date: Thu, 26 Jun 2025 00:36:54 +0530 Subject: kconfig: nconf: Ensure null termination where strncpy is used strncpy() does not guarantee null-termination if the source string is longer than the destination buffer. Ensure the buffer is explicitly null-terminated to prevent potential string overflows or undefined behavior. Signed-off-by: Shankari Anand Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap Tested-by: Nicolas Schier Acked-by: Nicolas Schier --- scripts/kconfig/nconf.c | 2 ++ scripts/kconfig/nconf.gui.c | 1 + 2 files changed, 3 insertions(+) (limited to 'scripts') diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c index c0b2dabf6c89..ae1fe5f60327 100644 --- a/scripts/kconfig/nconf.c +++ b/scripts/kconfig/nconf.c @@ -593,6 +593,8 @@ static void item_add_str(const char *fmt, ...) tmp_str, sizeof(k_menu_items[index].str)); + k_menu_items[index].str[sizeof(k_menu_items[index].str) - 1] = '\0'; + free_item(curses_menu_items[index]); curses_menu_items[index] = new_item( k_menu_items[index].str, diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c index 4bfdf8ac2a9a..7206437e784a 100644 --- a/scripts/kconfig/nconf.gui.c +++ b/scripts/kconfig/nconf.gui.c @@ -359,6 +359,7 @@ int dialog_inputbox(WINDOW *main_window, x = (columns-win_cols)/2; strncpy(result, init, *result_len); + result[*result_len - 1] = '\0'; /* create the windows */ win = newwin(win_lines, win_cols, y, x); -- cgit v1.2.3 From 0c82f50a06aa13e6fc29e17081094489d57745fd Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:43:27 +0900 Subject: kconfig: gconf: fix behavior of a menu under a symbol in split view A menu can be created under a symbol. [Example] menu "outer menu" config A bool "A" menu "inner menu" depends on A config B bool "B" endmenu endmenu After being re-parented by menu_finalize(), the menu tree is structured like follows: menu "outer menu" \-- A \-- menu "inner menu" \-- B In split view, the symbol A is shown in the right pane, so all of its descendants must also be shown there. This has never worked correctly. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 28c4b5b37448..7397a51641a7 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -803,7 +803,7 @@ static gboolean on_treeview2_button_press_event(GtkWidget *widget, enum prop_type ptype; ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN; - if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) { + if (ptype == P_MENU && view_mode == SINGLE_VIEW && col == COL_OPTION) { // goes down into menu browsed = menu; display_tree_part(); @@ -953,8 +953,7 @@ static void _display_tree(GtkTreeStore *tree, struct menu *menu, gtk_tree_store_append(tree, &iter, parent); set_node(tree, &iter, child); - if ((view_mode != FULL_VIEW) && (ptype == P_MENU) - && (tree == tree2)) + if ((view_mode == SINGLE_VIEW) && (ptype == P_MENU)) continue; /* if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT)) -- cgit v1.2.3 From 06ba76dc825703fa61cee72c2ae66508ef5f10ec Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:43:28 +0900 Subject: kconfig: gconf: use configure-event handler to adjust pane separator The size-request event handler is currently used to adjust the position of the horizontal separator in the right pane. However, the size-request signal is not available in GTK 3. Use the configure-event signal instead. Signed-off-by: Masahiro Yamada Tested-by: Randy Dunlap --- scripts/kconfig/gconf.c | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 7397a51641a7..37eec7a6bf54 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -606,23 +606,12 @@ static void on_window1_destroy(GtkObject *object, gpointer user_data) gtk_main_quit(); } -static void on_window1_size_request(GtkWidget *widget, - GtkRequisition *requisition, - gpointer user_data) +static gboolean on_window1_configure(GtkWidget *self, + GdkEventConfigure *event, + gpointer user_data) { - static gint old_h; - gint w, h; - - if (widget->window == NULL) - gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h); - else - gdk_window_get_size(widget->window, &w, &h); - - if (h == old_h) - return; - old_h = h; - - gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3); + gtk_paned_set_position(GTK_PANED(vpaned), 2 * event->height / 3); + return FALSE; } static gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event, @@ -1023,8 +1012,8 @@ static void init_main_window(const gchar *glade_file) main_wnd = glade_xml_get_widget(xml, "window1"); g_signal_connect(main_wnd, "destroy", G_CALLBACK(on_window1_destroy), NULL); - g_signal_connect(main_wnd, "size_request", - G_CALLBACK(on_window1_size_request), NULL); + g_signal_connect(main_wnd, "configure-event", + G_CALLBACK(on_window1_configure), NULL); g_signal_connect(main_wnd, "delete_event", G_CALLBACK(on_window1_delete_event), NULL); -- cgit v1.2.3 From 894ad403439e54d3cdee77a538190dd08ae54789 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:43:29 +0900 Subject: kconfig: gconf: rename display_tree_part() This function recreates the tree store to update the menu content. Rename it to recreate_tree() to better reflect its purpose. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 37eec7a6bf54..05ee10f5f45b 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -57,7 +57,7 @@ enum { }; static void display_tree(GtkTreeStore *store, struct menu *menu); -static void display_tree_part(void); +static void recreate_tree(void); static void conf_changed(bool dirty) { @@ -327,7 +327,7 @@ static void set_view_mode(enum view_mode mode) browsed = menu_get_parent_menu(selected) ?: &rootmenu; else browsed = &rootmenu; - display_tree_part(); + recreate_tree(); text_insert_msg("", ""); select_menu(GTK_TREE_VIEW(tree2_w), selected); gtk_widget_set_sensitive(single_btn, FALSE); @@ -558,7 +558,7 @@ static void on_back_clicked(GtkButton *button, gpointer user_data) ptype = browsed->prompt ? browsed->prompt->type : P_UNKNOWN; if (ptype != P_MENU) browsed = browsed->parent; - display_tree_part(); + recreate_tree(); if (browsed == &rootmenu) gtk_widget_set_sensitive(back_btn, FALSE); @@ -795,7 +795,7 @@ static gboolean on_treeview2_button_press_event(GtkWidget *widget, if (ptype == P_MENU && view_mode == SINGLE_VIEW && col == COL_OPTION) { // goes down into menu browsed = menu; - display_tree_part(); + recreate_tree(); gtk_widget_set_sensitive(back_btn, TRUE); } else if (col == COL_OPTION) { toggle_sym_value(menu); @@ -900,7 +900,7 @@ static gboolean on_treeview1_button_press_event(GtkWidget *widget, if (menu->type == M_MENU) { browsed = menu; - display_tree_part(); + recreate_tree(); } gtk_tree_view_set_cursor(view, path, NULL, FALSE); @@ -961,8 +961,8 @@ static void display_tree(GtkTreeStore *store, struct menu *menu) _display_tree(store, menu, NULL); } -/* Display a part of the tree starting at current node (single/split view) */ -static void display_tree_part(void) +/* Recreate the tree store starting at 'browsed' node */ +static void recreate_tree(void) { gtk_tree_store_clear(tree2); display_tree(tree2, browsed); -- cgit v1.2.3 From e06030c1ae3299f71ae38ccbdd4ae0a2d0aa9189 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:43:30 +0900 Subject: kconfig: gconf: rename gconf.glade to gconf.ui The next commit will convert this file to GtkBuilder format. Rename it in advance to reflect the intended format. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/gconf.c | 6 +- scripts/kconfig/gconf.glade | 536 -------------------------------------------- scripts/kconfig/gconf.ui | 536 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 539 insertions(+), 539 deletions(-) delete mode 100644 scripts/kconfig/gconf.glade create mode 100644 scripts/kconfig/gconf.ui (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 05ee10f5f45b..8006cc547180 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -1327,11 +1327,11 @@ int main(int ac, char *av[]) /* Determine GUI path */ env = getenv(SRCTREE); if (env) - glade_file = g_strconcat(env, "/scripts/kconfig/gconf.glade", NULL); + glade_file = g_strconcat(env, "/scripts/kconfig/gconf.ui", NULL); else if (av[0][0] == '/') - glade_file = g_strconcat(av[0], ".glade", NULL); + glade_file = g_strconcat(av[0], ".ui", NULL); else - glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL); + glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".ui", NULL); /* Conf stuffs */ if (ac > 1 && av[1][0] == '-') { diff --git a/scripts/kconfig/gconf.glade b/scripts/kconfig/gconf.glade deleted file mode 100644 index cd714e64cff1..000000000000 --- a/scripts/kconfig/gconf.glade +++ /dev/null @@ -1,536 +0,0 @@ - - - - - - True - Gtk Kernel Configurator - GTK_WINDOW_TOPLEVEL - GTK_WIN_POS_NONE - False - 640 - 480 - True - False - True - False - False - GDK_WINDOW_TYPE_HINT_NORMAL - GDK_GRAVITY_NORTH_WEST - - - - True - False - 0 - - - - True - - - - True - _File - True - - - - - - - True - Load a config file - _Load - True - - - - - - - True - Save the config in .config - _Save - True - - - - - - - True - Save the config in a file - Save _as - True - - - - - - True - - - - - - True - _Quit - True - - - - - - - - - - - True - _Options - True - - - - - - - True - Show name - Show _name - True - False - - - - - - True - Show range (Y/M/N) - Show _range - True - False - - - - - - True - Show value of the option - Show _data - True - False - - - - - - True - - - - - - True - Show normal options - Show normal options - True - True - - - - - - True - Show all options - Show all _options - True - False - set_option_mode1 - - - - - - True - Show all options with prompts - Show all prompt options - True - False - set_option_mode1 - - - - - - - - - - - True - _Help - True - - - - - - - True - _Introduction - True - - - - - - - True - _About - True - - - - - - - True - _License - True - - - - - - - - - 0 - False - False - - - - - - True - GTK_ORIENTATION_HORIZONTAL - GTK_TOOLBAR_BOTH - True - - - - True - Goes up one level (single view) - Back - True - gtk-undo - True - True - False - - - False - True - - - - - - True - True - True - False - - - - True - - - - - False - False - - - - - - True - Load a config file - Load - True - gtk-open - True - True - False - - - False - True - - - - - - True - Save a config file - Save - True - gtk-save - True - True - False - - - False - True - - - - - - True - True - True - False - - - - True - - - - - False - False - - - - - - True - Single view - Single - True - gtk-missing-image - True - True - False - - - False - True - - - - - - True - Split view - Split - True - gtk-missing-image - True - True - False - - - False - True - - - - - - True - Full view - Full - True - gtk-missing-image - True - True - False - - - False - True - - - - - - True - True - True - False - - - - True - - - - - False - False - - - - - - True - Collapse the whole tree in the right frame - Collapse - True - gtk-remove - True - True - False - - - False - True - - - - - - True - Expand the whole tree in the right frame - Expand - True - gtk-add - True - True - False - - - False - True - - - - - 0 - False - False - - - - - - 1 - True - True - 0 - - - - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - True - False - False - False - - - - - True - False - - - - - - True - True - 0 - - - - True - GTK_POLICY_AUTOMATIC - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - True - True - False - False - False - - - - - True - False - - - - - - True - GTK_POLICY_NEVER - GTK_POLICY_AUTOMATIC - GTK_SHADOW_IN - GTK_CORNER_TOP_LEFT - - - - True - True - False - False - True - GTK_JUSTIFY_LEFT - GTK_WRAP_WORD - True - 0 - 0 - 0 - 0 - 0 - 0 - - - - - True - True - - - - - True - True - - - - - 0 - True - True - - - - - - - diff --git a/scripts/kconfig/gconf.ui b/scripts/kconfig/gconf.ui new file mode 100644 index 000000000000..cd714e64cff1 --- /dev/null +++ b/scripts/kconfig/gconf.ui @@ -0,0 +1,536 @@ + + + + + + True + Gtk Kernel Configurator + GTK_WINDOW_TOPLEVEL + GTK_WIN_POS_NONE + False + 640 + 480 + True + False + True + False + False + GDK_WINDOW_TYPE_HINT_NORMAL + GDK_GRAVITY_NORTH_WEST + + + + True + False + 0 + + + + True + + + + True + _File + True + + + + + + + True + Load a config file + _Load + True + + + + + + + True + Save the config in .config + _Save + True + + + + + + + True + Save the config in a file + Save _as + True + + + + + + True + + + + + + True + _Quit + True + + + + + + + + + + + True + _Options + True + + + + + + + True + Show name + Show _name + True + False + + + + + + True + Show range (Y/M/N) + Show _range + True + False + + + + + + True + Show value of the option + Show _data + True + False + + + + + + True + + + + + + True + Show normal options + Show normal options + True + True + + + + + + True + Show all options + Show all _options + True + False + set_option_mode1 + + + + + + True + Show all options with prompts + Show all prompt options + True + False + set_option_mode1 + + + + + + + + + + + True + _Help + True + + + + + + + True + _Introduction + True + + + + + + + True + _About + True + + + + + + + True + _License + True + + + + + + + + + 0 + False + False + + + + + + True + GTK_ORIENTATION_HORIZONTAL + GTK_TOOLBAR_BOTH + True + + + + True + Goes up one level (single view) + Back + True + gtk-undo + True + True + False + + + False + True + + + + + + True + True + True + False + + + + True + + + + + False + False + + + + + + True + Load a config file + Load + True + gtk-open + True + True + False + + + False + True + + + + + + True + Save a config file + Save + True + gtk-save + True + True + False + + + False + True + + + + + + True + True + True + False + + + + True + + + + + False + False + + + + + + True + Single view + Single + True + gtk-missing-image + True + True + False + + + False + True + + + + + + True + Split view + Split + True + gtk-missing-image + True + True + False + + + False + True + + + + + + True + Full view + Full + True + gtk-missing-image + True + True + False + + + False + True + + + + + + True + True + True + False + + + + True + + + + + False + False + + + + + + True + Collapse the whole tree in the right frame + Collapse + True + gtk-remove + True + True + False + + + False + True + + + + + + True + Expand the whole tree in the right frame + Expand + True + gtk-add + True + True + False + + + False + True + + + + + 0 + False + False + + + + + + 1 + True + True + 0 + + + + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + True + False + False + False + + + + + True + False + + + + + + True + True + 0 + + + + True + GTK_POLICY_AUTOMATIC + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + True + True + False + False + False + + + + + True + False + + + + + + True + GTK_POLICY_NEVER + GTK_POLICY_AUTOMATIC + GTK_SHADOW_IN + GTK_CORNER_TOP_LEFT + + + + True + True + False + False + True + GTK_JUSTIFY_LEFT + GTK_WRAP_WORD + True + 0 + 0 + 0 + 0 + 0 + 0 + + + + + True + True + + + + + True + True + + + + + 0 + True + True + + + + + + + -- cgit v1.2.3 From 9755d167bf51fad7091bd990f8d57006d6a60669 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:43:31 +0900 Subject: kconfig: gconf: migrate to GTK 3 This commit switches from GTK 2.x to GTK 3, applying the following necessary changes: - Do not include individual headers - GtkObject is gone - Convert Glade to GtkBuilder Link: https://docs.gtk.org/gtk3/migrating-2to3.html Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/gconf-cfg.sh | 11 +-- scripts/kconfig/gconf.c | 70 ++++++++------- scripts/kconfig/gconf.ui | 200 +++++++++++++++++++++---------------------- 3 files changed, 135 insertions(+), 146 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf-cfg.sh b/scripts/kconfig/gconf-cfg.sh index fc954c0538fa..856c692f480c 100755 --- a/scripts/kconfig/gconf-cfg.sh +++ b/scripts/kconfig/gconf-cfg.sh @@ -6,7 +6,7 @@ set -eu cflags=$1 libs=$2 -PKG="gtk+-2.0 gmodule-2.0 libglade-2.0" +PKG=gtk+-3.0 if [ -z "$(command -v ${HOSTPKG_CONFIG})" ]; then echo >&2 "*" @@ -18,18 +18,11 @@ fi if ! ${HOSTPKG_CONFIG} --exists $PKG; then echo >&2 "*" echo >&2 "* Unable to find the GTK+ installation. Please make sure that" - echo >&2 "* the GTK+ 2.0 development package is correctly installed." + echo >&2 "* the GTK 3 development package is correctly installed." echo >&2 "* You need $PKG" echo >&2 "*" exit 1 fi -if ! ${HOSTPKG_CONFIG} --atleast-version=2.0.0 gtk+-2.0; then - echo >&2 "*" - echo >&2 "* GTK+ is present but version >= 2.0.0 is required." - echo >&2 "*" - exit 1 -fi - ${HOSTPKG_CONFIG} --cflags ${PKG} > ${cflags} ${HOSTPKG_CONFIG} --libs ${PKG} > ${libs} diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 8006cc547180..313250d4fc53 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -7,10 +7,7 @@ #include "lkc.h" #include "images.h" -#include #include -#include -#include #include #include @@ -601,7 +598,7 @@ static void on_expand_clicked(GtkButton *button, gpointer user_data) /* Main Windows Callbacks */ -static void on_window1_destroy(GtkObject *object, gpointer user_data) +static void on_window1_destroy(GtkWidget *widget, gpointer user_data) { gtk_main_quit(); } @@ -1001,15 +998,15 @@ static void replace_button_icon(GtkWidget *widget, const char * const xpm[]) static void init_main_window(const gchar *glade_file) { - GladeXML *xml; + GtkBuilder *builder; GtkWidget *widget; GtkTextBuffer *txtbuf; - xml = glade_xml_new(glade_file, "window1", NULL); - if (!xml) + builder = gtk_builder_new_from_file(glade_file); + if (!builder) g_error("GUI loading failed !\n"); - main_wnd = glade_xml_get_widget(xml, "window1"); + main_wnd = GTK_WIDGET(gtk_builder_get_object(builder, "window1")); g_signal_connect(main_wnd, "destroy", G_CALLBACK(on_window1_destroy), NULL); g_signal_connect(main_wnd, "configure-event", @@ -1017,9 +1014,9 @@ static void init_main_window(const gchar *glade_file) g_signal_connect(main_wnd, "delete_event", G_CALLBACK(on_window1_delete_event), NULL); - hpaned = glade_xml_get_widget(xml, "hpaned1"); - vpaned = glade_xml_get_widget(xml, "vpaned1"); - tree1_w = glade_xml_get_widget(xml, "treeview1"); + hpaned = GTK_WIDGET(gtk_builder_get_object(builder, "hpaned1")); + vpaned = GTK_WIDGET(gtk_builder_get_object(builder, "vpaned1")); + tree1_w = GTK_WIDGET(gtk_builder_get_object(builder, "treeview1")); g_signal_connect(tree1_w, "cursor_changed", G_CALLBACK(on_treeview2_cursor_changed), NULL); g_signal_connect(tree1_w, "button_press_event", @@ -1027,7 +1024,7 @@ static void init_main_window(const gchar *glade_file) g_signal_connect(tree1_w, "key_press_event", G_CALLBACK(on_treeview2_key_press_event), NULL); - tree2_w = glade_xml_get_widget(xml, "treeview2"); + tree2_w = GTK_WIDGET(gtk_builder_get_object(builder, "treeview2")); g_signal_connect(tree2_w, "cursor_changed", G_CALLBACK(on_treeview2_cursor_changed), NULL); g_signal_connect(tree2_w, "button_press_event", @@ -1035,101 +1032,101 @@ static void init_main_window(const gchar *glade_file) g_signal_connect(tree2_w, "key_press_event", G_CALLBACK(on_treeview2_key_press_event), NULL); - text_w = glade_xml_get_widget(xml, "textview3"); + text_w = GTK_WIDGET(gtk_builder_get_object(builder, "textview3")); /* menubar */ - widget = glade_xml_get_widget(xml, "load1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "load1")); g_signal_connect(widget, "activate", G_CALLBACK(on_load1_activate), NULL); - save_menu_item = glade_xml_get_widget(xml, "save1"); + save_menu_item = GTK_WIDGET(gtk_builder_get_object(builder, "save1")); g_signal_connect(save_menu_item, "activate", G_CALLBACK(on_save_activate), NULL); - widget = glade_xml_get_widget(xml, "save_as1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "save_as1")); g_signal_connect(widget, "activate", G_CALLBACK(on_save_as1_activate), NULL); - widget = glade_xml_get_widget(xml, "quit1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "quit1")); g_signal_connect(widget, "activate", G_CALLBACK(on_quit1_activate), NULL); - widget = glade_xml_get_widget(xml, "show_name1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "show_name1")); g_signal_connect(widget, "activate", G_CALLBACK(on_show_name1_activate), NULL); gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, show_name); - widget = glade_xml_get_widget(xml, "show_range1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "show_range1")); g_signal_connect(widget, "activate", G_CALLBACK(on_show_range1_activate), NULL); gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, show_range); - widget = glade_xml_get_widget(xml, "show_data1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "show_data1")); g_signal_connect(widget, "activate", G_CALLBACK(on_show_data1_activate), NULL); gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget, show_value); - widget = glade_xml_get_widget(xml, "set_option_mode1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "set_option_mode1")); g_signal_connect(widget, "activate", G_CALLBACK(on_set_option_mode1_activate), NULL); - widget = glade_xml_get_widget(xml, "set_option_mode2"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "set_option_mode2")); g_signal_connect(widget, "activate", G_CALLBACK(on_set_option_mode2_activate), NULL); - widget = glade_xml_get_widget(xml, "set_option_mode3"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "set_option_mode3")); g_signal_connect(widget, "activate", G_CALLBACK(on_set_option_mode3_activate), NULL); - widget = glade_xml_get_widget(xml, "introduction1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "introduction1")); g_signal_connect(widget, "activate", G_CALLBACK(on_introduction1_activate), NULL); - widget = glade_xml_get_widget(xml, "about1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "about1")); g_signal_connect(widget, "activate", G_CALLBACK(on_about1_activate), NULL); - widget = glade_xml_get_widget(xml, "license1"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "license1")); g_signal_connect(widget, "activate", G_CALLBACK(on_license1_activate), NULL); /* toolbar */ - back_btn = glade_xml_get_widget(xml, "button1"); + back_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button1")); g_signal_connect(back_btn, "clicked", G_CALLBACK(on_back_clicked), NULL); gtk_widget_set_sensitive(back_btn, FALSE); - widget = glade_xml_get_widget(xml, "button2"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "button2")); g_signal_connect(widget, "clicked", G_CALLBACK(on_load_clicked), NULL); - save_btn = glade_xml_get_widget(xml, "button3"); + save_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button3")); g_signal_connect(save_btn, "clicked", G_CALLBACK(on_save_clicked), NULL); - single_btn = glade_xml_get_widget(xml, "button4"); + single_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button4")); g_signal_connect(single_btn, "clicked", G_CALLBACK(on_single_clicked), NULL); replace_button_icon(single_btn, xpm_single_view); - split_btn = glade_xml_get_widget(xml, "button5"); + split_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button5")); g_signal_connect(split_btn, "clicked", G_CALLBACK(on_split_clicked), NULL); replace_button_icon(split_btn, xpm_split_view); - full_btn = glade_xml_get_widget(xml, "button6"); + full_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button6")); g_signal_connect(full_btn, "clicked", G_CALLBACK(on_full_clicked), NULL); replace_button_icon(full_btn, xpm_tree_view); - widget = glade_xml_get_widget(xml, "button7"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "button7")); g_signal_connect(widget, "clicked", G_CALLBACK(on_collapse_clicked), NULL); - widget = glade_xml_get_widget(xml, "button8"); + widget = GTK_WIDGET(gtk_builder_get_object(builder, "button8")); g_signal_connect(widget, "clicked", G_CALLBACK(on_expand_clicked), NULL); @@ -1144,7 +1141,9 @@ static void init_main_window(const gchar *glade_file) gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text); - gtk_widget_show(main_wnd); + gtk_widget_show_all(main_wnd); + + g_object_unref(builder); conf_set_changed_callback(conf_changed); } @@ -1322,7 +1321,6 @@ int main(int ac, char *av[]) /* GTK stuffs */ gtk_init(&ac, &av); - glade_init(); /* Determine GUI path */ env = getenv(SRCTREE); diff --git a/scripts/kconfig/gconf.ui b/scripts/kconfig/gconf.ui index cd714e64cff1..de20a9143aa8 100644 --- a/scripts/kconfig/gconf.ui +++ b/scripts/kconfig/gconf.ui @@ -1,8 +1,8 @@ - + - + True Gtk Kernel Configurator GTK_WINDOW_TOPLEVEL @@ -19,193 +19,193 @@ GDK_GRAVITY_NORTH_WEST - + True False 0 - + True - + True _File True - - + + - + True Load a config file _Load True - + - + True Save the config in .config _Save True - + - + True Save the config in a file Save _as True - + - + True - + - + True _Quit True - + - + - + - + True _Options True - - + + - + True Show name Show _name True False - + - + True Show range (Y/M/N) Show _range True False - + - + True Show value of the option Show _data True False - + - + True - + - + True Show normal options Show normal options True True - + - + True Show all options Show all _options True False set_option_mode1 - + - + True Show all options with prompts Show all prompt options True False set_option_mode1 - + - + - + - + True _Help True - - + + - + True _Introduction True - + - + True _About True - + - + True _License True - + - + - + - + 0 False @@ -214,14 +214,14 @@ - + True GTK_ORIENTATION_HORIZONTAL GTK_TOOLBAR_BOTH True - + True Goes up one level (single view) Back @@ -230,7 +230,7 @@ True True False - + False True @@ -238,18 +238,18 @@ - + True True True False - + True - + - + False False @@ -257,7 +257,7 @@ - + True Load a config file Load @@ -266,7 +266,7 @@ True True False - + False True @@ -274,7 +274,7 @@ - + True Save a config file Save @@ -283,7 +283,7 @@ True True False - + False True @@ -291,18 +291,18 @@ - + True True True False - + True - + - + False False @@ -310,7 +310,7 @@ - + True Single view Single @@ -319,7 +319,7 @@ True True False - + False True @@ -327,7 +327,7 @@ - + True Split view Split @@ -336,7 +336,7 @@ True True False - + False True @@ -344,7 +344,7 @@ - + True Full view Full @@ -353,7 +353,7 @@ True True False - + False True @@ -361,18 +361,18 @@ - + True True True False - + True - + - + False False @@ -380,7 +380,7 @@ - + True Collapse the whole tree in the right frame Collapse @@ -389,7 +389,7 @@ True True False - + False True @@ -397,7 +397,7 @@ - + True Expand the whole tree in the right frame Expand @@ -406,13 +406,13 @@ True True False - + False True - + 0 False @@ -421,14 +421,13 @@ - + 1 True True - 0 - + True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC @@ -436,16 +435,16 @@ GTK_CORNER_TOP_LEFT - + True True True False False False - + - + True False @@ -453,13 +452,12 @@ - + True True - 0 - + True GTK_POLICY_AUTOMATIC GTK_POLICY_AUTOMATIC @@ -467,7 +465,7 @@ GTK_CORNER_TOP_LEFT - + True True True @@ -475,9 +473,9 @@ False False False - + - + True False @@ -485,7 +483,7 @@ - + True GTK_POLICY_NEVER GTK_POLICY_AUTOMATIC @@ -493,7 +491,7 @@ GTK_CORNER_TOP_LEFT - + True True False @@ -508,29 +506,29 @@ 0 0 0 - + - + True True - + True True - + 0 True True - + - + - + -- cgit v1.2.3 From df889fdbb8d4243504eba94e1c3a809a4996a219 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:43:32 +0900 Subject: kconfig: gconf: replace GtkVbox with GtkBox GtkVBox is deprecated with GTK 3.2. [1] Use GtkBox instead. [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/3.2.0/gtk/gtkvbox.c#L47 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.ui | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.ui b/scripts/kconfig/gconf.ui index de20a9143aa8..806ea3d1bac4 100644 --- a/scripts/kconfig/gconf.ui +++ b/scripts/kconfig/gconf.ui @@ -19,7 +19,8 @@ GDK_GRAVITY_NORTH_WEST - + + vertical True False 0 -- cgit v1.2.3 From d6f0b652d9b54af5a9cf3e926ecfba81c28e1fc4 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:43:33 +0900 Subject: kconfig: gconf: replace GdkColor with GdkRGBA MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit GdkColor is deprecated with GTK 3.14. [1] Use GdkRGBA instead. This fixes warnings such as: scripts/kconfig/gconf.c: In function ‘set_node’: scripts/kconfig/gconf.c:138:9: warning: ‘gdk_color_parse’ is deprecated: Use 'gdk_rgba_parse' instead [-Wdeprecated-declarations] 138 | gdk_color_parse(menu_is_visible(menu) ? "Black" : "DarkGray", &color); | ^~~~~~~~~~~~~~~ [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/3.14.0/gdk/deprecated/gdkcolor.h#L52 Signed-off-by: Masahiro Yamada Tested-by: Randy Dunlap --- scripts/kconfig/gconf.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 313250d4fc53..f4c2b07e0207 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -176,7 +176,7 @@ static void set_node(GtkTreeStore *tree, GtkTreeIter *node, struct menu *menu) const gchar *_mod = ""; const gchar *_yes = ""; const gchar *value = ""; - GdkColor color; + GdkRGBA color; gboolean editable = FALSE; gboolean btnvis = FALSE; @@ -186,7 +186,7 @@ static void set_node(GtkTreeStore *tree, GtkTreeIter *node, struct menu *menu) menu->type == M_COMMENT ? "***" : "", sym && !sym_has_value(sym) ? "(NEW)" : ""); - gdk_color_parse(menu_is_visible(menu) ? "Black" : "DarkGray", &color); + gdk_rgba_parse(&color, menu_is_visible(menu) ? "Black" : "DarkGray"); if (!sym) goto set; @@ -1174,7 +1174,7 @@ static void init_left_tree(void) G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_POINTER, GDK_TYPE_COLOR, + G_TYPE_POINTER, GDK_TYPE_RGBA, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, @@ -1205,7 +1205,7 @@ static void init_left_tree(void) gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), renderer, "text", COL_OPTION, - "foreground-gdk", + "foreground-rgba", COL_COLOR, NULL); sel = gtk_tree_view_get_selection(view); @@ -1225,7 +1225,7 @@ static void init_right_tree(void) G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, - G_TYPE_POINTER, GDK_TYPE_COLOR, + G_TYPE_POINTER, GDK_TYPE_RGBA, G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, @@ -1263,32 +1263,32 @@ static void init_right_tree(void) gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column), renderer, "text", COL_OPTION, - "foreground-gdk", + "foreground-rgba", COL_COLOR, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(view, -1, "Name", renderer, "text", COL_NAME, - "foreground-gdk", + "foreground-rgba", COL_COLOR, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(view, -1, "N", renderer, "text", COL_NO, - "foreground-gdk", + "foreground-rgba", COL_COLOR, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(view, -1, "M", renderer, "text", COL_MOD, - "foreground-gdk", + "foreground-rgba", COL_COLOR, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(view, -1, "Y", renderer, "text", COL_YES, - "foreground-gdk", + "foreground-rgba", COL_COLOR, NULL); renderer = gtk_cell_renderer_text_new(); gtk_tree_view_insert_column_with_attributes(view, -1, @@ -1296,7 +1296,7 @@ static void init_right_tree(void) "text", COL_VALUE, "editable", COL_EDIT, - "foreground-gdk", + "foreground-rgba", COL_COLOR, NULL); g_signal_connect(G_OBJECT(renderer), "edited", G_CALLBACK(renderer_edited), tree2_w); -- cgit v1.2.3 From bfa7375c10dfabf6b3289041c12d698861277d90 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:43:34 +0900 Subject: kconfig: gconf: replace GtkHPaned and GtkVPaned with GtkPaned GtkHPaned and GtkVPaned are deprecated with GTK 3.2. [1] [2] Use GtkPaned instead. [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/3.2.0/gtk/gtkhpaned.c#L44 [2]: https://gitlab.gnome.org/GNOME/gtk/-/blob/3.2.0/gtk/gtkvpaned.c#L44 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.ui | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.ui b/scripts/kconfig/gconf.ui index 806ea3d1bac4..c37807e8b782 100644 --- a/scripts/kconfig/gconf.ui +++ b/scripts/kconfig/gconf.ui @@ -422,7 +422,7 @@ - + 1 True True @@ -453,7 +453,8 @@ - + + vertical True True -- cgit v1.2.3 From 65056488e8bfaf6626cd2bba9fa847b264d9fefc Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:43:35 +0900 Subject: kconfig: gconf: show GTK version in About dialog Likewise xconfig, it is useful to display the GTK version in the About dialog. Signed-off-by: Masahiro Yamada Acked-by: Randy Dunlap Tested-by: Randy Dunlap --- scripts/kconfig/gconf.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index f4c2b07e0207..7725d2c9d92a 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -525,7 +525,11 @@ static void on_about1_activate(GtkMenuItem *menuitem, gpointer user_data) dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd), GTK_DIALOG_DESTROY_WITH_PARENT, GTK_MESSAGE_INFO, - GTK_BUTTONS_CLOSE, "%s", about_text); + GTK_BUTTONS_CLOSE, "%s\nGTK version: %d.%d.%d", + about_text, + gtk_get_major_version(), + gtk_get_minor_version(), + gtk_get_micro_version()); gtk_dialog_run(GTK_DIALOG(dialog)); gtk_widget_destroy(dialog); } -- cgit v1.2.3 From 263e70bc42862af18dce43393ef14277827a0c7f Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:48:31 +0900 Subject: kconfig: add a function to dump all menu entries in a tree-like format This is useful for debugging purposes. menu_finalize() re-parents menu entries, and this function can be used to dump the final structure of the menu tree. Signed-off-by: Masahiro Yamada --- scripts/kconfig/lkc.h | 1 + scripts/kconfig/menu.c | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) (limited to 'scripts') diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index 37b606c74bff..56548efc14d7 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h @@ -102,6 +102,7 @@ struct menu *menu_get_menu_or_parent_menu(struct menu *menu); int get_jump_key_char(void); struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head); void menu_get_ext_help(struct menu *menu, struct gstr *help); +void menu_dump(void); /* symbol.c */ void sym_clear_all_valid(void); diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c index a5e5b4fdcd93..0f1a6513987c 100644 --- a/scripts/kconfig/menu.c +++ b/scripts/kconfig/menu.c @@ -788,3 +788,77 @@ void menu_get_ext_help(struct menu *menu, struct gstr *help) if (sym) get_symbol_str(help, sym, NULL); } + +/** + * menu_dump - dump all menu entries in a tree-like format + */ +void menu_dump(void) +{ + struct menu *menu = &rootmenu; + unsigned long long bits = 0; + int indent = 0; + + while (menu) { + + for (int i = indent - 1; i >= 0; i--) { + if (bits & (1ULL << i)) { + if (i > 0) + printf("| "); + else + printf("|-- "); + } else { + if (i > 0) + printf(" "); + else + printf("`-- "); + } + } + + switch (menu->type) { + case M_CHOICE: + printf("choice \"%s\"\n", menu->prompt->text); + break; + case M_COMMENT: + printf("comment \"%s\"\n", menu->prompt->text); + break; + case M_IF: + printf("if\n"); + break; + case M_MENU: + printf("menu \"%s\"", menu->prompt->text); + if (!menu->sym) { + printf("\n"); + break; + } + printf(" + "); + /* fallthrough */ + case M_NORMAL: + printf("symbol %s\n", menu->sym->name); + break; + } + if (menu->list) { + bits <<= 1; + menu = menu->list; + if (menu->next) + bits |= 1; + else + bits &= ~1; + indent++; + continue; + } + + while (menu && !menu->next) { + menu = menu->parent; + bits >>= 1; + indent--; + } + + if (menu) { + menu = menu->next; + if (menu->next) + bits |= 1; + else + bits &= ~1; + } + } +} -- cgit v1.2.3 From 721bfe583c52ba1ea74b3736a31a9dcfe6dd6d95 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:48:56 +0900 Subject: kconfig: qconf: fix ConfigList::updateListAllforAll() ConfigList::updateListForAll() and ConfigList::updateListAllforAll() are identical. Commit f9b918fae678 ("kconfig: qconf: move ConfigView::updateList(All) to ConfigList class") was a misconversion. Fixes: f9b918fae678 ("kconfig: qconf: move ConfigView::updateList(All) to ConfigList class") Signed-off-by: Masahiro Yamada --- scripts/kconfig/qconf.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index dc056b0a8fde..a7c98bbbd8ac 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -486,7 +486,7 @@ void ConfigList::updateListAllForAll() while (it.hasNext()) { ConfigList *list = it.next(); - list->updateList(); + list->updateListAll(); } } -- cgit v1.2.3 From 87433e3e06a6b6a78a541b6ac39000f41779a882 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Mon, 30 Jun 2025 03:50:35 +0900 Subject: kconfig: qconf: confine {begin,end}Group to constructor and destructor Call beginGroup() in the the constructor and endGroup() in the destructor. This looks cleaner. Signed-off-by: Masahiro Yamada --- scripts/kconfig/qconf.cc | 8 ++++++-- scripts/kconfig/qconf.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc index a7c98bbbd8ac..f8992db1870a 100644 --- a/scripts/kconfig/qconf.cc +++ b/scripts/kconfig/qconf.cc @@ -37,6 +37,12 @@ QAction *ConfigMainWindow::saveAction; ConfigSettings::ConfigSettings() : QSettings("kernel.org", "qconf") { + beginGroup("/kconfig/qconf"); +} + +ConfigSettings::~ConfigSettings() +{ + endGroup(); } /** @@ -1829,7 +1835,6 @@ int main(int ac, char** av) configApp = new QApplication(ac, av); configSettings = new ConfigSettings(); - configSettings->beginGroup("/kconfig/qconf"); v = new ConfigMainWindow(); //zconfdump(stdout); @@ -1837,7 +1842,6 @@ int main(int ac, char** av) v->show(); configApp->exec(); - configSettings->endGroup(); delete configSettings; delete v; delete configApp; diff --git a/scripts/kconfig/qconf.h b/scripts/kconfig/qconf.h index 62ab3286d04f..ab4e51f12914 100644 --- a/scripts/kconfig/qconf.h +++ b/scripts/kconfig/qconf.h @@ -24,6 +24,7 @@ class ConfigMainWindow; class ConfigSettings : public QSettings { public: ConfigSettings(); + ~ConfigSettings(void); QList readSizes(const QString& key, bool *ok); bool writeSizes(const QString& key, const QList& value); }; -- cgit v1.2.3 From e06aa69de21b6de2ef83f559768a4005114f5661 Mon Sep 17 00:00:00 2001 From: Giuliano Procida Date: Tue, 1 Jul 2025 16:19:10 +0100 Subject: gendwarfksyms: use preferred form of sizeof for allocation The preferred form is to use the variable being allocated to, rather than explicitly supplying a type name which might become stale. Also do this for memset. Suggested-by: Masahiro Yamada Signed-off-by: Giuliano Procida Reviewed-by: Sami Tolvanen Signed-off-by: Masahiro Yamada --- scripts/gendwarfksyms/cache.c | 2 +- scripts/gendwarfksyms/die.c | 4 ++-- scripts/gendwarfksyms/dwarf.c | 2 +- scripts/gendwarfksyms/kabi.c | 2 +- scripts/gendwarfksyms/symbols.c | 2 +- scripts/gendwarfksyms/types.c | 4 ++-- 6 files changed, 8 insertions(+), 8 deletions(-) (limited to 'scripts') diff --git a/scripts/gendwarfksyms/cache.c b/scripts/gendwarfksyms/cache.c index c9c19b86a686..1c640db93db3 100644 --- a/scripts/gendwarfksyms/cache.c +++ b/scripts/gendwarfksyms/cache.c @@ -15,7 +15,7 @@ void cache_set(struct cache *cache, unsigned long key, int value) { struct cache_item *ci; - ci = xmalloc(sizeof(struct cache_item)); + ci = xmalloc(sizeof(*ci)); ci->key = key; ci->value = value; hash_add(cache->cache, &ci->hash, hash_32(key)); diff --git a/scripts/gendwarfksyms/die.c b/scripts/gendwarfksyms/die.c index 6183bbbe7b54..052f7a3f975a 100644 --- a/scripts/gendwarfksyms/die.c +++ b/scripts/gendwarfksyms/die.c @@ -33,7 +33,7 @@ static struct die *create_die(Dwarf_Die *die, enum die_state state) { struct die *cd; - cd = xmalloc(sizeof(struct die)); + cd = xmalloc(sizeof(*cd)); init_die(cd); cd->addr = (uintptr_t)die->addr; @@ -123,7 +123,7 @@ static struct die_fragment *append_item(struct die *cd) { struct die_fragment *df; - df = xmalloc(sizeof(struct die_fragment)); + df = xmalloc(sizeof(*df)); df->type = FRAGMENT_EMPTY; list_add_tail(&df->list, &cd->fragments); return df; diff --git a/scripts/gendwarfksyms/dwarf.c b/scripts/gendwarfksyms/dwarf.c index 13ea7bf1ae7d..3538a7d9cb07 100644 --- a/scripts/gendwarfksyms/dwarf.c +++ b/scripts/gendwarfksyms/dwarf.c @@ -634,7 +634,7 @@ static int get_union_kabi_status(Dwarf_Die *die, Dwarf_Die *placeholder, * Note that the user of this feature is responsible for ensuring * that the structure actually remains ABI compatible. */ - memset(&state.kabi, 0, sizeof(struct kabi_state)); + memset(&state.kabi, 0, sizeof(state.kabi)); res = checkp(process_die_container(&state, NULL, die, check_union_member_kabi_status, diff --git a/scripts/gendwarfksyms/kabi.c b/scripts/gendwarfksyms/kabi.c index b3ade713778f..e3c2a3ccf51a 100644 --- a/scripts/gendwarfksyms/kabi.c +++ b/scripts/gendwarfksyms/kabi.c @@ -228,7 +228,7 @@ void kabi_read_rules(int fd) if (type == KABI_RULE_TYPE_UNKNOWN) error("unsupported kABI rule type: '%s'", field); - rule = xmalloc(sizeof(struct rule)); + rule = xmalloc(sizeof(*rule)); rule->type = type; rule->target = xstrdup(get_rule_field(&rule_str, &left)); diff --git a/scripts/gendwarfksyms/symbols.c b/scripts/gendwarfksyms/symbols.c index 327f87389c34..35ed594f0749 100644 --- a/scripts/gendwarfksyms/symbols.c +++ b/scripts/gendwarfksyms/symbols.c @@ -146,7 +146,7 @@ void symbol_read_exports(FILE *file) continue; } - sym = xcalloc(1, sizeof(struct symbol)); + sym = xcalloc(1, sizeof(*sym)); sym->name = name; sym->addr.section = SHN_UNDEF; sym->state = SYMBOL_UNPROCESSED; diff --git a/scripts/gendwarfksyms/types.c b/scripts/gendwarfksyms/types.c index 7bd459ea6c59..5344c7b9a9ce 100644 --- a/scripts/gendwarfksyms/types.c +++ b/scripts/gendwarfksyms/types.c @@ -43,7 +43,7 @@ static int type_list_append(struct list_head *list, const char *s, void *owned) if (!s) return 0; - entry = xmalloc(sizeof(struct type_list_entry)); + entry = xmalloc(sizeof(*entry)); entry->str = s; entry->owned = owned; list_add_tail(&entry->list, list); @@ -120,7 +120,7 @@ static struct type_expansion *type_map_add(const char *name, struct type_expansion *e; if (__type_map_get(name, &e)) { - e = xmalloc(sizeof(struct type_expansion)); + e = xmalloc(sizeof(*e)); type_expansion_init(e); e->name = xstrdup(name); -- cgit v1.2.3 From d8f26717c901b7ec88c3151988fe70ecaed990b8 Mon Sep 17 00:00:00 2001 From: Giuliano Procida Date: Tue, 1 Jul 2025 16:19:11 +0100 Subject: gendwarfksyms: order -T symtypes output by name When writing symtypes information, we iterate through the entire hash table containing type expansions. The key order varies unpredictably as new entries are added, making it harder to compare symtypes between builds. Resolve this by sorting the type expansions by name before output. Signed-off-by: Giuliano Procida Acked-by: Greg Kroah-Hartman Reviewed-by: Sami Tolvanen Signed-off-by: Masahiro Yamada --- scripts/gendwarfksyms/types.c | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) (limited to 'scripts') diff --git a/scripts/gendwarfksyms/types.c b/scripts/gendwarfksyms/types.c index 5344c7b9a9ce..9c3b053bf061 100644 --- a/scripts/gendwarfksyms/types.c +++ b/scripts/gendwarfksyms/types.c @@ -6,6 +6,8 @@ #define _GNU_SOURCE #include #include +#include +#include #include #include "gendwarfksyms.h" @@ -179,20 +181,41 @@ static int type_map_get(const char *name, struct type_expansion **res) return -1; } +static int cmp_expansion_name(const void *p1, const void *p2) +{ + struct type_expansion *const *e1 = p1; + struct type_expansion *const *e2 = p2; + + return strcmp((*e1)->name, (*e2)->name); +} + static void type_map_write(FILE *file) { struct type_expansion *e; struct hlist_node *tmp; + struct type_expansion **es; + size_t count = 0; + size_t i = 0; if (!file) return; - hash_for_each_safe(type_map, e, tmp, hash) { - checkp(fputs(e->name, file)); + hash_for_each_safe(type_map, e, tmp, hash) + ++count; + es = xmalloc(count * sizeof(*es)); + hash_for_each_safe(type_map, e, tmp, hash) + es[i++] = e; + + qsort(es, count, sizeof(*es), cmp_expansion_name); + + for (i = 0; i < count; ++i) { + checkp(fputs(es[i]->name, file)); checkp(fputs(" ", file)); - type_list_write(&e->expanded, file); + type_list_write(&es[i]->expanded, file); checkp(fputs("\n", file)); } + + free(es); } static void type_map_free(void) -- cgit v1.2.3 From b9f75396ec107628cc5f52fb6e055c1c9dc68401 Mon Sep 17 00:00:00 2001 From: Jeremy Linton Date: Mon, 14 Jul 2025 17:29:23 -0500 Subject: scripts: add zboot support to extract-vmlinux Zboot compressed kernel images are used for arm64 kernels on various distros. extract-vmlinux fails with those kernels because the wrapped image is another PE. While this could be a bit confusing, the tools primary purpose of unwrapping and decompressing the contained kernel image makes it the obvious place for this functionality. Add a 'file' check in check_vmlinux() that detects a contained PE image before trying readelf. Recent (FILES_39, Jun/2020) file implementations output something like: "Linux kernel ARM64 boot executable Image, little-endian, 4K pages" Which is also a stronger statement than readelf provides so drop that part of the comment. At the same time this means that kernel images which don't appear to contain a compressed image will be returned rather than reporting an error. Which matches the behavior for existing ELF files. The extracted PE image can then be inspected, or used as would any other kernel PE. Signed-off-by: Jeremy Linton Signed-off-by: Masahiro Yamada --- scripts/extract-vmlinux | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) (limited to 'scripts') diff --git a/scripts/extract-vmlinux b/scripts/extract-vmlinux index 8995cd304e6e..189956b5a5c8 100755 --- a/scripts/extract-vmlinux +++ b/scripts/extract-vmlinux @@ -12,13 +12,12 @@ check_vmlinux() { - # Use readelf to check if it's a valid ELF - # TODO: find a better to way to check that it's really vmlinux - # and not just an elf - readelf -h $1 > /dev/null 2>&1 || return 1 - - cat $1 - exit 0 + if file "$1" | grep -q 'Linux kernel.*boot executable' || + readelf -h "$1" > /dev/null 2>&1 + then + cat "$1" + exit 0 + fi } try_decompress() -- cgit v1.2.3 From 6d4d44254e43157bb760aa16367a394c2ab299b8 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 17 Jul 2025 08:24:08 +0900 Subject: kconfig: gconf: fix single view to display dependent symbols correctly In the following example, the symbol C was never displayed in Single view. Fix the recursion logic so that all symbols are shown. menu "menu" config A bool "A" config B bool "B" depends on A config C bool "C" depends on B endmenu Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 7725d2c9d92a..c67b35807e8e 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -914,9 +914,7 @@ static gboolean on_treeview1_button_press_event(GtkWidget *widget, static void _display_tree(GtkTreeStore *tree, struct menu *menu, GtkTreeIter *parent) { - struct property *prop; struct menu *child; - enum prop_type ptype; GtkTreeIter iter; for (child = menu->list; child; child = child->next) { @@ -929,9 +927,6 @@ static void _display_tree(GtkTreeStore *tree, struct menu *menu, if (child->type == M_IF) continue; - prop = child->prompt; - ptype = prop ? prop->type : P_UNKNOWN; - if ((view_mode == SPLIT_VIEW) && !(child->flags & MENU_ROOT) && (tree == tree1)) continue; @@ -943,16 +938,7 @@ static void _display_tree(GtkTreeStore *tree, struct menu *menu, gtk_tree_store_append(tree, &iter, parent); set_node(tree, &iter, child); - if ((view_mode == SINGLE_VIEW) && (ptype == P_MENU)) - continue; -/* - if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT)) - || (view_mode == FULL_VIEW) - || (view_mode == SPLIT_VIEW))*/ - - if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT)) - || (view_mode == FULL_VIEW) - || (view_mode == SPLIT_VIEW)) + if (view_mode != SINGLE_VIEW || child->type != M_MENU) _display_tree(tree, child, &iter); } } -- cgit v1.2.3 From 15a5ae3b0976d1190728044920cf6337a218ae62 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 17 Jul 2025 08:24:09 +0900 Subject: kconfig: gconf: Fix Back button behavior Clicking the Back button may navigate to a non-menu hierarchy level. [Example] menu "menu1" config A bool "A" default y config B bool "B" depends on A default y menu "menu2" depends on B config C bool "C" default y endmenu endmenu After being re-parented by menu_finalize(), the menu tree is structured like follows: menu "menu1" \-- A \-- B \-- menu "menu2" \-- C In Single view, visit "menu2" and click the Back button. It should go up to "menu1" and show A, B and "menu2", but instead goes up to A and show only B and "menu2". This is a bug in on_back_clicked(). Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index c67b35807e8e..d9ea71664412 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -553,12 +553,8 @@ static void on_license1_activate(GtkMenuItem *menuitem, gpointer user_data) /* toolbar handlers */ static void on_back_clicked(GtkButton *button, gpointer user_data) { - enum prop_type ptype; + browsed = menu_get_parent_menu(browsed) ?: &rootmenu; - browsed = browsed->parent; - ptype = browsed->prompt ? browsed->prompt->type : P_UNKNOWN; - if (ptype != P_MENU) - browsed = browsed->parent; recreate_tree(); if (browsed == &rootmenu) -- cgit v1.2.3 From 2bc0148f78193865065035fe19095c78c3d8129f Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 17 Jul 2025 08:24:10 +0900 Subject: kconfig: gconf: replace GtkImageMenuItem with GtkMenuItem GtkImageMenuItem is deprecated with GTK 3.10. [1] Use GtkMenuItem instead. [1]: https://gitlab.gnome.org/GNOME/gtk/-/blob/3.10.0/gtk/deprecated/gtkimagemenuitem.c#L797 Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.ui | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.ui b/scripts/kconfig/gconf.ui index c37807e8b782..ab4431255fa7 100644 --- a/scripts/kconfig/gconf.ui +++ b/scripts/kconfig/gconf.ui @@ -39,7 +39,7 @@ - + True Load a config file _Load @@ -49,7 +49,7 @@ - + True Save the config in .config _Save @@ -59,7 +59,7 @@ - + True Save the config in a file Save _as @@ -74,7 +74,7 @@ - + True _Quit True @@ -178,7 +178,7 @@ - + True _Introduction True @@ -187,7 +187,7 @@ - + True _About True @@ -196,7 +196,7 @@ - + True _License True -- cgit v1.2.3 From e16f08062f91570aa225bc490e0a92d63ae13769 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 17 Jul 2025 08:24:11 +0900 Subject: kconfig: gconf: use hyphens in signals Using hyphens in signal names is the official convention, even though underscores also work. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index d9ea71664412..e4f89270d19f 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -997,25 +997,25 @@ static void init_main_window(const gchar *glade_file) G_CALLBACK(on_window1_destroy), NULL); g_signal_connect(main_wnd, "configure-event", G_CALLBACK(on_window1_configure), NULL); - g_signal_connect(main_wnd, "delete_event", + g_signal_connect(main_wnd, "delete-event", G_CALLBACK(on_window1_delete_event), NULL); hpaned = GTK_WIDGET(gtk_builder_get_object(builder, "hpaned1")); vpaned = GTK_WIDGET(gtk_builder_get_object(builder, "vpaned1")); tree1_w = GTK_WIDGET(gtk_builder_get_object(builder, "treeview1")); - g_signal_connect(tree1_w, "cursor_changed", + g_signal_connect(tree1_w, "cursor-changed", G_CALLBACK(on_treeview2_cursor_changed), NULL); - g_signal_connect(tree1_w, "button_press_event", + g_signal_connect(tree1_w, "button-press-event", G_CALLBACK(on_treeview1_button_press_event), NULL); - g_signal_connect(tree1_w, "key_press_event", + g_signal_connect(tree1_w, "key-press-event", G_CALLBACK(on_treeview2_key_press_event), NULL); tree2_w = GTK_WIDGET(gtk_builder_get_object(builder, "treeview2")); - g_signal_connect(tree2_w, "cursor_changed", + g_signal_connect(tree2_w, "cursor-changed", G_CALLBACK(on_treeview2_cursor_changed), NULL); - g_signal_connect(tree2_w, "button_press_event", + g_signal_connect(tree2_w, "button-press-event", G_CALLBACK(on_treeview2_button_press_event), NULL); - g_signal_connect(tree2_w, "key_press_event", + g_signal_connect(tree2_w, "key-press-event", G_CALLBACK(on_treeview2_key_press_event), NULL); text_w = GTK_WIDGET(gtk_builder_get_object(builder, "textview3")); -- cgit v1.2.3 From 5ceb15fdc629aa3030e8f8987c561d36678f9559 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 17 Jul 2025 08:24:12 +0900 Subject: kconfig: gconf: remove unneeded variable in text_insert_msg The 'msg' and 'message' refer to the same pointer. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index e4f89270d19f..651140af7d13 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -90,11 +90,10 @@ static void text_insert_help(struct menu *menu) } -static void text_insert_msg(const char *title, const char *message) +static void text_insert_msg(const char *title, const char *msg) { GtkTextBuffer *buffer; GtkTextIter start, end; - const char *msg = message; buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); gtk_text_buffer_get_bounds(buffer, &start, &end); -- cgit v1.2.3 From eb549e194bf2d5c86b1b7a71fad54d610dd6c892 Mon Sep 17 00:00:00 2001 From: Masahiro Yamada Date: Thu, 17 Jul 2025 08:24:13 +0900 Subject: kconfig: gconf: refactor text_insert_help() text_insert_help() and text_insert_msg() share similar code. Refactor text_insert_help() to eliminate the code duplication. Signed-off-by: Masahiro Yamada --- scripts/kconfig/gconf.c | 33 ++++++++------------------------- 1 file changed, 8 insertions(+), 25 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c index 651140af7d13..8b164ccfa008 100644 --- a/scripts/kconfig/gconf.c +++ b/scripts/kconfig/gconf.c @@ -64,15 +64,10 @@ static void conf_changed(bool dirty) /* Utility Functions */ - -static void text_insert_help(struct menu *menu) +static void text_insert_msg(const char *title, const char *msg) { GtkTextBuffer *buffer; GtkTextIter start, end; - const char *prompt = menu_get_prompt(menu); - struct gstr help = str_new(); - - menu_get_ext_help(menu, &help); buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); gtk_text_buffer_get_bounds(buffer, &start, &end); @@ -80,33 +75,21 @@ static void text_insert_help(struct menu *menu) gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15); gtk_text_buffer_get_end_iter(buffer, &end); - gtk_text_buffer_insert_with_tags(buffer, &end, prompt, -1, tag1, + gtk_text_buffer_insert_with_tags(buffer, &end, title, -1, tag1, NULL); gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2); gtk_text_buffer_get_end_iter(buffer, &end); - gtk_text_buffer_insert_with_tags(buffer, &end, str_get(&help), -1, tag2, + gtk_text_buffer_insert_with_tags(buffer, &end, msg, -1, tag2, NULL); - str_free(&help); } - -static void text_insert_msg(const char *title, const char *msg) +static void text_insert_help(struct menu *menu) { - GtkTextBuffer *buffer; - GtkTextIter start, end; - - buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w)); - gtk_text_buffer_get_bounds(buffer, &start, &end); - gtk_text_buffer_delete(buffer, &start, &end); - gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15); + struct gstr help = str_new(); - gtk_text_buffer_get_end_iter(buffer, &end); - gtk_text_buffer_insert_with_tags(buffer, &end, title, -1, tag1, - NULL); - gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2); - gtk_text_buffer_get_end_iter(buffer, &end); - gtk_text_buffer_insert_with_tags(buffer, &end, msg, -1, tag2, - NULL); + menu_get_ext_help(menu, &help); + text_insert_msg(menu_get_prompt(menu), str_get(&help)); + str_free(&help); } static void _select_menu(GtkTreeView *view, GtkTreeModel *model, -- cgit v1.2.3 From a8f0b1f8ef628bd1003eed650862836e97b89fdd Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 23 Jul 2025 22:50:28 -0700 Subject: kstack_erase: Support Clang stack depth tracking Wire up CONFIG_KSTACK_ERASE to Clang 21's new stack depth tracking callback[1] option. Link: https://clang.llvm.org/docs/SanitizerCoverage.html#tracing-stack-depth [1] Acked-by: Nicolas Schier Link: https://lore.kernel.org/r/20250724055029.3623499-4-kees@kernel.org Signed-off-by: Kees Cook --- scripts/Makefile.kstack_erase | 6 ++++++ security/Kconfig.hardening | 5 ++++- 2 files changed, 10 insertions(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/Makefile.kstack_erase b/scripts/Makefile.kstack_erase index 5223d3a35817..c7bc2379e113 100644 --- a/scripts/Makefile.kstack_erase +++ b/scripts/Makefile.kstack_erase @@ -8,6 +8,12 @@ kstack-erase-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK_VERBOSE) += -fplugin-arg-stack DISABLE_KSTACK_ERASE := -fplugin-arg-stackleak_plugin-disable endif +ifdef CONFIG_CC_IS_CLANG +kstack-erase-cflags-y += -fsanitize-coverage=stack-depth +kstack-erase-cflags-y += -fsanitize-coverage-stack-depth-callback-min=$(CONFIG_KSTACK_ERASE_TRACK_MIN_SIZE) +DISABLE_KSTACK_ERASE := -fno-sanitize-coverage=stack-depth +endif + KSTACK_ERASE_CFLAGS := $(kstack-erase-cflags-y) export STACKLEAK_CFLAGS DISABLE_KSTACK_ERASE diff --git a/security/Kconfig.hardening b/security/Kconfig.hardening index f7aa2024ab25..b9a5bc3430aa 100644 --- a/security/Kconfig.hardening +++ b/security/Kconfig.hardening @@ -82,10 +82,13 @@ choice endchoice +config CC_HAS_SANCOV_STACK_DEPTH_CALLBACK + def_bool $(cc-option,-fsanitize-coverage-stack-depth-callback-min=1) + config KSTACK_ERASE bool "Poison kernel stack before returning from syscalls" depends on HAVE_ARCH_KSTACK_ERASE - depends on GCC_PLUGINS + depends on GCC_PLUGINS || CC_HAS_SANCOV_STACK_DEPTH_CALLBACK help This option makes the kernel erase the kernel stack before returning from system calls. This has the effect of leaving -- cgit v1.2.3 From fc525d625a22c2179877955ee87e33f532b0674c Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 29 Jul 2025 16:50:48 -0700 Subject: kstack_erase: Fix missed export of renamed KSTACK_ERASE_CFLAGS Certain targets disable kstack_erase by filtering out KSTACK_ERASE_CFLAGS rather than adding DISABLE_KSTACK_ERASE. The renaming to kstack_erase missed the CFLAGS export, which broke those build targets (e.g. x86 vdso32). Fixes: 76261fc7d1be ("stackleak: Split KSTACK_ERASE_CFLAGS from GCC_PLUGINS_CFLAGS") Signed-off-by: Kees Cook --- scripts/Makefile.kstack_erase | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'scripts') diff --git a/scripts/Makefile.kstack_erase b/scripts/Makefile.kstack_erase index c7bc2379e113..ee7e4ef7b892 100644 --- a/scripts/Makefile.kstack_erase +++ b/scripts/Makefile.kstack_erase @@ -16,6 +16,6 @@ endif KSTACK_ERASE_CFLAGS := $(kstack-erase-cflags-y) -export STACKLEAK_CFLAGS DISABLE_KSTACK_ERASE +export KSTACK_ERASE_CFLAGS DISABLE_KSTACK_ERASE KBUILD_CFLAGS += $(KSTACK_ERASE_CFLAGS) -- cgit v1.2.3 From 53f433891e698e76aaf01b84b30a17a79a53535c Mon Sep 17 00:00:00 2001 From: WangYuli Date: Tue, 22 Jul 2025 15:34:31 +0800 Subject: scripts/spelling.txt: add notifer||notifier to spelling.txt This typo was not listed in scripts/spelling.txt, thus it was more difficult to detect. Add it for convenience. Link: https://lkml.kernel.org/r/02153C05ED7B49B7+20250722073431.21983-8-wangyuli@uniontech.com Signed-off-by: WangYuli Reviewed-by: Jonathan Cameron Signed-off-by: Andrew Morton --- scripts/spelling.txt | 1 + 1 file changed, 1 insertion(+) (limited to 'scripts') diff --git a/scripts/spelling.txt b/scripts/spelling.txt index ac94fa1c2415..1e89b92c2f9a 100644 --- a/scripts/spelling.txt +++ b/scripts/spelling.txt @@ -1099,6 +1099,7 @@ notication||notification notications||notifications notifcations||notifications notifed||notified +notifer||notifier notity||notify notfify||notify nubmer||number -- cgit v1.2.3 From 1918f983687aa73bf0e5bc73431898994fce35a8 Mon Sep 17 00:00:00 2001 From: Suchit Karunakaran Date: Sun, 27 Jul 2025 01:13:07 +0530 Subject: kconfig: lxdialog: replace strcpy with snprintf in print_autowrap strcpy() does not perform bounds checking and can lead to buffer overflows if the source string exceeds the destination buffer size. In print_autowrap(), replace strcpy() with snprintf() to safely copy the prompt string into the fixed-size tempstr buffer. Signed-off-by: Suchit Karunakaran Signed-off-by: Masahiro Yamada --- scripts/kconfig/lxdialog/util.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/lxdialog/util.c b/scripts/kconfig/lxdialog/util.c index 964139c87fcb..b34000beb294 100644 --- a/scripts/kconfig/lxdialog/util.c +++ b/scripts/kconfig/lxdialog/util.c @@ -345,8 +345,7 @@ void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x) int prompt_len, room, wlen; char tempstr[MAX_LEN + 1], *word, *sp, *sp2, *newline_separator = 0; - strcpy(tempstr, prompt); - + snprintf(tempstr, sizeof(tempstr), "%s", prompt); prompt_len = strlen(tempstr); if (prompt_len <= width - x * 2) { /* If prompt is short */ -- cgit v1.2.3 From 5ac726653a1029a2eccba93bbe59e01fc9725828 Mon Sep 17 00:00:00 2001 From: Suchit Karunakaran Date: Sun, 27 Jul 2025 22:14:33 +0530 Subject: kconfig: lxdialog: replace strcpy() with strncpy() in inputbox.c strcpy() performs no bounds checking and can lead to buffer overflows if the input string exceeds the destination buffer size. This patch replaces it with strncpy(), and null terminates the input string. Signed-off-by: Suchit Karunakaran Reviewed-by: Nicolas Schier Signed-off-by: Masahiro Yamada --- scripts/kconfig/lxdialog/inputbox.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/kconfig/lxdialog/inputbox.c b/scripts/kconfig/lxdialog/inputbox.c index 3c6e24b20f5b..5e4a131724f2 100644 --- a/scripts/kconfig/lxdialog/inputbox.c +++ b/scripts/kconfig/lxdialog/inputbox.c @@ -39,8 +39,10 @@ int dialog_inputbox(const char *title, const char *prompt, int height, int width if (!init) instr[0] = '\0'; - else - strcpy(instr, init); + else { + strncpy(instr, init, sizeof(dialog_input_result) - 1); + instr[sizeof(dialog_input_result) - 1] = '\0'; + } do_resize: if (getmaxy(stdscr) <= (height - INPUTBOX_HEIGHT_MIN)) -- cgit v1.2.3 From 51337a9a3a404fde0f5337662ffc7699793dfeb5 Mon Sep 17 00:00:00 2001 From: Ada Couprie Diaz Date: Thu, 21 Aug 2025 13:07:35 +0100 Subject: kasan: fix GCC mem-intrinsic prefix with sw tags GCC doesn't support "hwasan-kernel-mem-intrinsic-prefix", only "asan-kernel-mem-intrinsic-prefix"[0], while LLVM supports both. This is already taken into account when checking "CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX", but not in the KASAN Makefile adding those parameters when "CONFIG_KASAN_SW_TAGS" is enabled. Replace the version check with "CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX", which already validates that mem-intrinsic prefix parameter can be used, and choose the correct name depending on compiler. GCC 13 and above trigger "CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX" which prevents `mem{cpy,move,set}()` being redefined in "mm/kasan/shadow.c" since commit 36be5cba99f6 ("kasan: treat meminstrinsic as builtins in uninstrumented files"), as we expect the compiler to prefix those calls with `__(hw)asan_` instead. But as the option passed to GCC has been incorrect, the compiler has not been emitting those prefixes, effectively never calling the instrumented versions of `mem{cpy,move,set}()` with "CONFIG_KASAN_SW_TAGS" enabled. If "CONFIG_FORTIFY_SOURCES" is enabled, this issue would be mitigated as it redefines `mem{cpy,move,set}()` and properly aliases the `__underlying_mem*()` that will be called to the instrumented versions. Link: https://lkml.kernel.org/r/20250821120735.156244-1-ada.coupriediaz@arm.com Link: https://gcc.gnu.org/onlinedocs/gcc-13.4.0/gcc/Optimize-Options.html [0] Signed-off-by: Ada Couprie Diaz Fixes: 36be5cba99f6 ("kasan: treat meminstrinsic as builtins in uninstrumented files") Reviewed-by: Yeoreum Yun Cc: Alexander Potapenko Cc: Andrey Konovalov Cc: Andrey Ryabinin Cc: Dmitriy Vyukov Cc: Marco Elver Cc: Marc Rutland Cc: Michael Ellerman Cc: Nathan Chancellor Cc: Vincenzo Frascino Cc: Signed-off-by: Andrew Morton --- scripts/Makefile.kasan | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'scripts') diff --git a/scripts/Makefile.kasan b/scripts/Makefile.kasan index 693dbbebebba..0ba2aac3b8dc 100644 --- a/scripts/Makefile.kasan +++ b/scripts/Makefile.kasan @@ -86,10 +86,14 @@ kasan_params += hwasan-instrument-stack=$(stack_enable) \ hwasan-use-short-granules=0 \ hwasan-inline-all-checks=0 -# Instrument memcpy/memset/memmove calls by using instrumented __hwasan_mem*(). -ifeq ($(call clang-min-version, 150000)$(call gcc-min-version, 130000),y) - kasan_params += hwasan-kernel-mem-intrinsic-prefix=1 -endif +# Instrument memcpy/memset/memmove calls by using instrumented __(hw)asan_mem*(). +ifdef CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX + ifdef CONFIG_CC_IS_GCC + kasan_params += asan-kernel-mem-intrinsic-prefix=1 + else + kasan_params += hwasan-kernel-mem-intrinsic-prefix=1 + endif +endif # CONFIG_CC_HAS_KASAN_MEMINTRINSIC_PREFIX endif # CONFIG_KASAN_SW_TAGS -- cgit v1.2.3 From 8851e27d2cb947ea8bbbe8e812068f7bf5cbd00b Mon Sep 17 00:00:00 2001 From: Miguel Ojeda Date: Fri, 29 Aug 2025 21:55:25 +0200 Subject: rust: support Rust >= 1.91.0 target spec Starting with Rust 1.91.0 (expected 2025-10-30), the target spec format has changed the type of the `target-pointer-width` key from string to integer [1]. Thus conditionally use one or the other depending on the version. Cc: Waffle Maybe Link: https://github.com/rust-lang/rust/pull/144443 [1] Link: https://lore.kernel.org/r/20250829195525.721664-1-ojeda@kernel.org Signed-off-by: Miguel Ojeda --- scripts/generate_rust_target.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'scripts') diff --git a/scripts/generate_rust_target.rs b/scripts/generate_rust_target.rs index 39c82908ff3a..38b3416bb979 100644 --- a/scripts/generate_rust_target.rs +++ b/scripts/generate_rust_target.rs @@ -225,7 +225,11 @@ fn main() { ts.push("features", features); ts.push("llvm-target", "x86_64-linux-gnu"); ts.push("supported-sanitizers", ["kcfi", "kernel-address"]); - ts.push("target-pointer-width", "64"); + if cfg.rustc_version_atleast(1, 91, 0) { + ts.push("target-pointer-width", 64); + } else { + ts.push("target-pointer-width", "64"); + } } else if cfg.has("X86_32") { // This only works on UML, as i386 otherwise needs regparm support in rustc if !cfg.has("UML") { @@ -245,7 +249,11 @@ fn main() { } ts.push("features", features); ts.push("llvm-target", "i386-unknown-linux-gnu"); - ts.push("target-pointer-width", "32"); + if cfg.rustc_version_atleast(1, 91, 0) { + ts.push("target-pointer-width", 32); + } else { + ts.push("target-pointer-width", "32"); + } } else if cfg.has("LOONGARCH") { panic!("loongarch uses the builtin rustc loongarch64-unknown-none-softfloat target"); } else { -- cgit v1.2.3