lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <21b98e7a-93ae-451c-b7a1-e1468e8da0ca@infradead.org>
Date: Fri, 14 Nov 2025 16:50:57 -0800
From: Randy Dunlap <rdunlap@...radead.org>
To: Mauro Carvalho Chehab <mchehab+huawei@...nel.org>
Cc: Linux Doc Mailing List <linux-doc@...r.kernel.org>,
 Jonathan Corbet <corbet@....net>, linux-kernel@...r.kernel.org,
 Jani Nikula <jani.nikula@...ux.intel.com>
Subject: Re: [PATCH] kernel-doc: add support for handling global variables

Hi,

I would still like to see this done. (more below)

On 9/10/25 1:54 AM, Mauro Carvalho Chehab wrote:


> Anyway, I did some changes at the code for it to better parse your
> examples. I'm also storing two additional fields captured at parsing
> time:
> 
> 1. full_proto: the prototype before any transformation;
> 2. default_val: if not None, contains the initialized value.
> 
> Right now, I'm using only default_val ato output, but, with both,
> we can play with printing them or not at kdoc_output logic.
> 
> Thanks,
> Mauro
> 
> [PATCH v2] kernel-doc: add support for handling global variables
> 
> Specially on kAPI, sometimes it is desirable to be able to
> describe global variables that are part of kAPI.
> 
> Documenting vars with Sphinx is simple, as we don't need
> to parse a data struct. All we need is the variable
> declaration and use natice C domain ::c:var: to format it
> for us.
> 
> Add support for it.
> 
> Link: https://lore.kernel.org/linux-doc/491c3022-cef8-4860-a945-c9c4a3b63c09@infradead.org/T/#m947c25d95cb1d96a394410ab1131dc8e9e5013f1
> Suggested-by: Randy Dunlap <rdunlap@...radead.org>
> Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@...nel.org>
> 
> diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output.py
> index 1eca9a918558..469f01a74724 100644
> --- a/scripts/lib/kdoc/kdoc_output.py
> +++ b/scripts/lib/kdoc/kdoc_output.py
> @@ -199,6 +199,10 @@ class OutputFormat:
>              self.out_enum(fname, name, args)
>              return self.data
>  
> +        if dtype == "global":
> +            self.out_global(fname, name, args)
> +            return self.data
> +
>          if dtype == "typedef":
>              self.out_typedef(fname, name, args)
>              return self.data
> @@ -227,6 +231,9 @@ class OutputFormat:
>      def out_enum(self, fname, name, args):
>          """Outputs an enum"""
>  
> +    def out_global(self, fname, name, args):
> +        """Outputs a global variable"""
> +
>      def out_typedef(self, fname, name, args):
>          """Outputs a typedef"""
>  
> @@ -472,6 +479,24 @@ class RestFormat(OutputFormat):
>          self.lineprefix = oldprefix
>          self.out_section(args)
>  
> +    def out_global(self, fname, name, args):
> +        oldprefix = self.lineprefix
> +        ln = args.declaration_start_line
> +        prototype = args.other_stuff["var_type"]
> +
> +        self.data += f"\n\n.. c:var:: {prototype}\n\n"
> +
> +        self.print_lineno(ln)
> +        self.lineprefix = "  "
> +        self.output_highlight(args.get('purpose', ''))
> +        self.data += "\n"
> +
> +        if args.other_stuff["default_val"]:
> +            self.data += f'{self.lineprefix}**Initialization**\n\n'
> +            self.output_highlight(f'default: ``{args.other_stuff["default_val"]}``')
> +
> +        self.out_section(args)
> +
>      def out_typedef(self, fname, name, args):
>  
>          oldprefix = self.lineprefix
> @@ -772,6 +797,26 @@ class ManFormat(OutputFormat):
>              self.data += f'.SH "{section}"' + "\n"
>              self.output_highlight(text)
>  
> +    def out_global(self, fname, name, args):
> +        out_name = self.arg_name(args, name)
> +        prototype = args.other_stuff["var_type"]
> +
> +        self.data += f'.TH "{self.modulename}" 9 "{out_name}" "{self.man_date}" "API Manual" LINUX' + "\n"
> +
> +        self.data += ".SH NAME\n"
> +        self.data += f"{prototype} \\- {args['purpose']}\n"
> +
> +        self.data += ".SH SYNOPSIS\n"
> +        self.data += f"enum {name}" + " {\n"
> +
> +        if args.other_stuff["default_val"]:
> +            self.data += f'.SH "Initialization"' + "\n"
> +            self.output_highlight(f'default: {args.other_stuff["default_val"]}')
> +
> +        for section, text in args.sections.items():
> +            self.data += f'.SH "{section}"' + "\n"
> +            self.output_highlight(text)
> +
>      def out_typedef(self, fname, name, args):
>          module = self.modulename
>          purpose = args.get('purpose')
> diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py
> index 574972e1f741..93a2fcebec28 100644
> --- a/scripts/lib/kdoc/kdoc_parser.py
> +++ b/scripts/lib/kdoc/kdoc_parser.py
> @@ -64,7 +64,7 @@ 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_data = KernRe(r"^\s*\*?\s*(struct|union|enum|typedef|global)\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)
> @@ -886,6 +886,57 @@ class KernelDoc:
>          self.output_declaration('enum', declaration_name,
>                                  purpose=self.entry.declaration_purpose)
>  
> +    def dump_global(self, ln, proto):
> +        """
> +        Stores global variables that are part of kAPI.
> +        """
> +        VAR_ATTRIBS = [
> +            "extern",
> +        ]
> +        OPTIONAL_VAR_ATTR = "^(?:" + "|".join(VAR_ATTRIBS) + ")?"
> +
> +        sub_prefixes = [
> +            (KernRe(r"__read_mostly"), ""),
> +            (KernRe(r"__ro_after_init"), ""),
> +            (KernRe(r"(?://.*)$"), ""),
> +            (KernRe(r"(?:/\*.*\*/)"), ""),
> +            (KernRe(r";$"), ""),
> +        ]
> +
> +        #
> +        # Store the full prototype before modifying it
> +        #
> +        full_proto = proto
> +
> +        #
> +        # Drop comments and macros to have a pure C prototype
> +        #
> +        for search, sub in sub_prefixes:
> +            proto = search.sub(sub, proto)
> +
> +        proto = proto.rstrip()
> +
> +        #
> +        # Variable name is at the end of the declaration
> +        #
> +
> +        r= KernRe(OPTIONAL_VAR_ATTR + r"\w.*\s+([\w_]+)\s*[\d\]\[]*\s*(=.*)?")
> +        if not r.match(proto):
> +           self.emit_msg(ln,f"{proto}: can't parse variable")
> +           return
> +
> +        var_type = r.group(0)
> +        declaration_name = r.group(1)
> +        default_val = r.group(2)
> +        if default_val:
> +            default_val = default_val.lstrip("=").strip()
> +
> +        self.output_declaration("global", declaration_name,
> +                                full_proto=full_proto,
> +                                var_type=var_type,
> +                                default_val=default_val,
> +                                purpose=self.entry.declaration_purpose)
> +
>      def dump_declaration(self, ln, prototype):
>          """
>          Stores a data declaration inside self.entries array.
> @@ -897,6 +948,8 @@ class KernelDoc:
>              self.dump_typedef(ln, prototype)
>          elif self.entry.decl_type in ["union", "struct"]:
>              self.dump_struct(ln, prototype)
> +        elif self.entry.decl_type == "global":
> +            self.dump_global(ln, prototype)
>          else:
>              # This would be a bug
>              self.emit_message(ln, f'Unknown declaration type: {self.entry.decl_type}')
> 

The v2 patch certainly is better with the new regexes to ignore.
I have a few minor changes that I would like to add (in a trivial patch below).
And there are still a few cases that are not handled correctly.
(see my kdoc-globals-test.patch from 9/9/2025)

Also, changing to "var" instead of "global" is good IMO.
---
 scripts/lib/kdoc/kdoc_output.py |    2 +-
 scripts/lib/kdoc/kdoc_parser.py |    1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

--- linux-next-20251114.orig/scripts/lib/kdoc/kdoc_output.py
+++ linux-next-20251114/scripts/lib/kdoc/kdoc_output.py
@@ -808,7 +808,7 @@ class ManFormat(OutputFormat):
         self.data += f"{prototype} \\- {args['purpose']}\n"
 
         self.data += ".SH SYNOPSIS\n"
-        self.data += f"enum {name}" + " {\n"
+        self.data += f"{prototype}" + ";\n"
 
         if args.other_stuff["default_val"]:
             self.data += f'.SH "Initialization"' + "\n"
--- linux-next-20251114.orig/scripts/lib/kdoc/kdoc_parser.py
+++ linux-next-20251114/scripts/lib/kdoc/kdoc_parser.py
@@ -939,6 +939,7 @@ class KernelDoc:
             (KernRe(r"(?://.*)$"), ""),
             (KernRe(r"(?:/\*.*\*/)"), ""),
             (KernRe(r";$"), ""),
+            (KernRe(r"=.*"), ""),
         ]
 
         #
-- 
~Randy


Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ