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

Configure Feed

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

Merge tag 'docs-6.16' of git://git.lwn.net/linux

Pull documentation updates from Jonathan Corbet:
"A moderately busy cycle for documentation this time around:

- The most significant change is the replacement of the old
kernel-doc script (a monstrous collection of Perl regexes that
predates the Git era) with a Python reimplementation. That, too, is
a horrifying collection of regexes, but in a much cleaner and more
maintainable structure that integrates far better with the Sphinx
build system.

This change has been in linux-next for the full 6.15 cycle; the
small number of problems that turned up have been addressed,
seemingly to everybody's satisfaction. The Perl kernel-doc script
remains in tree (as scripts/kernel-doc.pl) and can be used with a
command-line option if need be. Unless some reason to keep it
around materializes, it will probably go away in 6.17.

Credit goes to Mauro Carvalho Chehab for doing all this work.

- Some RTLA documentation updates

- A handful of Chinese translations

- The usual collection of typo fixes, general updates, etc"

* tag 'docs-6.16' of git://git.lwn.net/linux: (85 commits)
Docs: doc-guide: update sphinx.rst Sphinx version number
docs: doc-guide: clarify latest theme usage
Documentation/scheduler: Fix typo in sched-stats domain field description
scripts: kernel-doc: prevent a KeyError when checking output
docs: kerneldoc.py: simplify exception handling logic
MAINTAINERS: update linux-doc entry to cover new Python scripts
docs: align with scripts/syscall.tbl migration
Documentation: NTB: Fix typo
Documentation: ioctl-number: Update table intro
docs: conf.py: drop backward support for old Sphinx versions
Docs: driver-api/basics: add kobject_event interfaces
Docs: relay: editing cleanups
docs: fix "incase" typo in coresight/panic.rst
Fix spelling error for 'parallel'
docs: admin-guide: fix typos in reporting-issues.rst
docs: dmaengine: add explanation for DMA_ASYNC_TX capability
Documentation: leds: improve readibility of multicolor doc
docs: fix typo in firmware-related section
docs: Makefile: Inherit PYTHONPYCACHEPREFIX setting as env variable
Documentation: ioctl-number: Update outdated submission info
...

+7334 -375
+1
.gitignore
··· 40 40 *.o 41 41 *.o.* 42 42 *.patch 43 + *.pyc 43 44 *.rmeta 44 45 *.rpm 45 46 *.rsi
+2
.pylintrc
··· 1 + [MASTER] 2 + init-hook='import sys; sys.path += ["scripts/lib/kdoc", "scripts/lib/abi"]'
+5 -4
Documentation/Makefile
··· 60 60 # Internal variables. 61 61 PAPEROPT_a4 = -D latex_paper_size=a4 62 62 PAPEROPT_letter = -D latex_paper_size=letter 63 - KERNELDOC = $(srctree)/scripts/kernel-doc 64 - KERNELDOC_CONF = -D kerneldoc_srctree=$(srctree) -D kerneldoc_bin=$(KERNELDOC) 65 - ALLSPHINXOPTS = $(KERNELDOC_CONF) $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) 63 + ALLSPHINXOPTS = -D kerneldoc_srctree=$(srctree) -D kerneldoc_bin=$(KERNELDOC) 64 + ALLSPHINXOPTS += $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) 66 65 ifneq ($(wildcard $(srctree)/.config),) 67 66 ifeq ($(CONFIG_RUST),y) 68 67 # Let Sphinx know we will include rustdoc ··· 82 83 # $5 reST source folder relative to $(src), 83 84 # e.g. "userspace-api/media" for the linux-tv book-set at ./Documentation/userspace-api/media 84 85 86 + PYTHONPYCACHEPREFIX ?= $(abspath $(BUILDDIR)/__pycache__) 87 + 85 88 quiet_cmd_sphinx = SPHINX $@ --> file://$(abspath $(BUILDDIR)/$3/$4) 86 89 cmd_sphinx = $(MAKE) BUILDDIR=$(abspath $(BUILDDIR)) $(build)=Documentation/userspace-api/media $2 && \ 87 - PYTHONDONTWRITEBYTECODE=1 \ 90 + PYTHONPYCACHEPREFIX="$(PYTHONPYCACHEPREFIX)" \ 88 91 BUILDDIR=$(abspath $(BUILDDIR)) SPHINX_CONF=$(abspath $(src)/$5/$(SPHINX_CONF)) \ 89 92 $(PYTHON3) $(srctree)/scripts/jobserver-exec \ 90 93 $(CONFIG_SHELL) $(srctree)/Documentation/sphinx/parallel-wrapper.sh \
+12 -12
Documentation/admin-guide/namespaces/resource-control.rst
··· 1 - =========================== 2 - Namespaces research control 3 - =========================== 1 + ==================================== 2 + User namespaces and resource control 3 + ==================================== 4 4 5 - There are a lot of kinds of objects in the kernel that don't have 6 - individual limits or that have limits that are ineffective when a set 7 - of processes is allowed to switch user ids. With user namespaces 8 - enabled in a kernel for people who don't trust their users or their 9 - users programs to play nice this problems becomes more acute. 5 + The kernel contains many kinds of objects that either don't have 6 + individual limits or that have limits which are ineffective when 7 + a set of processes is allowed to switch their UID. On a system 8 + where the admins don't trust their users or their users' programs, 9 + user namespaces expose the system to potential misuse of resources. 10 10 11 - Therefore it is recommended that memory control groups be enabled in 12 - kernels that enable user namespaces, and it is further recommended 13 - that userspace configure memory control groups to limit how much 14 - memory user's they don't trust to play nice can use. 11 + In order to mitigate this, we recommend that admins enable memory 12 + control groups on any system that enables user namespaces. 13 + Furthermore, we recommend that admins configure the memory control 14 + groups to limit the maximum memory usable by any untrusted user. 15 15 16 16 Memory control groups can be configured by installing the libcgroup 17 17 package present on most distros editing /etc/cgrules.conf,
+4 -4
Documentation/admin-guide/pm/cpufreq.rst
··· 231 231 present). 232 232 233 233 The existence of the limit may be a result of some (often unintentional) 234 - BIOS settings, restrictions coming from a service processor or another 234 + BIOS settings, restrictions coming from a service processor or other 235 235 BIOS/HW-based mechanisms. 236 236 237 237 This does not cover ACPI thermal limitations which can be discovered ··· 258 258 extension on ARM). If one cannot be determined, this attribute should 259 259 not be present. 260 260 261 - Note, that failed attempt to retrieve current frequency for a given 262 - CPU(s) will result in an appropriate error, i.e: EAGAIN for CPU that 261 + Note that failed attempt to retrieve current frequency for a given 262 + CPU(s) will result in an appropriate error, i.e.: EAGAIN for CPU that 263 263 remains idle (raised on ARM). 264 264 265 265 ``cpuinfo_max_freq`` ··· 499 499 represented by it to be 1.5 times as high as the transition latency 500 500 (the default):: 501 501 502 - # echo `$(($(cat cpuinfo_transition_latency) * 3 / 2)) > ondemand/sampling_rate 502 + # echo `$(($(cat cpuinfo_transition_latency) * 3 / 2))` > ondemand/sampling_rate 503 503 504 504 ``up_threshold`` 505 505 If the estimated CPU load is above this value (in percent), the governor
+2 -2
Documentation/admin-guide/quickly-build-trimmed-linux.rst
··· 347 347 348 348 [:ref:`details<uninstall>`] 349 349 350 - .. _submit_improvements: 350 + .. _submit_improvements_qbtl: 351 351 352 352 Did you run into trouble following any of the above steps that is not cleared up 353 353 by the reference section below? Or do you have ideas how to improve the text? ··· 1070 1070 1071 1071 That being said: this of course is a balancing act. Hence, if you think an 1072 1072 additional use-case is worth describing, suggest it to the maintainers of this 1073 - document, as :ref:`described above <submit_improvements>`. 1073 + document, as :ref:`described above <submit_improvements_qbtl>`. 1074 1074 1075 1075 1076 1076 ..
+3 -3
Documentation/admin-guide/reporting-issues.rst
··· 41 41 separately. While writing your report, include all information relevant to the 42 42 issue, like the kernel and the distro used. In case of a regression, CC the 43 43 regressions mailing list (regressions@lists.linux.dev) to your report. Also try 44 - to pin-point the culprit with a bisection; if you succeed, include its 44 + to pinpoint the culprit with a bisection; if you succeed, include its 45 45 commit-id and CC everyone in the sign-off-by chain. 46 46 47 47 Once the report is out, answer any questions that come up and help where you ··· 206 206 This subsection is for you, if you tried the latest mainline kernel as outlined 207 207 above, but failed to reproduce your issue there; at the same time you want to 208 208 see the issue fixed in a still supported stable or longterm series or vendor 209 - kernels regularly rebased on those. If that the case, follow these steps: 209 + kernels regularly rebased on those. If that is the case, follow these steps: 210 210 211 211 * Prepare yourself for the possibility that going through the next few steps 212 212 might not get the issue solved in older releases: the fix might be too big ··· 312 312 example often holds true for the mainline kernels shipped by Debian GNU/Linux 313 313 Sid or Fedora Rawhide. Some developers will also accept reports about issues 314 314 with kernels from distributions shipping the latest stable kernel, as long as 315 - its only slightly modified; that for example is often the case for Arch Linux, 315 + it's only slightly modified; that for example is often the case for Arch Linux, 316 316 regular Fedora releases, and openSUSE Tumbleweed. But keep in mind, you better 317 317 want to use a mainline Linux and avoid using a stable kernel for this 318 318 process, as outlined in the section 'Install a fresh kernel for testing' in more
+2 -2
Documentation/admin-guide/verify-bugs-and-bisect-regressions.rst
··· 267 267 as a regression check out Documentation/admin-guide/reporting-regressions.rst. 268 268 269 269 If you run into any problems while following this guide or have ideas how to 270 - improve it, :ref:`please let the kernel developers know <submit_improvements>`. 270 + improve it, :ref:`please let the kernel developers know <submit_improvements_vbbr>`. 271 271 272 272 .. _introprep_bissbs: 273 273 ··· 1055 1055 1056 1056 [:ref:`details <introoptional_bisref>`] 1057 1057 1058 - .. _submit_improvements: 1058 + .. _submit_improvements_vbbr: 1059 1059 1060 1060 Conclusion 1061 1061 ----------
+1 -1
Documentation/arch/x86/x86_64/fsgs.rst
··· 130 130 131 131 =================== =========================== 132 132 _readfsbase_u64() Read the FS base register 133 - _readfsbase_u64() Read the GS base register 133 + _readgsbase_u64() Read the GS base register 134 134 _writefsbase_u64() Write the FS base register 135 135 _writegsbase_u64() Write the GS base register 136 136 =================== ===========================
+60 -92
Documentation/conf.py
··· 28 28 """ 29 29 return shutil.which(cmd) is not None 30 30 31 - # Get Sphinx version 32 - major, minor, patch = sphinx.version_info[:3] 33 - 34 - # 35 - # Warn about older versions that we don't want to support for much 36 - # longer. 37 - # 38 - if (major < 2) or (major == 2 and minor < 4): 39 - print('WARNING: support for Sphinx < 2.4 will be removed soon.') 40 - 41 31 # If extensions (or modules to document with autodoc) are in another directory, 42 32 # add these directories to sys.path here. If the directory is relative to the 43 33 # documentation root, use os.path.abspath to make it absolute, like shown here. ··· 47 57 'maintainers_include', 'sphinx.ext.autosectionlabel', 48 58 'kernel_abi', 'kernel_feat', 'translations'] 49 59 50 - if major >= 3: 51 - if (major > 3) or (minor > 0 or patch >= 2): 52 - # Sphinx c function parser is more pedantic with regards to type 53 - # checking. Due to that, having macros at c:function cause problems. 54 - # Those needed to be scaped by using c_id_attributes[] array 55 - c_id_attributes = [ 56 - # GCC Compiler types not parsed by Sphinx: 57 - "__restrict__", 60 + # Since Sphinx version 3, the C function parser is more pedantic with regards 61 + # to type checking. Due to that, having macros at c:function cause problems. 62 + # Those needed to be escaped by using c_id_attributes[] array 63 + c_id_attributes = [ 64 + # GCC Compiler types not parsed by Sphinx: 65 + "__restrict__", 58 66 59 - # include/linux/compiler_types.h: 60 - "__iomem", 61 - "__kernel", 62 - "noinstr", 63 - "notrace", 64 - "__percpu", 65 - "__rcu", 66 - "__user", 67 - "__force", 68 - "__counted_by_le", 69 - "__counted_by_be", 67 + # include/linux/compiler_types.h: 68 + "__iomem", 69 + "__kernel", 70 + "noinstr", 71 + "notrace", 72 + "__percpu", 73 + "__rcu", 74 + "__user", 75 + "__force", 76 + "__counted_by_le", 77 + "__counted_by_be", 70 78 71 - # include/linux/compiler_attributes.h: 72 - "__alias", 73 - "__aligned", 74 - "__aligned_largest", 75 - "__always_inline", 76 - "__assume_aligned", 77 - "__cold", 78 - "__attribute_const__", 79 - "__copy", 80 - "__pure", 81 - "__designated_init", 82 - "__visible", 83 - "__printf", 84 - "__scanf", 85 - "__gnu_inline", 86 - "__malloc", 87 - "__mode", 88 - "__no_caller_saved_registers", 89 - "__noclone", 90 - "__nonstring", 91 - "__noreturn", 92 - "__packed", 93 - "__pure", 94 - "__section", 95 - "__always_unused", 96 - "__maybe_unused", 97 - "__used", 98 - "__weak", 99 - "noinline", 100 - "__fix_address", 101 - "__counted_by", 79 + # include/linux/compiler_attributes.h: 80 + "__alias", 81 + "__aligned", 82 + "__aligned_largest", 83 + "__always_inline", 84 + "__assume_aligned", 85 + "__cold", 86 + "__attribute_const__", 87 + "__copy", 88 + "__pure", 89 + "__designated_init", 90 + "__visible", 91 + "__printf", 92 + "__scanf", 93 + "__gnu_inline", 94 + "__malloc", 95 + "__mode", 96 + "__no_caller_saved_registers", 97 + "__noclone", 98 + "__nonstring", 99 + "__noreturn", 100 + "__packed", 101 + "__pure", 102 + "__section", 103 + "__always_unused", 104 + "__maybe_unused", 105 + "__used", 106 + "__weak", 107 + "noinline", 108 + "__fix_address", 109 + "__counted_by", 102 110 103 - # include/linux/memblock.h: 104 - "__init_memblock", 105 - "__meminit", 111 + # include/linux/memblock.h: 112 + "__init_memblock", 113 + "__meminit", 106 114 107 - # include/linux/init.h: 108 - "__init", 109 - "__ref", 115 + # include/linux/init.h: 116 + "__init", 117 + "__ref", 110 118 111 - # include/linux/linkage.h: 112 - "asmlinkage", 119 + # include/linux/linkage.h: 120 + "asmlinkage", 113 121 114 - # include/linux/btf.h 115 - "__bpf_kfunc", 116 - ] 117 - 118 - else: 119 - extensions.append('cdomain') 122 + # include/linux/btf.h 123 + "__bpf_kfunc", 124 + ] 120 125 121 126 # Ensure that autosectionlabel will produce unique names 122 127 autosectionlabel_prefix_document = True ··· 133 148 load_imgmath = False 134 149 else: 135 150 sys.stderr.write("Unknown env SPHINX_IMGMATH=%s ignored.\n" % env_sphinx_imgmath) 136 - 137 - # Always load imgmath for Sphinx <1.8 or for epub docs 138 - load_imgmath = (load_imgmath or (major == 1 and minor < 8) 139 - or 'epub' in sys.argv) 140 151 141 152 if load_imgmath: 142 153 extensions.append("sphinx.ext.imgmath") ··· 303 322 for l in css: 304 323 html_css_files.append(l) 305 324 306 - if major <= 1 and minor < 8: 307 - html_context = { 308 - 'css_files': [], 309 - } 310 - 311 - for l in html_css_files: 312 - html_context['css_files'].append('_static/' + l) 313 - 314 325 if html_theme == 'alabaster': 315 326 html_theme_options = { 316 327 'description': get_cline_version(), ··· 381 408 \\setmonofont{DejaVu Sans Mono} 382 409 ''', 383 410 } 384 - 385 - # Fix reference escape troubles with Sphinx 1.4.x 386 - if major == 1: 387 - latex_elements['preamble'] += '\\renewcommand*{\\DUrole}[2]{ #2 }\n' 388 - 389 411 390 412 # Load kerneldoc specific LaTeX settings 391 413 latex_elements['preamble'] += ''' ··· 508 540 # kernel-doc extension configuration for running Sphinx directly (e.g. by Read 509 541 # the Docs). In a normal build, these are supplied from the Makefile via command 510 542 # line arguments. 511 - kerneldoc_bin = '../scripts/kernel-doc' 543 + kerneldoc_bin = '../scripts/kernel-doc.py' 512 544 kerneldoc_srctree = '..' 513 545 514 546 # ------------------------------------------------------------------------------
+7 -7
Documentation/doc-guide/sphinx.rst
··· 28 28 ============== 29 29 30 30 The ReST markups currently used by the Documentation/ files are meant to be 31 - built with ``Sphinx`` version 2.4.4 or higher. 31 + built with ``Sphinx`` version 3.4.3 or higher. 32 32 33 33 There's a script that checks for the Sphinx requirements. Please see 34 34 :ref:`sphinx-pre-install` for further details. ··· 41 41 with your distributions. In order to do so, it is recommended to install 42 42 Sphinx inside a virtual environment, using ``virtualenv-3`` 43 43 or ``virtualenv``, depending on how your distribution packaged Python 3. 44 - 45 - .. note:: 46 - 47 - #) It is recommended to use the RTD theme for html output. Depending 48 - on the Sphinx version, it should be installed separately, 49 - with ``pip install sphinx_rtd_theme``. 50 44 51 45 In summary, if you want to install the latest version of Sphinx, you 52 46 should do:: ··· 155 161 By default, the "Alabaster" theme is used to build the HTML documentation; 156 162 this theme is bundled with Sphinx and need not be installed separately. 157 163 The Sphinx theme can be overridden by using the ``DOCS_THEME`` make variable. 164 + 165 + .. note:: 166 + 167 + Some people might prefer to use the RTD theme for html output. 168 + Depending on the Sphinx version, it should be installed separately, 169 + with ``pip install sphinx_rtd_theme``. 158 170 159 171 There is another make variable ``SPHINXDIRS``, which is useful when test 160 172 building a subset of documentation. For example, you can build documents
+3
Documentation/driver-api/basics.rst
··· 108 108 .. kernel-doc:: lib/kobject.c 109 109 :export: 110 110 111 + .. kernel-doc:: lib/kobject_uevent.c 112 + :export: 113 + 111 114 Kernel utility functions 112 115 ------------------------ 113 116
+5 -3
Documentation/driver-api/dmaengine/provider.rst
··· 217 217 218 218 - DMA_ASYNC_TX 219 219 220 - - Must not be set by the device, and will be set by the framework 221 - if needed 220 + - The device supports asynchronous memory-to-memory operations, 221 + including memcpy, memset, xor, pq, xor_val, and pq_val. 222 222 223 - - TODO: What is it about? 223 + - This capability is automatically set by the DMA engine 224 + framework and must not be configured manually by device 225 + drivers. 224 226 225 227 - DMA_SLAVE 226 228
+1 -1
Documentation/driver-api/ntb.rst
··· 35 35 NTB Typical client driver implementation 36 36 ---------------------------------------- 37 37 38 - Primary purpose of NTB is to share some peace of memory between at least two 38 + Primary purpose of NTB is to share some piece of memory between at least two 39 39 systems. So the NTB device features like Scratchpad/Message registers are 40 40 mainly used to perform the proper memory window initialization. Typically 41 41 there are two types of memory window interfaces supported by the NTB API:
+1
Documentation/driver-api/usb/usb.rst
··· 161 161 .. kernel-doc:: drivers/usb/core/urb.c 162 162 :export: 163 163 164 + .. c:namespace:: usb_core 164 165 .. kernel-doc:: drivers/usb/core/message.c 165 166 :export: 166 167
+13 -13
Documentation/filesystems/relay.rst
··· 32 32 Semantics 33 33 ========= 34 34 35 - Each relay channel has one buffer per CPU, each buffer has one or more 35 + Each relay channel has one buffer per CPU; each buffer has one or more 36 36 sub-buffers. Messages are written to the first sub-buffer until it is 37 37 too full to contain a new message, in which case it is written to 38 38 the next (if available). Messages are never split across sub-buffers. ··· 40 40 sub-buffer, while the kernel continues writing to the next. 41 41 42 42 When notified that a sub-buffer is full, the kernel knows how many 43 - bytes of it are padding i.e. unused space occurring because a complete 43 + bytes of it are padding, i.e., unused space occurring because a complete 44 44 message couldn't fit into a sub-buffer. Userspace can use this 45 45 knowledge to copy only valid data. 46 46 ··· 71 71 ================================ 72 72 73 73 The relay interface itself is ready to use, but to make things easier, 74 - a couple simple utility functions and a set of examples are provided. 74 + a couple of simple utility functions and a set of examples are provided. 75 75 76 76 The relay-apps example tarball, available on the relay sourceforge 77 77 site, contains a set of self-contained examples, each consisting of a ··· 91 91 examples for details). 92 92 93 93 It is of course possible to use the relay interface from scratch, 94 - i.e. without using any of the relay-apps example code or klog, but 94 + i.e., without using any of the relay-apps example code or klog, but 95 95 you'll have to implement communication between userspace and kernel, 96 96 allowing both to convey the state of buffers (full, empty, amount of 97 97 padding). The read() interface both removes padding and internally ··· 119 119 must map the entire file, which is NRBUF * SUBBUFSIZE. 120 120 121 121 read() read the contents of a channel buffer. The bytes read are 122 - 'consumed' by the reader, i.e. they won't be available 122 + 'consumed' by the reader, i.e., they won't be available 123 123 again to subsequent reads. If the channel is being used 124 124 in no-overwrite mode (the default), it can be read at any 125 125 time even if there's an active kernel writer. If the ··· 138 138 notified when sub-buffer boundaries are crossed. 139 139 140 140 close() decrements the channel buffer's refcount. When the refcount 141 - reaches 0, i.e. when no process or kernel client has the 141 + reaches 0, i.e., when no process or kernel client has the 142 142 buffer open, the channel buffer is freed. 143 143 =========== ============================================================ 144 144 ··· 149 149 150 150 .. Note:: 151 151 152 - the host filesystem doesn't need to be mounted for kernel 152 + The host filesystem doesn't need to be mounted for kernel 153 153 clients to create or use channels - it only needs to be 154 154 mounted when user space applications need access to the buffer 155 155 data. ··· 325 325 In 'overwrite' mode, also known as 'flight recorder' mode, writes 326 326 continuously cycle around the buffer and will never fail, but will 327 327 unconditionally overwrite old data regardless of whether it's actually 328 - been consumed. In no-overwrite mode, writes will fail, i.e. data will 328 + been consumed. In no-overwrite mode, writes will fail, i.e., data will 329 329 be lost, if the number of unconsumed sub-buffers equals the total 330 330 number of sub-buffers in the channel. It should be clear that if 331 331 there is no consumer or if the consumer can't consume sub-buffers fast ··· 344 344 sub-buffer if appropriate and 3) return a boolean value indicating 345 345 whether or not to actually move on to the next sub-buffer. 346 346 347 - To implement 'no-overwrite' mode, the userspace client would provide 347 + To implement 'no-overwrite' mode, the userspace client provides 348 348 an implementation of the subbuf_start() callback something like the 349 349 following:: 350 350 ··· 364 364 return 1; 365 365 } 366 366 367 - If the current buffer is full, i.e. all sub-buffers remain unconsumed, 367 + If the current buffer is full, i.e., all sub-buffers remain unconsumed, 368 368 the callback returns 0 to indicate that the buffer switch should not 369 - occur yet, i.e. until the consumer has had a chance to read the 369 + occur yet, i.e., until the consumer has had a chance to read the 370 370 current set of ready sub-buffers. For the relay_buf_full() function 371 371 to make sense, the consumer is responsible for notifying the relay 372 372 interface when sub-buffers have been consumed via ··· 400 400 401 401 The default subbuf_start() implementation, used if the client doesn't 402 402 define any callbacks, or doesn't define the subbuf_start() callback, 403 - implements the simplest possible 'no-overwrite' mode, i.e. it does 403 + implements the simplest possible 'no-overwrite' mode, i.e., it does 404 404 nothing but return 0. 405 405 406 406 Header information can be reserved at the beginning of each sub-buffer ··· 467 467 can be used for this purpose - it resets a channel to its initial 468 468 state without reallocating channel buffer memory or destroying 469 469 existing mappings. It should however only be called when it's safe to 470 - do so, i.e. when the channel isn't currently being written to. 470 + do so, i.e., when the channel isn't currently being written to. 471 471 472 472 Finally, there are a couple of utility callbacks that can be used for 473 473 different purposes. buf_mapped() is called whenever a channel buffer
+1 -1
Documentation/gpu/rfc/i915_scheduler.rst
··· 26 26 which configures a slot with N contexts 27 27 * After I915_CONTEXT_ENGINES_EXT_PARALLEL a user can submit N batches to 28 28 a slot in a single execbuf IOCTL and the batches run on the GPU in 29 - paralllel 29 + parallel 30 30 * Initially only for GuC submission but execlists can be supported if 31 31 needed 32 32 * Convert the i915 to use the DRM scheduler
+4 -4
Documentation/hid/intel-thc-hid.rst
··· 182 182 183 183 THC also includes two GPIO pins, one for interrupt and the other for device reset control. 184 184 185 - Interrupt line can be configured to either level triggerred or edge triggerred by setting MMIO 185 + Interrupt line can be configured to either level triggered or edge triggered by setting MMIO 186 186 Control register. 187 187 188 188 Reset line is controlled by BIOS (or EFI) through ACPI _RST method, driver needs to call this ··· 302 302 3.3.2 Software DMA channel 303 303 ~~~~~~~~~~~~~~~~~~~~~~~~~~ 304 304 305 - THC supports a software triggerred RxDMA mode to read the touch data from touch IC. This SW RxDMA 305 + THC supports a software triggered RxDMA mode to read the touch data from touch IC. This SW RxDMA 306 306 is the 3rd THC RxDMA engine with the similar functionalities as the existing two RxDMAs, the only 307 - difference is this SW RxDMA is triggerred by software, and RxDMA2 is triggerred by external Touch IC 308 - interrupt. It gives a flexiblity to software driver to use RxDMA read Touch IC data in any time. 307 + difference is this SW RxDMA is triggered by software, and RxDMA2 is triggered by external Touch IC 308 + interrupt. It gives a flexibility to software driver to use RxDMA read Touch IC data in any time. 309 309 310 310 Before software starts a SW RxDMA, it shall stop the 1st and 2nd RxDMA, clear PRD read/write pointer 311 311 and quiesce the device interrupt (THC_DEVINT_QUIESCE_HW_STS = 1), other operations are the same with
+1 -1
Documentation/index.rst
··· 84 84 Firmware-related documentation 85 85 ============================== 86 86 The following holds information on the kernel's expectations regarding the 87 - platform firmwares. 87 + platform firmware. 88 88 89 89 .. toctree:: 90 90 :maxdepth: 1
+51 -31
Documentation/leds/leds-class-multicolor.rst
··· 18 18 led_class framework. The led_class framework is documented in led-class.rst 19 19 within this documentation directory. 20 20 21 - Each colored LED will be indexed under the multi_* files. The order of the 22 - colors will be arbitrary. The multi_index file can be read to determine the 21 + Each colored LED will be indexed under the ``multi_*`` files. The order of the 22 + colors will be arbitrary. The ``multi_index`` file can be read to determine the 23 23 color name to indexed value. 24 24 25 - The multi_index file is an array that contains the string list of the colors as 26 - they are defined in each multi_* array file. 25 + The ``multi_index`` file is an array that contains the string list of the colors as 26 + they are defined in each ``multi_*`` array file. 27 27 28 - The multi_intensity is an array that can be read or written to for the 28 + The ``multi_intensity`` is an array that can be read or written to for the 29 29 individual color intensities. All elements within this array must be written in 30 30 order for the color LED intensities to be updated. 31 31 32 32 Directory Layout Example 33 33 ======================== 34 - root:/sys/class/leds/multicolor:status# ls -lR 35 - -rw-r--r-- 1 root root 4096 Oct 19 16:16 brightness 36 - -r--r--r-- 1 root root 4096 Oct 19 16:16 max_brightness 37 - -r--r--r-- 1 root root 4096 Oct 19 16:16 multi_index 38 - -rw-r--r-- 1 root root 4096 Oct 19 16:16 multi_intensity 34 + .. code-block:: console 35 + 36 + root:/sys/class/leds/multicolor:status# ls -lR 37 + -rw-r--r-- 1 root root 4096 Oct 19 16:16 brightness 38 + -r--r--r-- 1 root root 4096 Oct 19 16:16 max_brightness 39 + -r--r--r-- 1 root root 4096 Oct 19 16:16 multi_index 40 + -rw-r--r-- 1 root root 4096 Oct 19 16:16 multi_intensity 41 + 42 + .. 39 43 40 44 Multicolor Class Brightness Control 41 45 =================================== ··· 47 43 intensity setting divided by the global max_brightness setting multiplied by 48 44 the requested brightness. 49 45 50 - led_brightness = brightness * multi_intensity/max_brightness 46 + ``led_brightness = brightness * multi_intensity/max_brightness`` 51 47 52 48 Example: 53 49 A user first writes the multi_intensity file with the brightness levels 54 50 for each LED that are necessary to achieve a certain color output from a 55 51 multicolor LED group. 56 52 57 - cat /sys/class/leds/multicolor:status/multi_index 58 - green blue red 53 + .. code-block:: console 59 54 60 - echo 43 226 138 > /sys/class/leds/multicolor:status/multi_intensity 55 + # cat /sys/class/leds/multicolor:status/multi_index 56 + green blue red 61 57 62 - red - 63 - intensity = 138 64 - max_brightness = 255 65 - green - 66 - intensity = 43 67 - max_brightness = 255 68 - blue - 69 - intensity = 226 70 - max_brightness = 255 58 + # echo 43 226 138 > /sys/class/leds/multicolor:status/multi_intensity 59 + 60 + red - 61 + intensity = 138 62 + max_brightness = 255 63 + green - 64 + intensity = 43 65 + max_brightness = 255 66 + blue - 67 + intensity = 226 68 + max_brightness = 255 69 + 70 + .. 71 71 72 72 The user can control the brightness of that multicolor LED group by writing the 73 73 global 'brightness' control. Assuming a max_brightness of 255 the user ··· 79 71 128 to the global brightness file then the values written to each LED will be 80 72 adjusted base on this value. 81 73 82 - cat /sys/class/leds/multicolor:status/max_brightness 83 - 255 84 - echo 128 > /sys/class/leds/multicolor:status/brightness 74 + .. code-block:: console 85 75 86 - adjusted_red_value = 128 * 138/255 = 69 87 - adjusted_green_value = 128 * 43/255 = 21 88 - adjusted_blue_value = 128 * 226/255 = 113 76 + # cat /sys/class/leds/multicolor:status/max_brightness 77 + 255 78 + # echo 128 > /sys/class/leds/multicolor:status/brightness 79 + 80 + .. 81 + 82 + .. code-block:: none 83 + 84 + adjusted_red_value = 128 * 138/255 = 69 85 + adjusted_green_value = 128 * 43/255 = 21 86 + adjusted_blue_value = 128 * 226/255 = 113 87 + 88 + .. 89 89 90 90 Reading the global brightness file will return the current brightness value of 91 91 the color LED group. 92 92 93 - cat /sys/class/leds/multicolor:status/brightness 94 - 128 93 + .. code-block:: console 94 + 95 + # cat /sys/class/leds/multicolor:status/brightness 96 + 128 97 + 98 + ..
+6 -6
Documentation/process/1.Intro.rst
··· 251 251 foreseeable future. 252 252 253 253 It is imperative that all code contributed to the kernel be legitimately 254 - free software. For that reason, code from anonymous (or pseudonymous) 255 - contributors will not be accepted. All contributors are required to "sign 256 - off" on their code, stating that the code can be distributed with the 257 - kernel under the GPL. Code which has not been licensed as free software by 258 - its owner, or which risks creating copyright-related problems for the 259 - kernel (such as code which derives from reverse-engineering efforts lacking 254 + free software. For that reason, code from contributors without a known 255 + identity or anonymous contributors will not be accepted. All contributors are 256 + required to "sign off" on their code, stating that the code can be distributed 257 + with the kernel under the GPL. Code which has not been licensed as free 258 + software by its owner, or which risks creating copyright-related problems for 259 + the kernel (such as code which derives from reverse-engineering efforts lacking 260 260 proper safeguards) cannot be contributed. 261 261 262 262 Questions about copyright-related issues are common on Linux development
+84
Documentation/process/adding-syscalls.rst
··· 248 248 - fallback stub in ``kernel/sys_ni.c`` 249 249 250 250 251 + .. _syscall_generic_6_11: 252 + 253 + Since 6.11 254 + ~~~~~~~~~~ 255 + 256 + Starting with kernel version 6.11, general system call implementation for the 257 + following architectures no longer requires modifications to 258 + ``include/uapi/asm-generic/unistd.h``: 259 + 260 + - arc 261 + - arm64 262 + - csky 263 + - hexagon 264 + - loongarch 265 + - nios2 266 + - openrisc 267 + - riscv 268 + 269 + Instead, you need to update ``scripts/syscall.tbl`` and, if applicable, adjust 270 + ``arch/*/kernel/Makefile.syscalls``. 271 + 272 + As ``scripts/syscall.tbl`` serves as a common syscall table across multiple 273 + architectures, a new entry is required in this table:: 274 + 275 + 468 common xyzzy sys_xyzzy 276 + 277 + Note that adding an entry to ``scripts/syscall.tbl`` with the "common" ABI 278 + also affects all architectures that share this table. For more limited or 279 + architecture-specific changes, consider using an architecture-specific ABI or 280 + defining a new one. 281 + 282 + If a new ABI, say ``xyz``, is introduced, the corresponding updates should be 283 + made to ``arch/*/kernel/Makefile.syscalls`` as well:: 284 + 285 + syscall_abis_{32,64} += xyz (...) 286 + 287 + To summarize, you need a commit that includes: 288 + 289 + - ``CONFIG`` option for the new function, normally in ``init/Kconfig`` 290 + - ``SYSCALL_DEFINEn(xyzzy, ...)`` for the entry point 291 + - corresponding prototype in ``include/linux/syscalls.h`` 292 + - new entry in ``scripts/syscall.tbl`` 293 + - (if needed) Makefile updates in ``arch/*/kernel/Makefile.syscalls`` 294 + - fallback stub in ``kernel/sys_ni.c`` 295 + 296 + 251 297 x86 System Call Implementation 252 298 ------------------------------ 253 299 ··· 397 351 - (if needed) 32-bit mapping struct in ``include/linux/compat.h`` 398 352 - instance of ``__SC_COMP`` not ``__SYSCALL`` in 399 353 ``include/uapi/asm-generic/unistd.h`` 354 + 355 + 356 + Since 6.11 357 + ~~~~~~~~~~ 358 + 359 + This applies to all the architectures listed in :ref:`Since 6.11<syscall_generic_6_11>` 360 + under "Generic System Call Implementation", except arm64. See 361 + :ref:`Compatibility System Calls (arm64)<compat_arm64>` for more information. 362 + 363 + You need to extend the entry in ``scripts/syscall.tbl`` with an extra column 364 + to indicate that a 32-bit userspace program running on a 64-bit kernel should 365 + hit the compat entry point:: 366 + 367 + 468 common xyzzy sys_xyzzy compat_sys_xyzzy 368 + 369 + To summarize, you need: 370 + 371 + - ``COMPAT_SYSCALL_DEFINEn(xyzzy, ...)`` for the compat entry point 372 + - corresponding prototype in ``include/linux/compat.h`` 373 + - modification of the entry in ``scripts/syscall.tbl`` to include an extra 374 + "compat" column 375 + - (if needed) 32-bit mapping struct in ``include/linux/compat.h`` 376 + 377 + 378 + .. _compat_arm64: 379 + 380 + Compatibility System Calls (arm64) 381 + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 382 + 383 + On arm64, there is a dedicated syscall table for compatibility system calls 384 + targeting 32-bit (AArch32) userspace: ``arch/arm64/tools/syscall_32.tbl``. 385 + You need to add an additional line to this table specifying the compat 386 + entry point:: 387 + 388 + 468 common xyzzy sys_xyzzy compat_sys_xyzzy 400 389 401 390 402 391 Compatibility System Calls (x86) ··· 656 575 - Recommendation from Linus Torvalds that x32 system calls should prefer 657 576 compatibility with 64-bit versions rather than 32-bit versions: 658 577 https://lore.kernel.org/r/CA+55aFxfmwfB7jbbrXxa=K7VBYPfAvmu3XOkGrLbB1UFjX1+Ew@mail.gmail.com 578 + - Patch series revising system call table infrastructure to use 579 + scripts/syscall.tbl across multiple architectures: 580 + https://lore.kernel.org/lkml/20240704143611.2979589-1-arnd@kernel.org
+1 -1
Documentation/scheduler/sched-stats.rst
··· 135 135 cpu was idle but no busier group was found 136 136 137 137 23) # of times in this domain sched_balance_rq() was called when the 138 - was just becoming idle 138 + cpu was just becoming idle 139 139 24) # of times in this domain sched_balance_rq() checked but found the 140 140 load did not require balancing when the cpu was just becoming idle 141 141 25) # of times in this domain sched_balance_rq() tried to move one or more
+24 -73
Documentation/sphinx/automarkup.py
··· 128 128 # own C role, but both match the same regex, so we try both. 129 129 # 130 130 def markup_func_ref_sphinx3(docname, app, match): 131 - cdom = app.env.domains['c'] 132 - # 133 - # Go through the dance of getting an xref out of the C domain 134 - # 135 131 base_target = match.group(2) 136 132 target_text = nodes.Text(match.group(0)) 137 - xref = None 138 133 possible_targets = [base_target] 139 134 # Check if this document has a namespace, and if so, try 140 135 # cross-referencing inside it first. ··· 141 146 if (target not in Skipfuncs) and not failure_seen(target): 142 147 lit_text = nodes.literal(classes=['xref', 'c', 'c-func']) 143 148 lit_text += target_text 144 - pxref = addnodes.pending_xref('', refdomain = 'c', 145 - reftype = 'function', 146 - reftarget = target, 147 - modname = None, 148 - classname = None) 149 - # 150 - # XXX The Latex builder will throw NoUri exceptions here, 151 - # work around that by ignoring them. 152 - # 153 - try: 154 - xref = cdom.resolve_xref(app.env, docname, app.builder, 155 - 'function', target, pxref, 156 - lit_text) 157 - except NoUri: 158 - xref = None 159 - 149 + xref = add_and_resolve_xref(app, docname, 'c', 'function', 150 + target, contnode=lit_text) 160 151 if xref: 161 152 return xref 162 153 note_failure(target) ··· 169 188 RE_typedef: 'type', 170 189 } 171 190 172 - cdom = app.env.domains['c'] 173 - # 174 - # Go through the dance of getting an xref out of the C domain 175 - # 176 191 base_target = match.group(2) 177 192 target_text = nodes.Text(match.group(0)) 178 - xref = None 179 193 possible_targets = [base_target] 180 194 # Check if this document has a namespace, and if so, try 181 195 # cross-referencing inside it first. ··· 182 206 if not (match.re == RE_function and target in Skipfuncs): 183 207 lit_text = nodes.literal(classes=['xref', 'c', class_str[match.re]]) 184 208 lit_text += target_text 185 - pxref = addnodes.pending_xref('', refdomain = 'c', 186 - reftype = reftype_str[match.re], 187 - reftarget = target, modname = None, 188 - classname = None) 189 - # 190 - # XXX The Latex builder will throw NoUri exceptions here, 191 - # work around that by ignoring them. 192 - # 193 - try: 194 - xref = cdom.resolve_xref(app.env, docname, app.builder, 195 - reftype_str[match.re], target, pxref, 196 - lit_text) 197 - except NoUri: 198 - xref = None 199 - 209 + xref = add_and_resolve_xref(app, docname, 'c', 210 + reftype_str[match.re], target, 211 + contnode=lit_text) 200 212 if xref: 201 213 return xref 202 214 ··· 195 231 # cross reference to that page 196 232 # 197 233 def markup_doc_ref(docname, app, match): 198 - stddom = app.env.domains['std'] 199 - # 200 - # Go through the dance of getting an xref out of the std domain 201 - # 202 234 absolute = match.group(1) 203 235 target = match.group(2) 204 236 if absolute: 205 237 target = "/" + target 206 - xref = None 207 - pxref = addnodes.pending_xref('', refdomain = 'std', reftype = 'doc', 208 - reftarget = target, modname = None, 209 - classname = None, refexplicit = False) 210 - # 211 - # XXX The Latex builder will throw NoUri exceptions here, 212 - # work around that by ignoring them. 213 - # 214 - try: 215 - xref = stddom.resolve_xref(app.env, docname, app.builder, 'doc', 216 - target, pxref, None) 217 - except NoUri: 218 - xref = None 219 - # 220 - # Return the xref if we got it; otherwise just return the plain text. 221 - # 238 + 239 + xref = add_and_resolve_xref(app, docname, 'std', 'doc', target) 222 240 if xref: 223 241 return xref 224 242 else: ··· 211 265 # with a cross reference to that page 212 266 # 213 267 def markup_abi_ref(docname, app, match, warning=False): 214 - stddom = app.env.domains['std'] 215 - # 216 - # Go through the dance of getting an xref out of the std domain 217 - # 218 268 kernel_abi = get_kernel_abi() 219 269 220 270 fname = match.group(1) ··· 222 280 kernel_abi.log.warning("%s not found", fname) 223 281 return nodes.Text(match.group(0)) 224 282 225 - pxref = addnodes.pending_xref('', refdomain = 'std', reftype = 'ref', 283 + xref = add_and_resolve_xref(app, docname, 'std', 'ref', target) 284 + if xref: 285 + return xref 286 + else: 287 + return nodes.Text(match.group(0)) 288 + 289 + def add_and_resolve_xref(app, docname, domain, reftype, target, contnode=None): 290 + # 291 + # Go through the dance of getting an xref out of the corresponding domain 292 + # 293 + dom_obj = app.env.domains[domain] 294 + pxref = addnodes.pending_xref('', refdomain = domain, reftype = reftype, 226 295 reftarget = target, modname = None, 227 296 classname = None, refexplicit = False) 228 297 ··· 242 289 # work around that by ignoring them. 243 290 # 244 291 try: 245 - xref = stddom.resolve_xref(app.env, docname, app.builder, 'ref', 246 - target, pxref, None) 292 + xref = dom_obj.resolve_xref(app.env, docname, app.builder, reftype, 293 + target, pxref, contnode) 247 294 except NoUri: 248 295 xref = None 249 - # 250 - # Return the xref if we got it; otherwise just return the plain text. 251 - # 296 + 252 297 if xref: 253 298 return xref 254 - else: 255 - return nodes.Text(match.group(0)) 299 + 300 + return None 256 301 257 302 # 258 303 # Variant of markup_abi_ref() that warns whan a reference is not found
+191 -42
Documentation/sphinx/kerneldoc.py
··· 40 40 import sphinx 41 41 from sphinx.util.docutils import switch_source_input 42 42 from sphinx.util import logging 43 + from pprint import pformat 44 + 45 + srctree = os.path.abspath(os.environ["srctree"]) 46 + sys.path.insert(0, os.path.join(srctree, "scripts/lib/kdoc")) 47 + 48 + from kdoc_files import KernelFiles 49 + from kdoc_output import RestFormat 43 50 44 51 __version__ = '1.0' 52 + kfiles = None 53 + logger = logging.getLogger(__name__) 54 + 55 + def cmd_str(cmd): 56 + """ 57 + Helper function to output a command line that can be used to produce 58 + the same records via command line. Helpful to debug troubles at the 59 + script. 60 + """ 61 + 62 + cmd_line = "" 63 + 64 + for w in cmd: 65 + if w == "" or " " in w: 66 + esc_cmd = "'" + w + "'" 67 + else: 68 + esc_cmd = w 69 + 70 + if cmd_line: 71 + cmd_line += " " + esc_cmd 72 + continue 73 + else: 74 + cmd_line = esc_cmd 75 + 76 + return cmd_line 45 77 46 78 class KernelDocDirective(Directive): 47 79 """Extract kernel-doc comments from the specified file""" ··· 88 56 'functions': directives.unchanged, 89 57 } 90 58 has_content = False 91 - logger = logging.getLogger('kerneldoc') 59 + verbose = 0 92 60 93 - def run(self): 61 + parse_args = {} 62 + msg_args = {} 63 + 64 + def handle_args(self): 65 + 94 66 env = self.state.document.settings.env 95 67 cmd = [env.config.kerneldoc_bin, '-rst', '-enable-lineno'] 96 68 97 69 filename = env.config.kerneldoc_srctree + '/' + self.arguments[0] 70 + 71 + # Arguments used by KernelFiles.parse() function 72 + self.parse_args = { 73 + "file_list": [filename], 74 + "export_file": [] 75 + } 76 + 77 + # Arguments used by KernelFiles.msg() function 78 + self.msg_args = { 79 + "enable_lineno": True, 80 + "export": False, 81 + "internal": False, 82 + "symbol": [], 83 + "nosymbol": [], 84 + "no_doc_sections": False 85 + } 86 + 98 87 export_file_patterns = [] 88 + 89 + verbose = os.environ.get("V") 90 + if verbose: 91 + try: 92 + self.verbose = int(verbose) 93 + except ValueError: 94 + pass 99 95 100 96 # Tell sphinx of the dependency 101 97 env.note_dependency(os.path.abspath(filename)) 102 98 103 - tab_width = self.options.get('tab-width', self.state.document.settings.tab_width) 99 + self.tab_width = self.options.get('tab-width', 100 + self.state.document.settings.tab_width) 104 101 105 102 # 'function' is an alias of 'identifiers' 106 103 if 'functions' in self.options: ··· 138 77 # FIXME: make this nicer and more robust against errors 139 78 if 'export' in self.options: 140 79 cmd += ['-export'] 80 + self.msg_args["export"] = True 141 81 export_file_patterns = str(self.options.get('export')).split() 142 82 elif 'internal' in self.options: 143 83 cmd += ['-internal'] 84 + self.msg_args["internal"] = True 144 85 export_file_patterns = str(self.options.get('internal')).split() 145 86 elif 'doc' in self.options: 146 - cmd += ['-function', str(self.options.get('doc'))] 87 + func = str(self.options.get('doc')) 88 + cmd += ['-function', func] 89 + self.msg_args["symbol"].append(func) 147 90 elif 'identifiers' in self.options: 148 91 identifiers = self.options.get('identifiers').split() 149 92 if identifiers: 150 93 for i in identifiers: 94 + i = i.rstrip("\\").strip() 95 + if not i: 96 + continue 97 + 151 98 cmd += ['-function', i] 99 + self.msg_args["symbol"].append(i) 152 100 else: 153 101 cmd += ['-no-doc-sections'] 102 + self.msg_args["no_doc_sections"] = True 154 103 155 104 if 'no-identifiers' in self.options: 156 105 no_identifiers = self.options.get('no-identifiers').split() 157 106 if no_identifiers: 158 107 for i in no_identifiers: 108 + i = i.rstrip("\\").strip() 109 + if not i: 110 + continue 111 + 159 112 cmd += ['-nosymbol', i] 113 + self.msg_args["nosymbol"].append(i) 160 114 161 115 for pattern in export_file_patterns: 116 + pattern = pattern.rstrip("\\").strip() 117 + if not pattern: 118 + continue 119 + 162 120 for f in glob.glob(env.config.kerneldoc_srctree + '/' + pattern): 163 121 env.note_dependency(os.path.abspath(f)) 164 122 cmd += ['-export-file', f] 123 + self.parse_args["export_file"].append(f) 124 + 125 + # Export file is needed by both parse and msg, as kernel-doc 126 + # cache exports. 127 + self.msg_args["export_file"] = self.parse_args["export_file"] 165 128 166 129 cmd += [filename] 167 130 131 + return cmd 132 + 133 + def run_cmd(self, cmd): 134 + """ 135 + Execute an external kernel-doc command. 136 + """ 137 + 138 + env = self.state.document.settings.env 139 + node = nodes.section() 140 + 141 + p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 142 + out, err = p.communicate() 143 + 144 + out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8') 145 + 146 + if p.returncode != 0: 147 + sys.stderr.write(err) 148 + 149 + logger.warning("kernel-doc '%s' failed with return code %d" 150 + % (" ".join(cmd), p.returncode)) 151 + return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))] 152 + elif env.config.kerneldoc_verbosity > 0: 153 + sys.stderr.write(err) 154 + 155 + filenames = self.parse_args["file_list"] 156 + for filename in filenames: 157 + self.parse_msg(filename, node, out, cmd) 158 + 159 + return node.children 160 + 161 + def parse_msg(self, filename, node, out, cmd): 162 + """ 163 + Handles a kernel-doc output for a given file 164 + """ 165 + 166 + env = self.state.document.settings.env 167 + 168 + lines = statemachine.string2lines(out, self.tab_width, 169 + convert_whitespace=True) 170 + result = ViewList() 171 + 172 + lineoffset = 0; 173 + line_regex = re.compile(r"^\.\. LINENO ([0-9]+)$") 174 + for line in lines: 175 + match = line_regex.search(line) 176 + if match: 177 + # sphinx counts lines from 0 178 + lineoffset = int(match.group(1)) - 1 179 + # we must eat our comments since the upset the markup 180 + else: 181 + doc = str(env.srcdir) + "/" + env.docname + ":" + str(self.lineno) 182 + result.append(line, doc + ": " + filename, lineoffset) 183 + lineoffset += 1 184 + 185 + self.do_parse(result, node) 186 + 187 + def run_kdoc(self, cmd, kfiles): 188 + """ 189 + Execute kernel-doc classes directly instead of running as a separate 190 + command. 191 + """ 192 + 193 + env = self.state.document.settings.env 194 + 195 + node = nodes.section() 196 + 197 + kfiles.parse(**self.parse_args) 198 + filenames = self.parse_args["file_list"] 199 + 200 + for filename, out in kfiles.msg(**self.msg_args, filenames=filenames): 201 + self.parse_msg(filename, node, out, cmd) 202 + 203 + return node.children 204 + 205 + def run(self): 206 + global kfiles 207 + 208 + cmd = self.handle_args() 209 + if self.verbose >= 1: 210 + logger.info(cmd_str(cmd)) 211 + 168 212 try: 169 - self.logger.verbose("calling kernel-doc '%s'" % (" ".join(cmd))) 170 - 171 - p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 172 - out, err = p.communicate() 173 - 174 - out, err = codecs.decode(out, 'utf-8'), codecs.decode(err, 'utf-8') 175 - 176 - if p.returncode != 0: 177 - sys.stderr.write(err) 178 - 179 - self.logger.warning("kernel-doc '%s' failed with return code %d" 180 - % (" ".join(cmd), p.returncode)) 181 - return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))] 182 - elif env.config.kerneldoc_verbosity > 0: 183 - sys.stderr.write(err) 184 - 185 - lines = statemachine.string2lines(out, tab_width, convert_whitespace=True) 186 - result = ViewList() 187 - 188 - lineoffset = 0; 189 - line_regex = re.compile(r"^\.\. LINENO ([0-9]+)$") 190 - for line in lines: 191 - match = line_regex.search(line) 192 - if match: 193 - # sphinx counts lines from 0 194 - lineoffset = int(match.group(1)) - 1 195 - # we must eat our comments since the upset the markup 196 - else: 197 - doc = str(env.srcdir) + "/" + env.docname + ":" + str(self.lineno) 198 - result.append(line, doc + ": " + filename, lineoffset) 199 - lineoffset += 1 200 - 201 - node = nodes.section() 202 - self.do_parse(result, node) 203 - 204 - return node.children 213 + if kfiles: 214 + return self.run_kdoc(cmd, kfiles) 215 + else: 216 + return self.run_cmd(cmd) 205 217 206 218 except Exception as e: # pylint: disable=W0703 207 - self.logger.warning("kernel-doc '%s' processing failed with: %s" % 208 - (" ".join(cmd), str(e))) 219 + logger.warning("kernel-doc '%s' processing failed with: %s" % 220 + (cmd_str(cmd), pformat(e))) 209 221 return [nodes.error(None, nodes.paragraph(text = "kernel-doc missing"))] 210 222 211 223 def do_parse(self, result, node): 212 224 with switch_source_input(self.state, result): 213 225 self.state.nested_parse(result, 0, node, match_titles=1) 226 + 227 + def setup_kfiles(app): 228 + global kfiles 229 + 230 + kerneldoc_bin = app.env.config.kerneldoc_bin 231 + 232 + if kerneldoc_bin and kerneldoc_bin.endswith("kernel-doc.py"): 233 + print("Using Python kernel-doc") 234 + out_style = RestFormat() 235 + kfiles = KernelFiles(out_style=out_style, logger=logger) 236 + else: 237 + print(f"Using {kerneldoc_bin}") 238 + 214 239 215 240 def setup(app): 216 241 app.add_config_value('kerneldoc_bin', None, 'env') ··· 304 157 app.add_config_value('kerneldoc_verbosity', 1, 'env') 305 158 306 159 app.add_directive('kernel-doc', KernelDocDirective) 160 + 161 + app.connect('builder-inited', setup_kfiles) 307 162 308 163 return dict( 309 164 version = __version__,
-1
Documentation/staging/speculation.rst
··· 63 63 microarchitectural state dependent on this value. This may provide an 64 64 arbitrary read primitive. 65 65 66 - ==================================== 67 66 Mitigating speculation side-channels 68 67 ==================================== 69 68
+9 -1
Documentation/tools/rtla/common_timerlat_description.rst
··· 6 6 7 7 The *timerlat* tracer outputs information in two ways. It periodically 8 8 prints the timer latency at the timer *IRQ* handler and the *Thread* 9 - handler. It also enable the trace of the most relevant information via 9 + handler. It also enables the trace of the most relevant information via 10 10 **osnoise:** tracepoints. 11 + 12 + The **rtla timerlat** tool sets the options of the *timerlat* tracer 13 + and collects and displays a summary of the results. By default, 14 + the collection is done synchronously in kernel space using a dedicated 15 + BPF program attached to the *timerlat* tracer. If either BPF or 16 + the **osnoise:timerlat_sample** tracepoint it attaches to is 17 + unavailable, the **rtla timerlat** tool falls back to using tracefs to 18 + process the data asynchronously in user space.
+3 -6
Documentation/tools/rtla/rtla-timerlat.rst
··· 16 16 17 17 .. include:: common_timerlat_description.rst 18 18 19 - The *timerlat* tracer outputs information in two ways. It periodically 20 - prints the timer latency at the timer *IRQ* handler and the *Thread* handler. 21 - It also provides information for each noise via the **osnoise:** tracepoints. 22 19 The **rtla timerlat top** mode displays a summary of the periodic output 23 - from the *timerlat* tracer. The **rtla hist hist** mode displays a histogram 24 - of each tracer event occurrence. For further details, please refer to the 25 - respective man page. 20 + from the *timerlat* tracer. The **rtla timerlat hist** mode displays 21 + a histogram of each tracer event occurrence. For further details, please 22 + refer to the respective man page. 26 23 27 24 MODES 28 25 =====
+2 -2
Documentation/trace/coresight/panic.rst
··· 67 67 or from crashdump kernel using a special device file /dev/crash_tmc_xxx. 68 68 This device file is created only when there is a valid crashdata available. 69 69 70 - General flow of trace capture and decode incase of kernel panic 71 - ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 70 + General flow of trace capture and decode in case of kernel panic 71 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 72 72 1. Enable source and sink on all the cores using the sysfs interface. 73 73 ETR sinks should have trace buffers allocated from reserved memory, 74 74 by selecting "resrv" buffer mode from sysfs.
+81 -17
Documentation/trace/index.rst
··· 1 - ========================== 2 - Linux Tracing Technologies 3 - ========================== 1 + ================================ 2 + Linux Tracing Technologies Guide 3 + ================================ 4 + 5 + Tracing in the Linux kernel is a powerful mechanism that allows 6 + developers and system administrators to analyze and debug system 7 + behavior. This guide provides documentation on various tracing 8 + frameworks and tools available in the Linux kernel. 9 + 10 + Introduction to Tracing 11 + ----------------------- 12 + 13 + This section provides an overview of Linux tracing mechanisms 14 + and debugging approaches. 4 15 5 16 .. toctree:: 6 - :maxdepth: 2 17 + :maxdepth: 1 7 18 8 - ftrace-design 19 + debugging 20 + tracepoints 9 21 tracepoint-analysis 22 + ring-buffer-map 23 + 24 + Core Tracing Frameworks 25 + ----------------------- 26 + 27 + The following are the primary tracing frameworks integrated into 28 + the Linux kernel. 29 + 30 + .. toctree:: 31 + :maxdepth: 1 32 + 10 33 ftrace 34 + ftrace-design 11 35 ftrace-uses 12 - fprobe 13 36 kprobes 14 37 kprobetrace 15 - uprobetracer 16 38 fprobetrace 17 - tracepoints 39 + fprobe 40 + ring-buffer-design 41 + 42 + Event Tracing and Analysis 43 + -------------------------- 44 + 45 + A detailed explanation of event tracing mechanisms and their 46 + applications. 47 + 48 + .. toctree:: 49 + :maxdepth: 1 50 + 18 51 events 19 52 events-kmem 20 53 events-power 21 54 events-nmi 22 55 events-msr 23 - mmiotrace 56 + boottime-trace 24 57 histogram 25 58 histogram-design 26 - boottime-trace 27 - debugging 28 - hwlat_detector 29 - osnoise-tracer 30 - timerlat-tracer 59 + 60 + Hardware and Performance Tracing 61 + -------------------------------- 62 + 63 + This section covers tracing features that monitor hardware 64 + interactions and system performance. 65 + 66 + .. toctree:: 67 + :maxdepth: 1 68 + 31 69 intel_th 32 - ring-buffer-design 33 - ring-buffer-map 34 70 stm 35 71 sys-t 36 72 coresight/index 37 - user_events 38 73 rv/index 39 74 hisi-ptt 75 + mmiotrace 76 + hwlat_detector 77 + osnoise-tracer 78 + timerlat-tracer 79 + 80 + User-Space Tracing 81 + ------------------ 82 + 83 + These tools allow tracing user-space applications and 84 + interactions. 85 + 86 + .. toctree:: 87 + :maxdepth: 1 88 + 89 + user_events 90 + uprobetracer 91 + 92 + Additional Resources 93 + -------------------- 94 + 95 + For more details, refer to the respective documentation of each 96 + tracing tool and framework. 97 + 98 + .. only:: subproject and html 99 + 100 + Indices 101 + ======= 102 + 103 + * :ref:`genindex`
+6 -5
Documentation/translations/sp_SP/process/2.Process.rst
··· 428 428 carga de correo electrónico, incumplir las convenciones utilizadas en las 429 429 listas de Linux, o ambas cosas. 430 430 431 - La mayoría de las listas de correo del kernel se ejecutan en 432 - vger.kernel.org; la lista principal se puede encontrar en: 431 + La mayoría de las listas de correo del kernel se alojan en kernel.org; la 432 + lista principal se puede encontrar en: 433 433 434 - http://vger.kernel.org/vger-lists.html 434 + https://subspace.kernel.org 435 435 436 - Sim embargo, hay listas alojadas en otros lugares; varios de ellos se 437 - encuentran en redhat.com/mailman/listinfo. 436 + Sin embargo, hay listas alojadas en otros lugares; consulte el archivo 437 + MAINTAINERS para obtener la lista relevante para cualquier subsistema en 438 + particular. 438 439 439 440 La lista de correo principal para el desarrollo del kernel es, por 440 441 supuesto, linux-kernel. Esta lista es un lugar intimidante; el volumen
+5 -5
Documentation/translations/sp_SP/process/howto.rst
··· 334 334 un repositorio especial de pruebas en el que se encuentran casi todos los 335 335 árboles de subsistema, actualizado casi a diario: 336 336 337 - https://git.kernel.org/?p=linux/kernel/git/next/linux-next.git 337 + https://git.kernel.org/pub/scm/linux/kernel/git/next/linux-next.git 338 338 339 339 De esta manera, linux-next ofrece una perspectiva resumida de lo que se 340 340 espera que entre en el kernel principal en el próximo período de "merge" ··· 378 378 Linux. Detalles sobre cómo para suscribirse y darse de baja de la lista se 379 379 pueden encontrar en: 380 380 381 - http://vger.kernel.org/vger-lists.html#linux-kernel 381 + https://subspace.kernel.org/subscribing.html 382 382 383 383 Existen archivos de la lista de correo en la web en muchos lugares 384 384 distintos. Utilice un motor de búsqueda para encontrar estos archivos. Por 385 385 ejemplo: 386 386 387 - http://dir.gmane.org/gmane.linux.kernel 387 + https://lore.kernel.org/linux-kernel/ 388 388 389 389 Es muy recomendable que busque en los archivos sobre el tema que desea 390 390 tratar, antes de publicarlo en la lista. Un montón de cosas ya discutidas ··· 398 398 Muchas de las listas están alojadas en kernel.org. La información sobre 399 399 estas puede ser encontrada en: 400 400 401 - http://vger.kernel.org/vger-lists.html 401 + https://subspace.kernel.org 402 402 403 403 Recuerde mantener buenos hábitos de comportamiento al usar las listas. 404 404 Aunque un poco cursi, la siguiente URL tiene algunas pautas simples para 405 405 interactuar con la lista (o cualquier lista): 406 406 407 - http://www.albion.com/netiquette/ 407 + https://subspace.kernel.org/etiquette.html 408 408 409 409 Si varias personas responden a su correo, el CC (lista de destinatarios) 410 410 puede hacerse bastante grande. No elimine a nadie de la lista CC: sin una
+2 -3
Documentation/translations/sp_SP/process/kernel-docs.rst
··· 170 170 171 171 * Título: **linux-kernel mailing list archives and search engines** 172 172 173 - :URL: http://vger.kernel.org/vger-lists.html 174 - :URL: http://www.uwsg.indiana.edu/hypermail/linux/kernel/index.html 175 - :URL: http://groups.google.com/group/mlist.linux.kernel 173 + :URL: https://subspace.kernel.org 174 + :URL: https://lore.kernel.org 176 175 :Palabras Clave: linux-kernel, archives, buscar, search, archivos. 177 176 :Descripción: Algunos de los archivadores de listas de correo del 178 177 kernel de Linux. Si usted tiene uno mejor/otro, por favor hágamelo
+5 -8
Documentation/translations/sp_SP/process/submitting-patches.rst
··· 136 136 137 137 Cuando se vincule a archivos de listas de correo, preferiblemente use el 138 138 servicio de archivador de mensajes lore.kernel.org. Para crear la URL del 139 - enlace, utilice el contenido del encabezado ("header") ``Message-Id`` del 139 + enlace, utilice el contenido del encabezado ("header") ``Message-ID`` del 140 140 mensaje sin los corchetes angulares que lo rodean. 141 141 Por ejemplo:: 142 142 143 - Link: https://lore.kernel.org/r/30th.anniversary.repost@klaava.Helsinki.FI/ 143 + Link: https://lore.kernel.org/30th.anniversary.repost@klaava.Helsinki.FI 144 144 145 145 Verifique el enlace para asegurarse de que realmente funciona y apunta al 146 146 mensaje correspondiente. ··· 257 257 probablemente recibirá más atención allí. Sin embargo, no envíe spam a 258 258 listas no relacionadas. 259 259 260 - Muchas listas relacionadas con el kernel están alojadas en vger.kernel.org; 260 + Muchas listas relacionadas con el kernel están alojadas en kernel.org; 261 261 puedes encontrar un listado de estas en 262 - http://vger.kernel.org/vger-lists.html. Existen listas relacionadas con el 263 - kernel alojadas en otros lugares, no obstante. 262 + https://subspace.kernel.org. Existen listas relacionadas con el kernel 263 + alojadas en otros lugares, no obstante. 264 264 265 265 ¡No envíe más de 15 parches a la vez a las listas de correo de vger! 266 266 ··· 906 906 <http://www.kroah.com/log/linux/maintainer-05.html> 907 907 908 908 <http://www.kroah.com/log/linux/maintainer-06.html> 909 - 910 - NO!!!! Gente, no mas bombas enormes de parches a linux-kernel@vger.kernel.org! 911 - <https://lore.kernel.org/r/20050711.125305.08322243.davem@davemloft.net> 912 909 913 910 Kernel Documentation/process/coding-style.rst 914 911
+459
Documentation/translations/zh_CN/how-to.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + ========================= 4 + Linux内核中文文档翻译规范 5 + ========================= 6 + 7 + 修订记录: 8 + - v1.0 2025年3月28日,司延腾、慕冬亮共同编写了该规范。 9 + 10 + 制定规范的背景 11 + ============== 12 + 13 + 过去几年,在广大社区爱好者的友好合作下,Linux 内核中文文档迎来了蓬勃的发 14 + 展。在翻译的早期,一切都是混乱的,社区对译稿只有一个准确翻译的要求,以鼓 15 + 励更多的开发者参与进来,这是从0到1的必然过程,所以早期的中文文档目录更加 16 + 具有多样性,不过好在文档不多,维护上并没有过大的压力。 17 + 18 + 然而,世事变幻,不觉有年,现在内核中文文档在前进的道路上越走越远,很多潜 19 + 在的问题逐渐浮出水面,而且随着中文文档数量的增加,翻译更多的文档与提高中 20 + 文文档可维护性之间的矛盾愈发尖锐。由于文档翻译的特殊性,很多开发者并不会 21 + 一直更新文档,如果中文文档落后英文文档太多,文档更新的工作量会远大于重新 22 + 翻译。而且邮件列表中陆续有新的面孔出现,他们那股热情,就像燃烧的火焰,能 23 + 瞬间点燃整个空间,可是他们的补丁往往具有个性,这会给审阅带来了很大的困难, 24 + reviewer 们只能耐心地指导他们如何与社区更好地合作,但是这项工作具有重复 25 + 性,长此以往,会渐渐浇灭 reviewer 审阅的热情。 26 + 27 + 虽然内核文档中已经有了类似的贡献指南,但是缺乏专门针对于中文翻译的,尤其 28 + 是对于新手来说,浏览大量的文档反而更加迷惑,该文档就是为了缓解这一问题而 29 + 编写,目的是为提供给新手一个快速翻译指南。 30 + 31 + 详细的贡献指南:Documentation/translations/zh_CN/process/index.rst。 32 + 33 + 环境搭建 34 + ======== 35 + 36 + 工欲善其事必先利其器,如果您目前对内核文档翻译满怀热情,并且会独立地安装 37 + linux 发行版和简单地使用 linux 命令行,那么可以迅速开始了。若您尚不具备该 38 + 能力,很多网站上会有详细的手把手教程,最多一个上午,您应该就能掌握对应技 39 + 能。您需要注意的一点是,请不要使用 root 用户进行后续步骤和文档翻译。 40 + 41 + 拉取开发树 42 + ---------- 43 + 44 + 中文文档翻译工作目前独立于 linux-doc 开发树开展,所以您需要拉取该开发树, 45 + 打开终端命令行执行:: 46 + 47 + git clone git://git.kernel.org/pub/scm/linux/kernel/git/alexs/linux.git 48 + 49 + 如果您遇到网络连接问题,也可以执行以下命令:: 50 + 51 + git clone https://mirrors.hust.edu.cn/git/kernel-doc-zh.git linux 52 + 53 + 这是 Alex 开发树的镜像库,每两个小时同步一次上游。如果您了解到更快的 mirror, 54 + 请随时 **添加** 。 55 + 56 + 命令执行完毕后,您会在当前目录下得到一个 linux 目录,该目录就是您之后的工作 57 + 仓库,请把它放在一个稳妥的位置。 58 + 59 + 安装文档构建环境 60 + ---------------- 61 + 62 + 内核仓库里面提供了一个半自动化脚本,执行该脚本,会检测您的发行版中需要安 63 + 装哪些软件包,请按照命令行提示进行安装,通常您只需要复制命令并执行就行。 64 + :: 65 + 66 + cd linux 67 + ./scripts/sphinx-pre-install 68 + 69 + 以Fedora为例,它的输出是这样的:: 70 + 71 + You should run: 72 + 73 + sudo dnf install -y dejavu-sans-fonts dejavu-sans-mono-fonts dejavu-serif-fonts google-noto-sans-cjk-fonts graphviz-gd latexmk librsvg2-tools texlive-anyfontsize texlive-capt-of texlive-collection-fontsrecommended texlive-ctex texlive-eqparbox texlive-fncychap texlive-framed texlive-luatex85 texlive-multirow texlive-needspace texlive-tabulary texlive-threeparttable texlive-upquote texlive-wrapfig texlive-xecjk 74 + 75 + Sphinx needs to be installed either: 76 + 1) via pip/pypi with: 77 + 78 + /usr/bin/python3 -m venv sphinx_latest 79 + . sphinx_latest/bin/activate 80 + pip install -r ./Documentation/sphinx/requirements.txt 81 + 82 + If you want to exit the virtualenv, you can use: 83 + deactivate 84 + 85 + 2) As a package with: 86 + 87 + sudo dnf install -y python3-sphinx 88 + 89 + Please note that Sphinx >= 3.0 will currently produce false-positive 90 + warning when the same name is used for more than one type (functions, 91 + structs, enums,...). This is known Sphinx bug. For more details, see: 92 + https://github.com/sphinx-doc/sphinx/pull/8313 93 + 94 + 请您按照提示复制打印的命令到命令行执行,您必须具备 root 权限才能执行 sudo 95 + 开头的命令。 96 + 97 + 如果您处于一个多用户环境中,为了避免对其他人造成影响,建议您配置单用户 98 + sphinx 虚拟环境,即只需要执行:: 99 + 100 + /usr/bin/python3 -m venv sphinx_latest 101 + . sphinx_latest/bin/activate 102 + pip install -r ./Documentation/sphinx/requirements.txt 103 + 104 + 最后执行以下命令退出虚拟环境:: 105 + 106 + deactivate 107 + 108 + 您可以在任何需要的时候再次执行以下命令进入虚拟环境:: 109 + 110 + . sphinx_latest/bin/activate 111 + 112 + 进行第一次文档编译 113 + ------------------ 114 + 115 + 进入开发树目录:: 116 + 117 + cd linux 118 + 119 + 这是一个标准的编译和调试流程,请每次构建时都严格执行:: 120 + 121 + . sphinx_latest/bin/activate 122 + make cleandocs 123 + make htmldocs 124 + deactivate 125 + 126 + 检查编译结果 127 + ------------ 128 + 129 + 编译输出在Documentation/output/目录下,请用浏览器打开该目录下对应 130 + 的文件进行检查。 131 + 132 + git和邮箱配置 133 + ------------- 134 + 135 + 打开命令行执行:: 136 + 137 + sudo dnf install git-email 138 + vim ~/.gitconfig 139 + 140 + 这里是我的一个配置文件示范,请根据您的邮箱域名服务商提供的手册替换到对 141 + 应的字段。 142 + :: 143 + 144 + [user] 145 + name = Yanteng Si # 这会出现在您的补丁头部签名栏 146 + email = si.yanteng@linux.dev # 这会出现在您的补丁头部签名栏 147 + 148 + [sendemail] 149 + from = Yanteng Si <si.yanteng@linux.dev> # 这会出现在您的补丁头部 150 + smtpencryption = ssl 151 + smtpserver = smtp.migadu.com 152 + smtpuser = si.yanteng@linux.dev 153 + smtppass = <passwd> # 建议使用第三方客户端专用密码 154 + chainreplyto = false 155 + smtpserverport = 465 156 + 157 + 关于邮件客户端的配置,请查阅Documentation/translations/zh_CN/process/email-clients.rst。 158 + 159 + 开始翻译文档 160 + ============ 161 + 162 + 文档索引结构 163 + ------------ 164 + 165 + 目前中文文档是在Documentation/translations/zh_CN/目录下进行,该 166 + 目录结构最终会与Documentation/结构一致,所以您只需要将您感兴趣的英文 167 + 文档文件和对应的 index.rst 复制到 zh_CN 目录下对应的位置,然后修改更 168 + 上一级的 index 即可开始您的翻译。 169 + 170 + 为了保证翻译的文档补丁被顺利合并,不建议多人同时翻译一个目录,因为这会 171 + 造成补丁之间互相依赖,往往会导致一部分补丁被合并,另一部分产生冲突。 172 + 173 + 如果实在无法避免两个人同时对一个目录进行翻译的情况,请将补丁制作进一个补 174 + 丁集。但是不推荐刚开始就这么做,因为经过实践,在没有指导的情况下,新手很 175 + 难一次处理好这个补丁集。 176 + 177 + 请执行以下命令,新建开发分支:: 178 + 179 + git checkout docs-next 180 + git branch my-trans 181 + git checkout my-trans 182 + 183 + 译文格式要求 184 + ------------ 185 + 186 + - 每行长度最多不超过40个字符 187 + - 每行长度请保持一致 188 + - 标题的下划线长度请按照一个英文一个字符、一个中文两个字符与标题对齐 189 + - 其它的修饰符请与英文文档保持一致 190 + 191 + 此外在译文的头部,您需要插入以下内容:: 192 + 193 + .. SPDX-License-Identifier: GPL-2.0 194 + .. include:: ../disclaimer-zh_CN.rst #您需要了解该文件的路径,根 195 + 据您实际翻译的文档灵活调整 196 + 197 + :Original: Documentation/xxx/xxx.rst #替换为您翻译的英文文档路径 198 + 199 + :翻译: 200 + 201 + 司延腾 Yanteng Si <si.yanteng@linux.dev> #替换为您自己的联系方式 202 + 203 + 翻译技巧 204 + -------- 205 + 206 + 中文文档有每行40字符限制,因为一个中文字符等于2个英文字符。但是社区并没有 207 + 那么严格,一个诀窍是将您的翻译的内容与英文原文的每行长度对齐即可,这样, 208 + 您也不必总是检查有没有超限。 209 + 210 + 如果您的英文阅读能力有限,可以考虑使用辅助翻译工具,例如 deepseek 。但是您 211 + 必须仔细地打磨,使译文达到“信达雅”的标准。 212 + 213 + **请注意** 社区不接受纯机器翻译的文档,社区工作建立在信任的基础上,请认真对待。 214 + 215 + 编译和检查 216 + ---------- 217 + 218 + 请执行:: 219 + 220 + . sphinx_latest/bin/activate 221 + make cleandocs 222 + make htmldocs 223 + 224 + 解决与您翻译的文档相关的 warning 和 error,然后执行:: 225 + 226 + make cleandocs #该步骤不能省略,否则可能不会再次输出真实存在的警告 227 + make htmldocs 228 + deactivate 229 + 230 + 进入 output 目录用浏览器打开您翻译的文档,检查渲染的页面是否正常,如果正常, 231 + 继续进行后续步骤,否则请尝试解决。 232 + 233 + 制作补丁 234 + ======== 235 + 236 + 提交改动 237 + -------- 238 + 239 + 执行以下命令,在弹出的交互式页面中填写必要的信息。 240 + :: 241 + 242 + git add . 243 + git commit -s -v 244 + 245 + 请参考以下信息进行输入:: 246 + 247 + docs/zh_CN: Add self-protection index Chinese translation 248 + 249 + Translate .../security/self-protection.rst into Chinese. 250 + 251 + Update the translation through commit b080e52110ea #请执行git log <您翻译的英文文档路径> 复制最顶部第一个补丁的sha值的前12位,替换掉12位sha值。 252 + ("docs: update self-protection __ro_after_init status") 253 + 254 + Signed-off-by: Yanteng Si <si.yanteng@linux.dev> #如果您前面的步骤正确执行,该行会自动显示,否则请检查gitconfig文件。 255 + 256 + 保存并退出。 257 + 258 + **请注意** 以上四行,缺少任何一行,您都将会在第一轮审阅后返工,如果您需要一个更加明确的示例,请对 zh_CN 目录执行 git log。 259 + 260 + 导出补丁和制作封面 261 + ------------------ 262 + 263 + 这个时候,可以导出补丁,做发送邮件列表最后的准备了。命令行执行:: 264 + 265 + git format-patch -N 266 + 267 + 然后命令行会输出类似下面的内容:: 268 + 269 + 0001-docs-zh_CN-add-xxxxxxxx.patch 270 + 0002-docs-zh_CN-add-xxxxxxxx.patch 271 + …… 272 + 273 + 测试补丁 274 + -------- 275 + 276 + 内核提供了一个补丁检测脚本,请执行:: 277 + 278 + ./scripts/checkpatch.pl *.patch 279 + 280 + 参考脚本输出,解决掉所有的 error 和 warning,通常情况下,只有下面这个 281 + warning 不需要解决:: 282 + 283 + WARNING: added, moved or deleted file(s), does MAINTAINERS need updating? 284 + 285 + 一个简单的解决方法是一次只检查一个补丁,然后打上该补丁,直接对译文进行修改, 286 + 然后执行以下命令为补丁追加更改:: 287 + 288 + git checkout docs-next 289 + git branch test-trans 290 + git am 0001-xxxxx.patch 291 + ./scripts/checkpatch.pl 0001-xxxxx.patch 292 + 直接修改您的翻译 293 + git add . 294 + git am --amend 295 + 保存退出 296 + git am 0002-xxxxx.patch 297 + …… 298 + 299 + 重新导出再次检测,重复这个过程,直到处理完所有的补丁。 300 + 301 + 最后,如果检测时没有 warning 和 error 需要被处理或者您只有一个补丁,请跳 302 + 过下面这个步骤,否则请重新导出补丁制作封面:: 303 + 304 + git format-patch -N --cover-letter --thread=shallow #N为您的补丁数量,N一般要大于1。 305 + 306 + 然后命令行会输出类似下面的内容:: 307 + 308 + 0000-cover-letter.patch 309 + 0001-docs-zh_CN-add-xxxxxxxx.patch 310 + 0002-docs-zh_CN-add-xxxxxxxx.patch 311 + 312 + 您需要用编辑器打开0号补丁,修改两处内容:: 313 + 314 + vim 0000-cover-letter.patch 315 + 316 + ... 317 + Subject: [PATCH 0/1] *** SUBJECT HERE *** #修改该字段,概括您的补丁集都做了哪些事情 318 + 319 + *** BLURB HERE *** #修改该字段,详细描述您的补丁集做了哪些事情 320 + 321 + Yanteng Si (1): 322 + docs/zh_CN: add xxxxx 323 + ... 324 + 325 + 如果您只有一个补丁,则可以不制作封面,即0号补丁,只需要执行:: 326 + 327 + git format-patch -1 328 + 329 + 把补丁提交到邮件列表 330 + ==================== 331 + 332 + 恭喜您,您的文档翻译现在可以提交到邮件列表了。 333 + 334 + 获取维护者和审阅者邮箱以及邮件列表地址 335 + -------------------------------------- 336 + 337 + 内核提供了一个自动化脚本工具,请执行:: 338 + 339 + ./scripts/get_maintainer.pl *.patch 340 + 341 + 将输出的邮箱地址保存下来。 342 + 343 + 将补丁提交到邮件列表 344 + -------------------- 345 + 346 + 打开上面您保存的邮件地址,执行:: 347 + 348 + git send-email *.patch --to <maintainer email addr> --cc <others addr> #一个to对应一个地址,一个cc对应一个地址,有几个就写几个。 349 + 350 + 执行该命令时,请确保网络通常,邮件发送成功一般会返回250。 351 + 352 + 您可以先发送给自己,尝试发出的 patch 是否可以用 'git am' 工具正常打上。 353 + 如果检查正常, 您就可以放心的发送到社区评审了。 354 + 355 + 如果该步骤被中断,您可以检查一下,继续用上条命令发送失败的补丁,一定不要再 356 + 次发送已经发送成功的补丁。 357 + 358 + 积极参与审阅过程并迭代补丁 359 + ========================== 360 + 361 + 补丁提交到邮件列表并不代表万事大吉,您还需要积极回复 maintainer 和 362 + reviewer 的评论,做到每条都有回复,每个回复都落实到位。 363 + 364 + 如何回复评论 365 + ------------ 366 + 367 + - 请先将您的邮箱客户端信件回复修改为 **纯文本** 格式,并去除所有签名,尤其是 368 + 企业邮箱。 369 + - 然后点击回复按钮,并将要回复的邮件带入, 370 + - 在第一条评论行尾换行,输入您的回复 371 + - 在第二条评论行尾换行,输入您的回复 372 + - 直到处理完最后一条评论,换行空两行输入问候语和署名 373 + 374 + 注意,信件回复请尽量使用英文。 375 + 376 + 迭代补丁 377 + -------- 378 + 379 + 建议您每回复一条评论,就修改一处翻译。然后重新生成补丁,相信您现在已经具 380 + 备了灵活使用 git am --amend 的能力。 381 + 382 + 每次迭代一个补丁,不要一次多个:: 383 + 384 + git am <您要修改的补丁> 385 + 直接对文件进行您的修改 386 + git add . 387 + git commit --amend 388 + 389 + 当您将所有的评论落实到位后,导出第二版补丁,并修改封面:: 390 + 391 + git format-patch -N -v 2 --cover-letter --thread=shallow 392 + 393 + 打开0号补丁,在 BLURB HERE 处编写相较于上个版本,您做了哪些改动。 394 + 395 + 然后执行:: 396 + 397 + git send-email v2* --to <maintainer email addr> --cc <others addr> 398 + 399 + 这样,新的一版补丁就又发送到邮件列表等待审阅,之后就是重复这个过程。 400 + 401 + 审阅周期 402 + -------- 403 + 404 + 因为有时邮件列表比较繁忙,您的邮件可能会被淹没,如果超过两周没有得到任何 405 + 回复,请自己回复自己,回复的内容为 Ping. 406 + 407 + 最终,如果您落实好了所有的评论,并且一段时间后没有最新的评论,您的补丁将 408 + 会先进入 Alex 的开发树,然后进入 linux-doc 开发树,最终在下个窗口打开 409 + 时合并进 mainline 仓库。 410 + 411 + 紧急处理 412 + -------- 413 + 414 + 如果您发送到邮件列表之后。发现发错了补丁集,尤其是在多个版本迭代的过程中; 415 + 自己发现了一些不妥的翻译;发送错了邮件列表…… 416 + 417 + git email默认会抄送给您一份,所以您可以切换为审阅者的角色审查自己的补丁, 418 + 并留下评论,描述有何不妥,将在下个版本怎么改,并付诸行动,重新提交,但是 419 + 注意频率,每天提交的次数不要超过两次。 420 + 421 + 新手任务 422 + -------- 423 + 对于首次参与 Linux 内核中文文档翻译的新手,建议您在 linux 目录中运行以下命令: 424 + :: 425 + 426 + ./script/checktransupdate.py -l zh_CN`` 427 + 428 + 该命令会列出需要翻译或更新的英文文档。 429 + 430 + 关于详细操作说明,请参考: Documentation/translations/zh_CN/doc-guide/checktransupdate.rst\ 431 + 432 + 进阶 433 + ---- 434 + 435 + 希望您不只是单纯的翻译内核文档,在熟悉了一起与社区工作之后,您可以审阅其他 436 + 开发者的翻译,或者提出具有建设性的主张。与此同时,与文档对应的代码更加有趣, 437 + 而且需要完善的地方还有很多,勇敢地去探索,然后提交你的想法吧。 438 + 439 + 常见的问题 440 + ========== 441 + 442 + Maintainer回复补丁不能正常apply 443 + ------------------------------- 444 + 445 + 这通常是因为您的补丁与邮件列表其他人的补丁产生了冲突,别人的补丁先被 apply 了, 446 + 您的补丁集就无法成功 apply 了,这需要您更新本地分支,在本地解决完冲突后再次提交。 447 + 448 + 请尽量避免冲突,不要多个人同时翻译一个目录。翻译之前可以通过 git log 查看您感 449 + 兴趣的目录近期有没有其他人翻译,如果有,请提前私信联系对方,请求其代为发送您 450 + 的补丁。如果对方未来一个月内没有提交新补丁的打算,您可以独自发送。 451 + 452 + 回信被邮件列表拒收 453 + ------------------ 454 + 455 + 大部分情况下,是由于您发送了非纯文本格式的信件,请尽量避免使用 webmail,推荐 456 + 使用邮件客户端,比如 thunderbird,记得在设置中的回信配置那改为纯文本发送。 457 + 458 + 如果超过了24小时,您依旧没有在<https://lore.kernel.org/linux-doc/>发现您的邮 459 + 件,请联系您的网络管理员帮忙解决。
+10 -10
Documentation/translations/zh_CN/index.rst
··· 21 21 这是中文内核文档树的顶级目录。内核文档,就像内核本身一样,在很大程度上是一 22 22 项正在进行的工作;当我们努力将许多分散的文件整合成一个连贯的整体时尤其如此。 23 23 另外,随时欢迎您对内核文档进行改进;如果您想提供帮助,请加入vger.kernel.org 24 - 上的linux-doc邮件列表。 24 + 上的linux-doc邮件列表,并按照Documentation/translations/zh_CN/how-to.rst的 25 + 指引提交补丁。提交补丁之前请确保执行"make htmldocs”后无与翻译有关的异常输出。 25 26 26 - 顺便说下,中文文档也需要遵守内核编码风格,风格中中文和英文的主要不同就是中文 27 - 的字符标点占用两个英文字符宽度,所以,当英文要求不要超过每行100个字符时, 28 - 中文就不要超过50个字符。另外,也要注意'-','='等符号与相关标题的对齐。在将 29 - 补丁提交到社区之前,一定要进行必要的 ``checkpatch.pl`` 检查和编译测试,确保 30 - 在 ``make htmldocs/pdfdocs`` 中不增加新的告警,最后,安装检查你生成的 31 - html/pdf 文件,确认它们看起来是正常的。 27 + 如何翻译内核文档 28 + ---------------- 32 29 33 - 提交之前请确认你的补丁可以正常提交到中文文档维护库: 34 - https://git.kernel.org/pub/scm/linux/kernel/git/alexs/linux.git/ 35 - 如果你的补丁依赖于其他人的补丁, 可以与其他人商量后由某一个人合并提交。 30 + 翻译文档本身是一件很简单的事情,但是提交补丁需要注意一些细节,为了保证内核中文文档的高质量可持续发展,提供了一份翻译指南。 31 + 32 + .. toctree:: 33 + :maxdepth: 1 34 + 35 + how-to.rst 36 36 37 37 与Linux 内核社区一起工作 38 38 ------------------------
+160
Documentation/translations/zh_CN/networking/index.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + .. include:: ../disclaimer-zh_CN.rst 4 + 5 + :Original: Documentation/networking/index.rst 6 + 7 + :翻译: 8 + 9 + 王亚鑫 Wang Yaxin <wang.yaxin@zte.com.cn> 10 + 11 + :校译: 12 + 13 + 网络 14 + ==== 15 + 16 + 有关网络设备(netdev)开发过程的详细指南,请参考::ref:`netdev-FAQ` 17 + 18 + 目录: 19 + 20 + .. toctree:: 21 + :maxdepth: 1 22 + 23 + msg_zerocopy 24 + 25 + Todolist: 26 + 27 + * af_xdp 28 + * bareudp 29 + * batman-adv 30 + * can 31 + * can_ucan_protocol 32 + * device_drivers/index 33 + * diagnostic/index 34 + * dsa/index 35 + * devlink/index 36 + * caif/index 37 + * ethtool-netlink 38 + * ieee802154 39 + * iso15765-2 40 + * j1939 41 + * kapi 42 + * failover 43 + * net_dim 44 + * net_failover 45 + * page_pool 46 + * phy 47 + * sfp-phylink 48 + * alias 49 + * bridge 50 + * snmp_counter 51 + * checksum-offloads 52 + * segmentation-offloads 53 + * scaling 54 + * tls 55 + * tls-offload 56 + * tls-handshake 57 + * nfc 58 + * 6lowpan 59 + * 6pack 60 + * arcnet-hardware 61 + * arcnet 62 + * atm 63 + * ax25 64 + * bonding 65 + * cdc_mbim 66 + * dccp 67 + * dctcp 68 + * devmem 69 + * dns_resolver 70 + * driver 71 + * eql 72 + * fib_trie 73 + * filter 74 + * generic-hdlc 75 + * generic_netlink 76 + * netlink_spec/index 77 + * gen_stats 78 + * gtp 79 + * ila 80 + * ioam6-sysctl 81 + * ip_dynaddr 82 + * ipsec 83 + * ip-sysctl 84 + * ipv6 85 + * ipvlan 86 + * ipvs-sysctl 87 + * kcm 88 + * l2tp 89 + * lapb-module 90 + * mac80211-injection 91 + * mctp 92 + * mpls-sysctl 93 + * mptcp 94 + * mptcp-sysctl 95 + * multiqueue 96 + * multi-pf-netdev 97 + * napi 98 + * net_cachelines/index 99 + * netconsole 100 + * netdev-features 101 + * netdevices 102 + * netfilter-sysctl 103 + * netif-msg 104 + * netmem 105 + * nexthop-group-resilient 106 + * nf_conntrack-sysctl 107 + * nf_flowtable 108 + * oa-tc6-framework 109 + * openvswitch 110 + * operstates 111 + * packet_mmap 112 + * phonet 113 + * phy-link-topology 114 + * pktgen 115 + * plip 116 + * ppp_generic 117 + * proc_net_tcp 118 + * pse-pd/index 119 + * radiotap-headers 120 + * rds 121 + * regulatory 122 + * representors 123 + * rxrpc 124 + * sctp 125 + * secid 126 + * seg6-sysctl 127 + * skbuff 128 + * smc-sysctl 129 + * sriov 130 + * statistics 131 + * strparser 132 + * switchdev 133 + * sysfs-tagging 134 + * tc-actions-env-rules 135 + * tc-queue-filters 136 + * tcp_ao 137 + * tcp-thin 138 + * team 139 + * timestamping 140 + * tipc 141 + * tproxy 142 + * tuntap 143 + * udplite 144 + * vrf 145 + * vxlan 146 + * x25 147 + * x25-iface 148 + * xfrm_device 149 + * xfrm_proc 150 + * xfrm_sync 151 + * xfrm_sysctl 152 + * xdp-rx-metadata 153 + * xsk-tx-metadata 154 + 155 + .. only:: subproject and html 156 + 157 + Indices 158 + ======= 159 + 160 + * :ref:`genindex`
+223
Documentation/translations/zh_CN/networking/msg_zerocopy.rst
··· 1 + .. SPDX-License-Identifier: GPL-2.0 2 + 3 + .. include:: ../disclaimer-zh_CN.rst 4 + 5 + :Original: Documentation/networking/msg_zerocopy.rst 6 + 7 + :翻译: 8 + 9 + 王亚鑫 Wang Yaxin <wang.yaxin@zte.com.cn> 10 + 11 + :校译: 12 + 13 + - 徐鑫 xu xin <xu.xin16@zte.com.cn> 14 + - 何配林 He Peilin <he.peilin@zte.com.cn> 15 + 16 + ============ 17 + MSG_ZEROCOPY 18 + ============ 19 + 20 + 简介 21 + ==== 22 + 23 + MSG_ZEROCOPY 标志用于启用套接字发送调用的免拷贝功能。该功能目前适用于 TCP、UDP 和 VSOCK 24 + (使用 virtio 传输)套接字。 25 + 26 + 机遇与注意事项 27 + -------------- 28 + 29 + 在用户进程与内核之间拷贝大型缓冲区可能会消耗大量资源。Linux 支持多种免拷贝的接口,如sendfile 30 + 和 splice。MSG_ZEROCOPY 标志将底层的拷贝避免机制扩展到了常见的套接字发送调用中。 31 + 32 + 免拷贝并非毫无代价。在实现上,它通过页面固定(page pinning)将按字节拷贝的成本替换为页面统计 33 + (page accounting)和完成通知的开销。因此,MSG_ZEROCOPY 通常仅在写入量超过大约 10 KB 时 34 + 才有效。 35 + 36 + 页面固定还会改变系统调用的语义。它会暂时在进程和网络堆栈之间共享缓冲区。与拷贝不同,进程在系统 37 + 调用返回后不能立即覆盖缓冲区,否则可能会修改正在传输中的数据。内核的完整性不会受到影响,但有缺 38 + 陷的程序可能会破坏自己的数据流。 39 + 40 + 当内核返回数据可以安全修改的通知时,进程才可以修改数据。因此,将现有应用程序转换为使用 41 + MSG_ZEROCOPY 并非总是像简单地传递该标志那样容易。 42 + 43 + 更多信息 44 + -------- 45 + 46 + 本文档的大部分内容是来自于 netdev 2.1 上发表的一篇长篇论文。如需更深入的信息,请参阅该论文和 47 + 演讲,或者浏览 LWN.net 上的精彩报道,也可以直接阅读源码。 48 + 49 + 论文、幻灯片、视频: 50 + https://netdevconf.org/2.1/session.html?debruijn 51 + 52 + LWN 文章: 53 + https://lwn.net/Articles/726917/ 54 + 55 + 补丁集: 56 + [PATCH net-next v4 0/9] socket sendmsg MSG_ZEROCOPY 57 + https://lore.kernel.org/netdev/20170803202945.70750-1-willemdebruijn.kernel@gmail.com 58 + 59 + 接口 60 + ==== 61 + 62 + 传递 MSG_ZEROCOPY 标志是启用免拷贝功能的最明显步骤,但并非唯一的步骤。 63 + 64 + 套接字设置 65 + ---------- 66 + 67 + 当应用程序向 send 系统调用传递未定义的标志时,内核通常会宽容对待。默认情况下,它会简单地忽略 68 + 这些标志。为了避免为那些偶然传递此标志的遗留进程启用免拷贝模式,进程必须首先通过设置套接字选项 69 + 来表明意图: 70 + 71 + :: 72 + 73 + if (setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &one, sizeof(one))) 74 + error(1, errno, "setsockopt zerocopy"); 75 + 76 + 传输 77 + ---- 78 + 79 + 对 send(或 sendto、sendmsg、sendmmsg)本身的改动非常简单。只需传递新的标志即可。 80 + 81 + :: 82 + 83 + ret = send(fd, buf, sizeof(buf), MSG_ZEROCOPY); 84 + 85 + 如果零拷贝操作失败,将返回 -1,并设置 errno 为 ENOBUFS。这种情况可能发生在套接字超出其 86 + optmem 限制,或者用户超出其锁定页面的 ulimit 时。 87 + 88 + 混合使用免拷贝和拷贝 89 + ~~~~~~~~~~~~~~~~~~~~ 90 + 91 + 许多工作负载同时包含大型和小型缓冲区。由于对于小数据包来说,免拷贝的成本高于拷贝,因此该 92 + 功能是通过标志实现的。带有标志的调用和没有标志的调用可以安全地混合使用。 93 + 94 + 通知 95 + ---- 96 + 97 + 当内核认为可以安全地重用之前传递的缓冲区时,它必须通知进程。完成通知在套接字的错误队列上 98 + 排队,类似于传输时间戳接口。 99 + 100 + 通知本身是一个简单的标量值。每个套接字都维护一个内部的无符号 32 位计数器。每次带有 101 + MSG_ZEROCOPY 标志的 send 调用成功发送数据时,计数器都会增加。如果调用失败或长度为零, 102 + 则计数器不会增加。该计数器统计系统调用的调用次数,而不是字节数。在 UINT_MAX 次调用后, 103 + 计数器会循环。 104 + 105 + 通知接收 106 + ~~~~~~~~ 107 + 108 + 下面的代码片段展示了 API 的使用。在最简单的情况下,每次 send 系统调用后,都会对错误队列 109 + 进行轮询和 recvmsg 调用。 110 + 111 + 从错误队列读取始终是一个非阻塞操作。poll 调用用于阻塞,直到出现错误。它会在其输出标志中 112 + 设置 POLLERR。该标志不需要在 events 字段中设置。错误会无条件地发出信号。 113 + 114 + :: 115 + 116 + pfd.fd = fd; 117 + pfd.events = 0; 118 + if (poll(&pfd, 1, -1) != 1 || pfd.revents & POLLERR == 0) 119 + error(1, errno, "poll"); 120 + 121 + ret = recvmsg(fd, &msg, MSG_ERRQUEUE); 122 + if (ret == -1) 123 + error(1, errno, "recvmsg"); 124 + 125 + read_notification(msg); 126 + 127 + 128 + 这个示例仅用于演示目的。在实际应用中,不等待通知,而是每隔几次 send 调用就进行一次非阻塞 129 + 读取会更高效。 130 + 131 + 零拷贝通知可以与其他套接字操作乱序处理。通常,拥有错误队列套接字会阻塞其他操作,直到错误 132 + 被读取。然而,零拷贝通知具有零错误代码,因此不会阻塞 send 和 recv 调用。 133 + 134 + 通知批处理 135 + ~~~~~~~~~~~~ 136 + 137 + 可以使用 recvmmsg 调用来一次性读取多个未决的数据包。这通常不是必需的。在每条消息中,内核 138 + 返回的不是一个单一的值,而是一个范围。当错误队列上有一个通知正在等待接收时,它会将连续的通 139 + 知合并起来。 140 + 141 + 当一个新的通知即将被排队时,它会检查队列尾部的通知的范围是否可以扩展以包含新的值。如果是这 142 + 样,它会丢弃新的通知数据包,并增大未处理通知的范围上限值。 143 + 144 + 对于按顺序确认数据的协议(如 TCP),每个通知都可以合并到前一个通知中,因此在任何时候在等待 145 + 的通知都不会超过一个。 146 + 147 + 有序交付是常见的情况,但不能保证。在重传和套接字拆除时,通知可能会乱序到达。 148 + 149 + 通知解析 150 + ~~~~~~~~ 151 + 152 + 下面的代码片段演示了如何解析控制消息:前面代码片段中的 read_notification() 调用。通知 153 + 以标准错误格式 sock_extended_err 编码。 154 + 155 + 控制数据中的级别和类型字段是协议族特定的,对于 TCP 或 UDP 套接字,分别为 IP_RECVERR 或 156 + IPV6_RECVERR。对于 VSOCK 套接字,cmsg_level 为 SOL_VSOCK,cmsg_type 为 VSOCK_RECVERR。 157 + 158 + 错误来源是新的类型 SO_EE_ORIGIN_ZEROCOPY。如前所述,ee_errno 为零,以避免在套接字上 159 + 阻塞地读取和写入系统调用。 160 + 161 + 32 位通知范围编码为 [ee_info, ee_data]。这个范围是包含边界值的。除了下面讨论的 ee_code 162 + 字段外,结构中的其他字段应被视为未定义的。 163 + 164 + :: 165 + 166 + struct sock_extended_err *serr; 167 + struct cmsghdr *cm; 168 + 169 + cm = CMSG_FIRSTHDR(msg); 170 + if (cm->cmsg_level != SOL_IP && 171 + cm->cmsg_type != IP_RECVERR) 172 + error(1, 0, "cmsg"); 173 + 174 + serr = (void *) CMSG_DATA(cm); 175 + if (serr->ee_errno != 0 || 176 + serr->ee_origin != SO_EE_ORIGIN_ZEROCOPY) 177 + error(1, 0, "serr"); 178 + 179 + printf("completed: %u..%u\n", serr->ee_info, serr->ee_data); 180 + 181 + 182 + 延迟拷贝 183 + ~~~~~~~~ 184 + 185 + 传递标志 MSG_ZEROCOPY 是向内核发出的一个提示,让内核采用免拷贝的策略,同时也是一种约 186 + 定,即内核会对完成通知进行排队处理。但这并不保证拷贝操作一定会被省略。 187 + 188 + 拷贝避免不总是适用的。不支持分散/聚集 I/O 的设备无法发送由内核生成的协议头加上零拷贝用户 189 + 数据组成的数据包。数据包可能需要在协议栈底层转换为一份私有数据副本,例如用于计算校验和。 190 + 191 + 在所有这些情况下,当内核释放对共享页面的持有权时,它会返回一个完成通知。该通知可能在(已 192 + 拷贝)数据完全传输之前到达。因此。零拷贝完成通知并不是传输完成通知。 193 + 194 + 如果数据不在缓存中,延迟拷贝可能会比立即在系统调用中拷贝开销更大。进程还会因通知处理而产 195 + 生成本,但却没有带来任何好处。因此,内核会在返回时通过在 ee_code 字段中设置标志 196 + SO_EE_CODE_ZEROCOPY_COPIED 来指示数据是否以拷贝的方式完成。进程可以利用这个信号,在 197 + 同一套接字上后续的请求中停止传递 MSG_ZEROCOPY 标志。 198 + 199 + 实现 200 + ==== 201 + 202 + 环回 203 + ---- 204 + 205 + 对于 TCP 和 UDP: 206 + 如果接收进程不读取其套接字,发送到本地套接字的数据可能会无限期排队。无限期的通知延迟是不 207 + 可接受的。因此,所有使用 MSG_ZEROCOPY 生成并环回到本地套接字的数据包都将产生延迟拷贝。 208 + 这包括环回到数据包套接字(例如,tcpdump)和 tun 设备。 209 + 210 + 对于 VSOCK: 211 + 发送到本地套接字的数据路径与非本地套接字相同。 212 + 213 + 测试 214 + ==== 215 + 216 + 更具体的示例代码可以在内核源码的 tools/testing/selftests/net/msg_zerocopy.c 中找到。 217 + 218 + 要留意环回约束问题。该测试可以在一对主机之间进行。但如果是在本地的一对进程之间运行,例如当使用 219 + msg_zerocopy.sh 脚本在跨命名空间的虚拟以太网(veth)对之间运行时,测试将不会显示出任何性能 220 + 提升。为了便于测试,可以通过让 skb_orphan_frags_rx 与 skb_orphan_frags 相同,来暂时放宽 221 + 环回限制。 222 + 223 + 对于 VSOCK 类型套接字的示例可以在 tools/testing/vsock/vsock_test_zerocopy.c 中找到。
+6 -7
Documentation/userspace-api/ioctl/ioctl-number.rst
··· 28 28 many drivers share a partial letter with other drivers. 29 29 30 30 If you are writing a driver for a new device and need a letter, pick an 31 - unused block with enough room for expansion: 32 to 256 ioctl commands. 32 - You can register the block by patching this file and submitting the 33 - patch to Linus Torvalds. Or you can e-mail me at <mec@shout.net> and 34 - I'll register one for you. 31 + unused block with enough room for expansion: 32 to 256 ioctl commands 32 + should suffice. You can register the block by patching this file and 33 + submitting the patch through :doc:`usual patch submission process 34 + </process/submitting-patches>`. 35 35 36 36 The second argument to _IO, _IOW, _IOR, or _IOWR is a sequence number 37 37 to distinguish ioctls from each other. The third argument to _IOW, ··· 62 62 (5) When following the convention, the driver code can use generic 63 63 code to copy the parameters between user and kernel space. 64 64 65 - This table lists ioctls visible from user land for Linux/x86. It contains 66 - most drivers up to 2.6.31, but I know I am missing some. There has been 67 - no attempt to list non-X86 architectures or ioctls from drivers/staging/. 65 + This table lists ioctls visible from userland, excluding ones from 66 + drivers/staging/. 68 67 69 68 ==== ===== ======================================================= ================================================================ 70 69 Code Seq# Include File Comments
+5 -2
MAINTAINERS
··· 5510 5510 5511 5511 CHINESE DOCUMENTATION 5512 5512 M: Alex Shi <alexs@kernel.org> 5513 - M: Yanteng Si <siyanteng@loongson.cn> 5513 + M: Yanteng Si <si.yanteng@linux.dev> 5514 5514 R: Dongliang Mu <dzm91@hust.edu.cn> 5515 5515 T: git git://git.kernel.org/pub/scm/linux/kernel/git/alexs/linux.git 5516 5516 S: Maintained ··· 7088 7088 F: Documentation/ 7089 7089 F: scripts/check-variable-fonts.sh 7090 7090 F: scripts/documentation-file-ref-check 7091 - F: scripts/kernel-doc 7091 + F: scripts/get_abi.py 7092 + F: scripts/kernel-doc* 7093 + F: scripts/lib/abi/* 7094 + F: scripts/lib/kdoc/* 7092 7095 F: scripts/sphinx-pre-install 7093 7096 X: Documentation/ABI/ 7094 7097 X: Documentation/admin-guide/media/
+5
Makefile
··· 458 458 HOSTRUSTC = rustc 459 459 HOSTPKG_CONFIG = pkg-config 460 460 461 + # the KERNELDOC macro needs to be exported, as scripts/Makefile.build 462 + # has a logic to call it 463 + KERNELDOC = $(srctree)/scripts/kernel-doc.py 464 + export KERNELDOC 465 + 461 466 KBUILD_USERHOSTCFLAGS := -Wall -Wmissing-prototypes -Wstrict-prototypes \ 462 467 -O2 -fomit-frame-pointer -std=gnu11 463 468 KBUILD_USERCFLAGS := $(KBUILD_USERHOSTCFLAGS) $(USERCFLAGS)
+1 -1
drivers/gpu/drm/Makefile
··· 236 236 quiet_cmd_hdrtest = HDRTEST $(patsubst %.hdrtest,%.h,$@) 237 237 cmd_hdrtest = \ 238 238 $(CC) $(c_flags) -fsyntax-only -x c /dev/null -include $< -include $<; \ 239 - $(srctree)/scripts/kernel-doc -none $(if $(CONFIG_WERROR)$(CONFIG_DRM_WERROR),-Werror) $<; \ 239 + PYTHONDONTWRITEBYTECODE=1 $(KERNELDOC) -none $(if $(CONFIG_WERROR)$(CONFIG_DRM_WERROR),-Werror) $<; \ 240 240 touch $@ 241 241 242 242 $(obj)/%.hdrtest: $(src)/%.h FORCE
+1 -1
drivers/gpu/drm/i915/Makefile
··· 408 408 # 409 409 # Enable locally for CONFIG_DRM_I915_WERROR=y. See also scripts/Makefile.build 410 410 ifdef CONFIG_DRM_I915_WERROR 411 - cmd_checkdoc = $(srctree)/scripts/kernel-doc -none -Werror $< 411 + cmd_checkdoc = PYTHONDONTWRITEBYTECODE=1 $(KERNELDOC) -none -Werror $< 412 412 endif 413 413 414 414 # header test
+1 -1
include/drm/Makefile
··· 11 11 quiet_cmd_hdrtest = HDRTEST $(patsubst %.hdrtest,%.h,$@) 12 12 cmd_hdrtest = \ 13 13 $(CC) $(c_flags) -fsyntax-only -x c /dev/null -include $< -include $<; \ 14 - $(srctree)/scripts/kernel-doc -none $(if $(CONFIG_WERROR)$(CONFIG_DRM_WERROR),-Werror) $<; \ 14 + PYTHONDONTWRITEBYTECODE=1 $(KERNELDOC) -none $(if $(CONFIG_WERROR)$(CONFIG_DRM_WERROR),-Werror) $<; \ 15 15 touch $@ 16 16 17 17 $(obj)/%.hdrtest: $(src)/%.h FORCE
+1 -1
scripts/Makefile.build
··· 83 83 endif 84 84 85 85 ifneq ($(KBUILD_EXTRA_WARN),) 86 - cmd_checkdoc = $(srctree)/scripts/kernel-doc -none $(KDOCFLAGS) \ 86 + cmd_checkdoc = PYTHONDONTWRITEBYTECODE=1 $(KERNELDOC) -none $(KDOCFLAGS) \ 87 87 $(if $(findstring 2, $(KBUILD_EXTRA_WARN)), -Wall) \ 88 88 $< 89 89 endif
+1 -1
scripts/find-unused-docs.sh
··· 54 54 if [[ ${FILES_INCLUDED[$file]+_} ]]; then 55 55 continue; 56 56 fi 57 - str=$(scripts/kernel-doc -export "$file" 2>/dev/null) 57 + str=$(PYTHONDONTWRITEBYTECODE=1 scripts/kernel-doc -export "$file" 2>/dev/null) 58 58 if [[ -n "$str" ]]; then 59 59 echo "$file" 60 60 fi
+2439
scripts/kernel-doc.pl
··· 1 + #!/usr/bin/env perl 2 + # SPDX-License-Identifier: GPL-2.0 3 + # vim: softtabstop=4 4 + 5 + use warnings; 6 + use strict; 7 + 8 + ## Copyright (c) 1998 Michael Zucchi, All Rights Reserved ## 9 + ## Copyright (C) 2000, 1 Tim Waugh <twaugh@redhat.com> ## 10 + ## Copyright (C) 2001 Simon Huggins ## 11 + ## Copyright (C) 2005-2012 Randy Dunlap ## 12 + ## Copyright (C) 2012 Dan Luedtke ## 13 + ## ## 14 + ## #define enhancements by Armin Kuster <akuster@mvista.com> ## 15 + ## Copyright (c) 2000 MontaVista Software, Inc. ## 16 + # 17 + # Copyright (C) 2022 Tomasz Warniełło (POD) 18 + 19 + use Pod::Usage qw/pod2usage/; 20 + 21 + =head1 NAME 22 + 23 + kernel-doc - Print formatted kernel documentation to stdout 24 + 25 + =head1 SYNOPSIS 26 + 27 + kernel-doc [-h] [-v] [-Werror] [-Wall] [-Wreturn] [-Wshort-desc[ription]] [-Wcontents-before-sections] 28 + [ -man | 29 + -rst [-enable-lineno] | 30 + -none 31 + ] 32 + [ 33 + -export | 34 + -internal | 35 + [-function NAME] ... | 36 + [-nosymbol NAME] ... 37 + ] 38 + [-no-doc-sections] 39 + [-export-file FILE] ... 40 + FILE ... 41 + 42 + Run `kernel-doc -h` for details. 43 + 44 + =head1 DESCRIPTION 45 + 46 + Read C language source or header FILEs, extract embedded documentation comments, 47 + and print formatted documentation to standard output. 48 + 49 + The documentation comments are identified by the "/**" opening comment mark. 50 + 51 + See Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax. 52 + 53 + =cut 54 + 55 + # more perldoc at the end of the file 56 + 57 + ## init lots of data 58 + 59 + my $errors = 0; 60 + my $warnings = 0; 61 + my $anon_struct_union = 0; 62 + 63 + # match expressions used to find embedded type information 64 + my $type_constant = '\b``([^\`]+)``\b'; 65 + my $type_constant2 = '\%([-_*\w]+)'; 66 + my $type_func = '(\w+)\(\)'; 67 + my $type_param = '\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)'; 68 + my $type_param_ref = '([\!~\*]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)'; 69 + my $type_fp_param = '\@(\w+)\(\)'; # Special RST handling for func ptr params 70 + my $type_fp_param2 = '\@(\w+->\S+)\(\)'; # Special RST handling for structs with func ptr params 71 + my $type_env = '(\$\w+)'; 72 + my $type_enum = '\&(enum\s*([_\w]+))'; 73 + my $type_struct = '\&(struct\s*([_\w]+))'; 74 + my $type_typedef = '\&(typedef\s*([_\w]+))'; 75 + my $type_union = '\&(union\s*([_\w]+))'; 76 + my $type_member = '\&([_\w]+)(\.|->)([_\w]+)'; 77 + my $type_fallback = '\&([_\w]+)'; 78 + my $type_member_func = $type_member . '\(\)'; 79 + 80 + # Output conversion substitutions. 81 + # One for each output format 82 + 83 + # these are pretty rough 84 + my @highlights_man = ( 85 + [$type_constant, "\$1"], 86 + [$type_constant2, "\$1"], 87 + [$type_func, "\\\\fB\$1\\\\fP"], 88 + [$type_enum, "\\\\fI\$1\\\\fP"], 89 + [$type_struct, "\\\\fI\$1\\\\fP"], 90 + [$type_typedef, "\\\\fI\$1\\\\fP"], 91 + [$type_union, "\\\\fI\$1\\\\fP"], 92 + [$type_param, "\\\\fI\$1\\\\fP"], 93 + [$type_param_ref, "\\\\fI\$1\$2\\\\fP"], 94 + [$type_member, "\\\\fI\$1\$2\$3\\\\fP"], 95 + [$type_fallback, "\\\\fI\$1\\\\fP"] 96 + ); 97 + my $blankline_man = ""; 98 + 99 + # rst-mode 100 + my @highlights_rst = ( 101 + [$type_constant, "``\$1``"], 102 + [$type_constant2, "``\$1``"], 103 + 104 + # Note: need to escape () to avoid func matching later 105 + [$type_member_func, "\\:c\\:type\\:`\$1\$2\$3\\\\(\\\\) <\$1>`"], 106 + [$type_member, "\\:c\\:type\\:`\$1\$2\$3 <\$1>`"], 107 + [$type_fp_param, "**\$1\\\\(\\\\)**"], 108 + [$type_fp_param2, "**\$1\\\\(\\\\)**"], 109 + [$type_func, "\$1()"], 110 + [$type_enum, "\\:c\\:type\\:`\$1 <\$2>`"], 111 + [$type_struct, "\\:c\\:type\\:`\$1 <\$2>`"], 112 + [$type_typedef, "\\:c\\:type\\:`\$1 <\$2>`"], 113 + [$type_union, "\\:c\\:type\\:`\$1 <\$2>`"], 114 + 115 + # in rst this can refer to any type 116 + [$type_fallback, "\\:c\\:type\\:`\$1`"], 117 + [$type_param_ref, "**\$1\$2**"] 118 + ); 119 + my $blankline_rst = "\n"; 120 + 121 + # read arguments 122 + if ($#ARGV == -1) { 123 + pod2usage( 124 + -message => "No arguments!\n", 125 + -exitval => 1, 126 + -verbose => 99, 127 + -sections => 'SYNOPSIS', 128 + -output => \*STDERR, 129 + ); 130 + } 131 + 132 + my $kernelversion; 133 + 134 + my $dohighlight = ""; 135 + 136 + my $verbose = 0; 137 + my $Werror = 0; 138 + my $Wreturn = 0; 139 + my $Wshort_desc = 0; 140 + my $output_mode = "rst"; 141 + my $output_preformatted = 0; 142 + my $no_doc_sections = 0; 143 + my $enable_lineno = 0; 144 + my @highlights = @highlights_rst; 145 + my $blankline = $blankline_rst; 146 + my $modulename = "Kernel API"; 147 + 148 + use constant { 149 + OUTPUT_ALL => 0, # output all symbols and doc sections 150 + OUTPUT_INCLUDE => 1, # output only specified symbols 151 + OUTPUT_EXPORTED => 2, # output exported symbols 152 + OUTPUT_INTERNAL => 3, # output non-exported symbols 153 + }; 154 + my $output_selection = OUTPUT_ALL; 155 + my $show_not_found = 0; # No longer used 156 + 157 + my @export_file_list; 158 + 159 + my @build_time; 160 + if (defined($ENV{'KBUILD_BUILD_TIMESTAMP'}) && 161 + (my $seconds = `date -d "${ENV{'KBUILD_BUILD_TIMESTAMP'}}" +%s`) ne '') { 162 + @build_time = gmtime($seconds); 163 + } else { 164 + @build_time = localtime; 165 + } 166 + 167 + my $man_date = ('January', 'February', 'March', 'April', 'May', 'June', 168 + 'July', 'August', 'September', 'October', 169 + 'November', 'December')[$build_time[4]] . 170 + " " . ($build_time[5]+1900); 171 + 172 + # Essentially these are globals. 173 + # They probably want to be tidied up, made more localised or something. 174 + # CAVEAT EMPTOR! Some of the others I localised may not want to be, which 175 + # could cause "use of undefined value" or other bugs. 176 + my ($function, %function_table, %parametertypes, $declaration_purpose); 177 + my %nosymbol_table = (); 178 + my $declaration_start_line; 179 + my ($type, $declaration_name, $return_type); 180 + my ($newsection, $newcontents, $prototype, $brcount); 181 + 182 + if (defined($ENV{'KBUILD_VERBOSE'}) && $ENV{'KBUILD_VERBOSE'} =~ '1') { 183 + $verbose = 1; 184 + } 185 + 186 + if (defined($ENV{'KCFLAGS'})) { 187 + my $kcflags = "$ENV{'KCFLAGS'}"; 188 + 189 + if ($kcflags =~ /(\s|^)-Werror(\s|$)/) { 190 + $Werror = 1; 191 + } 192 + } 193 + 194 + # reading this variable is for backwards compat just in case 195 + # someone was calling it with the variable from outside the 196 + # kernel's build system 197 + if (defined($ENV{'KDOC_WERROR'})) { 198 + $Werror = "$ENV{'KDOC_WERROR'}"; 199 + } 200 + # other environment variables are converted to command-line 201 + # arguments in cmd_checkdoc in the build system 202 + 203 + # Generated docbook code is inserted in a template at a point where 204 + # docbook v3.1 requires a non-zero sequence of RefEntry's; see: 205 + # https://www.oasis-open.org/docbook/documentation/reference/html/refentry.html 206 + # We keep track of number of generated entries and generate a dummy 207 + # if needs be to ensure the expanded template can be postprocessed 208 + # into html. 209 + my $section_counter = 0; 210 + 211 + my $lineprefix=""; 212 + 213 + # Parser states 214 + use constant { 215 + STATE_NORMAL => 0, # normal code 216 + STATE_NAME => 1, # looking for function name 217 + STATE_BODY_MAYBE => 2, # body - or maybe more description 218 + STATE_BODY => 3, # the body of the comment 219 + STATE_BODY_WITH_BLANK_LINE => 4, # the body, which has a blank line 220 + STATE_PROTO => 5, # scanning prototype 221 + STATE_DOCBLOCK => 6, # documentation block 222 + STATE_INLINE => 7, # gathering doc outside main block 223 + }; 224 + my $state; 225 + my $leading_space; 226 + 227 + # Inline documentation state 228 + use constant { 229 + STATE_INLINE_NA => 0, # not applicable ($state != STATE_INLINE) 230 + STATE_INLINE_NAME => 1, # looking for member name (@foo:) 231 + STATE_INLINE_TEXT => 2, # looking for member documentation 232 + STATE_INLINE_END => 3, # done 233 + STATE_INLINE_ERROR => 4, # error - Comment without header was found. 234 + # Spit a warning as it's not 235 + # proper kernel-doc and ignore the rest. 236 + }; 237 + my $inline_doc_state; 238 + 239 + #declaration types: can be 240 + # 'function', 'struct', 'union', 'enum', 'typedef' 241 + my $decl_type; 242 + 243 + # Name of the kernel-doc identifier for non-DOC markups 244 + my $identifier; 245 + 246 + my $doc_start = '^/\*\*\s*$'; # Allow whitespace at end of comment start. 247 + my $doc_end = '\*/'; 248 + my $doc_com = '\s*\*\s*'; 249 + my $doc_com_body = '\s*\* ?'; 250 + my $doc_decl = $doc_com . '(\w+)'; 251 + # @params and a strictly limited set of supported section names 252 + # Specifically: 253 + # Match @word: 254 + # @...: 255 + # @{section-name}: 256 + # while trying to not match literal block starts like "example::" 257 + # 258 + my $doc_sect = $doc_com . 259 + '\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:([^:].*)?$'; 260 + my $doc_content = $doc_com_body . '(.*)'; 261 + my $doc_block = $doc_com . 'DOC:\s*(.*)?'; 262 + my $doc_inline_start = '^\s*/\*\*\s*$'; 263 + my $doc_inline_sect = '\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)'; 264 + my $doc_inline_end = '^\s*\*/\s*$'; 265 + my $doc_inline_oneline = '^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$'; 266 + my $export_symbol = '^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*;'; 267 + my $export_symbol_ns = '^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+"\)\s*;'; 268 + my $function_pointer = qr{([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)}; 269 + my $attribute = qr{__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)}i; 270 + 271 + my %parameterdescs; 272 + my %parameterdesc_start_lines; 273 + my @parameterlist; 274 + my %sections; 275 + my @sectionlist; 276 + my %section_start_lines; 277 + my $sectcheck; 278 + my $struct_actual; 279 + 280 + my $contents = ""; 281 + my $new_start_line = 0; 282 + 283 + # the canonical section names. see also $doc_sect above. 284 + my $section_default = "Description"; # default section 285 + my $section_intro = "Introduction"; 286 + my $section = $section_default; 287 + my $section_context = "Context"; 288 + my $section_return = "Return"; 289 + 290 + my $undescribed = "-- undescribed --"; 291 + 292 + reset_state(); 293 + 294 + while ($ARGV[0] =~ m/^--?(.*)/) { 295 + my $cmd = $1; 296 + shift @ARGV; 297 + if ($cmd eq "man") { 298 + $output_mode = "man"; 299 + @highlights = @highlights_man; 300 + $blankline = $blankline_man; 301 + } elsif ($cmd eq "rst") { 302 + $output_mode = "rst"; 303 + @highlights = @highlights_rst; 304 + $blankline = $blankline_rst; 305 + } elsif ($cmd eq "none") { 306 + $output_mode = "none"; 307 + } elsif ($cmd eq "module") { # not needed for XML, inherits from calling document 308 + $modulename = shift @ARGV; 309 + } elsif ($cmd eq "function") { # to only output specific functions 310 + $output_selection = OUTPUT_INCLUDE; 311 + $function = shift @ARGV; 312 + $function_table{$function} = 1; 313 + } elsif ($cmd eq "nosymbol") { # Exclude specific symbols 314 + my $symbol = shift @ARGV; 315 + $nosymbol_table{$symbol} = 1; 316 + } elsif ($cmd eq "export") { # only exported symbols 317 + $output_selection = OUTPUT_EXPORTED; 318 + %function_table = (); 319 + } elsif ($cmd eq "internal") { # only non-exported symbols 320 + $output_selection = OUTPUT_INTERNAL; 321 + %function_table = (); 322 + } elsif ($cmd eq "export-file") { 323 + my $file = shift @ARGV; 324 + push(@export_file_list, $file); 325 + } elsif ($cmd eq "v") { 326 + $verbose = 1; 327 + } elsif ($cmd eq "Werror") { 328 + $Werror = 1; 329 + } elsif ($cmd eq "Wreturn") { 330 + $Wreturn = 1; 331 + } elsif ($cmd eq "Wshort-desc" or $cmd eq "Wshort-description") { 332 + $Wshort_desc = 1; 333 + } elsif ($cmd eq "Wall") { 334 + $Wreturn = 1; 335 + $Wshort_desc = 1; 336 + } elsif (($cmd eq "h") || ($cmd eq "help")) { 337 + pod2usage(-exitval => 0, -verbose => 2); 338 + } elsif ($cmd eq 'no-doc-sections') { 339 + $no_doc_sections = 1; 340 + } elsif ($cmd eq 'enable-lineno') { 341 + $enable_lineno = 1; 342 + } elsif ($cmd eq 'show-not-found') { 343 + $show_not_found = 1; # A no-op but don't fail 344 + } else { 345 + # Unknown argument 346 + pod2usage( 347 + -message => "Argument unknown!\n", 348 + -exitval => 1, 349 + -verbose => 99, 350 + -sections => 'SYNOPSIS', 351 + -output => \*STDERR, 352 + ); 353 + } 354 + if ($#ARGV < 0){ 355 + pod2usage( 356 + -message => "FILE argument missing\n", 357 + -exitval => 1, 358 + -verbose => 99, 359 + -sections => 'SYNOPSIS', 360 + -output => \*STDERR, 361 + ); 362 + } 363 + } 364 + 365 + # continue execution near EOF; 366 + 367 + sub findprog($) 368 + { 369 + foreach(split(/:/, $ENV{PATH})) { 370 + return "$_/$_[0]" if(-x "$_/$_[0]"); 371 + } 372 + } 373 + 374 + # get kernel version from env 375 + sub get_kernel_version() { 376 + my $version = 'unknown kernel version'; 377 + 378 + if (defined($ENV{'KERNELVERSION'})) { 379 + $version = $ENV{'KERNELVERSION'}; 380 + } 381 + return $version; 382 + } 383 + 384 + # 385 + sub print_lineno { 386 + my $lineno = shift; 387 + if ($enable_lineno && defined($lineno)) { 388 + print ".. LINENO " . $lineno . "\n"; 389 + } 390 + } 391 + 392 + sub emit_warning { 393 + my $location = shift; 394 + my $msg = shift; 395 + print STDERR "$location: warning: $msg"; 396 + ++$warnings; 397 + } 398 + ## 399 + # dumps section contents to arrays/hashes intended for that purpose. 400 + # 401 + sub dump_section { 402 + my $file = shift; 403 + my $name = shift; 404 + my $contents = join "\n", @_; 405 + 406 + if ($name =~ m/$type_param/) { 407 + $name = $1; 408 + $parameterdescs{$name} = $contents; 409 + $sectcheck = $sectcheck . $name . " "; 410 + $parameterdesc_start_lines{$name} = $new_start_line; 411 + $new_start_line = 0; 412 + } elsif ($name eq "@\.\.\.") { 413 + $name = "..."; 414 + $parameterdescs{$name} = $contents; 415 + $sectcheck = $sectcheck . $name . " "; 416 + $parameterdesc_start_lines{$name} = $new_start_line; 417 + $new_start_line = 0; 418 + } else { 419 + if (defined($sections{$name}) && ($sections{$name} ne "")) { 420 + # Only warn on user specified duplicate section names. 421 + if ($name ne $section_default) { 422 + emit_warning("${file}:$.", "duplicate section name '$name'\n"); 423 + } 424 + $sections{$name} .= $contents; 425 + } else { 426 + $sections{$name} = $contents; 427 + push @sectionlist, $name; 428 + $section_start_lines{$name} = $new_start_line; 429 + $new_start_line = 0; 430 + } 431 + } 432 + } 433 + 434 + ## 435 + # dump DOC: section after checking that it should go out 436 + # 437 + sub dump_doc_section { 438 + my $file = shift; 439 + my $name = shift; 440 + my $contents = join "\n", @_; 441 + 442 + if ($no_doc_sections) { 443 + return; 444 + } 445 + 446 + return if (defined($nosymbol_table{$name})); 447 + 448 + if (($output_selection == OUTPUT_ALL) || 449 + (($output_selection == OUTPUT_INCLUDE) && 450 + defined($function_table{$name}))) 451 + { 452 + dump_section($file, $name, $contents); 453 + output_blockhead({'sectionlist' => \@sectionlist, 454 + 'sections' => \%sections, 455 + 'module' => $modulename, 456 + 'content-only' => ($output_selection != OUTPUT_ALL), }); 457 + } 458 + } 459 + 460 + ## 461 + # output function 462 + # 463 + # parameterdescs, a hash. 464 + # function => "function name" 465 + # parameterlist => @list of parameters 466 + # parameterdescs => %parameter descriptions 467 + # sectionlist => @list of sections 468 + # sections => %section descriptions 469 + # 470 + 471 + sub output_highlight { 472 + my $contents = join "\n",@_; 473 + my $line; 474 + 475 + # DEBUG 476 + # if (!defined $contents) { 477 + # use Carp; 478 + # confess "output_highlight got called with no args?\n"; 479 + # } 480 + 481 + # print STDERR "contents b4:$contents\n"; 482 + eval $dohighlight; 483 + die $@ if $@; 484 + # print STDERR "contents af:$contents\n"; 485 + 486 + foreach $line (split "\n", $contents) { 487 + if (! $output_preformatted) { 488 + $line =~ s/^\s*//; 489 + } 490 + if ($line eq ""){ 491 + if (! $output_preformatted) { 492 + print $lineprefix, $blankline; 493 + } 494 + } else { 495 + if ($output_mode eq "man" && substr($line, 0, 1) eq ".") { 496 + print "\\&$line"; 497 + } else { 498 + print $lineprefix, $line; 499 + } 500 + } 501 + print "\n"; 502 + } 503 + } 504 + 505 + ## 506 + # output function in man 507 + sub output_function_man(%) { 508 + my %args = %{$_[0]}; 509 + my ($parameter, $section); 510 + my $count; 511 + my $func_macro = $args{'func_macro'}; 512 + my $paramcount = $#{$args{'parameterlist'}}; # -1 is empty 513 + 514 + print ".TH \"$args{'function'}\" 9 \"$args{'function'}\" \"$man_date\" \"Kernel Hacker's Manual\" LINUX\n"; 515 + 516 + print ".SH NAME\n"; 517 + print $args{'function'} . " \\- " . $args{'purpose'} . "\n"; 518 + 519 + print ".SH SYNOPSIS\n"; 520 + if ($args{'functiontype'} ne "") { 521 + print ".B \"" . $args{'functiontype'} . "\" " . $args{'function'} . "\n"; 522 + } else { 523 + print ".B \"" . $args{'function'} . "\n"; 524 + } 525 + $count = 0; 526 + my $parenth = "("; 527 + my $post = ","; 528 + foreach my $parameter (@{$args{'parameterlist'}}) { 529 + if ($count == $#{$args{'parameterlist'}}) { 530 + $post = ");"; 531 + } 532 + $type = $args{'parametertypes'}{$parameter}; 533 + if ($type =~ m/$function_pointer/) { 534 + # pointer-to-function 535 + print ".BI \"" . $parenth . $1 . "\" " . " \") (" . $2 . ")" . $post . "\"\n"; 536 + } else { 537 + $type =~ s/([^\*])$/$1 /; 538 + print ".BI \"" . $parenth . $type . "\" " . " \"" . $post . "\"\n"; 539 + } 540 + $count++; 541 + $parenth = ""; 542 + } 543 + 544 + $paramcount = $#{$args{'parameterlist'}}; # -1 is empty 545 + if ($paramcount >= 0) { 546 + print ".SH ARGUMENTS\n"; 547 + } 548 + foreach $parameter (@{$args{'parameterlist'}}) { 549 + my $parameter_name = $parameter; 550 + $parameter_name =~ s/\[.*//; 551 + 552 + print ".IP \"" . $parameter . "\" 12\n"; 553 + output_highlight($args{'parameterdescs'}{$parameter_name}); 554 + } 555 + foreach $section (@{$args{'sectionlist'}}) { 556 + print ".SH \"", uc $section, "\"\n"; 557 + output_highlight($args{'sections'}{$section}); 558 + } 559 + } 560 + 561 + ## 562 + # output enum in man 563 + sub output_enum_man(%) { 564 + my %args = %{$_[0]}; 565 + my ($parameter, $section); 566 + my $count; 567 + 568 + print ".TH \"$args{'module'}\" 9 \"enum $args{'enum'}\" \"$man_date\" \"API Manual\" LINUX\n"; 569 + 570 + print ".SH NAME\n"; 571 + print "enum " . $args{'enum'} . " \\- " . $args{'purpose'} . "\n"; 572 + 573 + print ".SH SYNOPSIS\n"; 574 + print "enum " . $args{'enum'} . " {\n"; 575 + $count = 0; 576 + foreach my $parameter (@{$args{'parameterlist'}}) { 577 + print ".br\n.BI \" $parameter\"\n"; 578 + if ($count == $#{$args{'parameterlist'}}) { 579 + print "\n};\n"; 580 + last; 581 + } else { 582 + print ", \n.br\n"; 583 + } 584 + $count++; 585 + } 586 + 587 + print ".SH Constants\n"; 588 + foreach $parameter (@{$args{'parameterlist'}}) { 589 + my $parameter_name = $parameter; 590 + $parameter_name =~ s/\[.*//; 591 + 592 + print ".IP \"" . $parameter . "\" 12\n"; 593 + output_highlight($args{'parameterdescs'}{$parameter_name}); 594 + } 595 + foreach $section (@{$args{'sectionlist'}}) { 596 + print ".SH \"$section\"\n"; 597 + output_highlight($args{'sections'}{$section}); 598 + } 599 + } 600 + 601 + ## 602 + # output struct in man 603 + sub output_struct_man(%) { 604 + my %args = %{$_[0]}; 605 + my ($parameter, $section); 606 + 607 + print ".TH \"$args{'module'}\" 9 \"" . $args{'type'} . " " . $args{'struct'} . "\" \"$man_date\" \"API Manual\" LINUX\n"; 608 + 609 + print ".SH NAME\n"; 610 + print $args{'type'} . " " . $args{'struct'} . " \\- " . $args{'purpose'} . "\n"; 611 + 612 + my $declaration = $args{'definition'}; 613 + $declaration =~ s/\t/ /g; 614 + $declaration =~ s/\n/"\n.br\n.BI \"/g; 615 + print ".SH SYNOPSIS\n"; 616 + print $args{'type'} . " " . $args{'struct'} . " {\n.br\n"; 617 + print ".BI \"$declaration\n};\n.br\n\n"; 618 + 619 + print ".SH Members\n"; 620 + foreach $parameter (@{$args{'parameterlist'}}) { 621 + ($parameter =~ /^#/) && next; 622 + 623 + my $parameter_name = $parameter; 624 + $parameter_name =~ s/\[.*//; 625 + 626 + ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; 627 + print ".IP \"" . $parameter . "\" 12\n"; 628 + output_highlight($args{'parameterdescs'}{$parameter_name}); 629 + } 630 + foreach $section (@{$args{'sectionlist'}}) { 631 + print ".SH \"$section\"\n"; 632 + output_highlight($args{'sections'}{$section}); 633 + } 634 + } 635 + 636 + ## 637 + # output typedef in man 638 + sub output_typedef_man(%) { 639 + my %args = %{$_[0]}; 640 + my ($parameter, $section); 641 + 642 + print ".TH \"$args{'module'}\" 9 \"$args{'typedef'}\" \"$man_date\" \"API Manual\" LINUX\n"; 643 + 644 + print ".SH NAME\n"; 645 + print "typedef " . $args{'typedef'} . " \\- " . $args{'purpose'} . "\n"; 646 + 647 + foreach $section (@{$args{'sectionlist'}}) { 648 + print ".SH \"$section\"\n"; 649 + output_highlight($args{'sections'}{$section}); 650 + } 651 + } 652 + 653 + sub output_blockhead_man(%) { 654 + my %args = %{$_[0]}; 655 + my ($parameter, $section); 656 + my $count; 657 + 658 + print ".TH \"$args{'module'}\" 9 \"$args{'module'}\" \"$man_date\" \"API Manual\" LINUX\n"; 659 + 660 + foreach $section (@{$args{'sectionlist'}}) { 661 + print ".SH \"$section\"\n"; 662 + output_highlight($args{'sections'}{$section}); 663 + } 664 + } 665 + 666 + ## 667 + # output in restructured text 668 + # 669 + 670 + # 671 + # This could use some work; it's used to output the DOC: sections, and 672 + # starts by putting out the name of the doc section itself, but that tends 673 + # to duplicate a header already in the template file. 674 + # 675 + sub output_blockhead_rst(%) { 676 + my %args = %{$_[0]}; 677 + my ($parameter, $section); 678 + 679 + foreach $section (@{$args{'sectionlist'}}) { 680 + next if (defined($nosymbol_table{$section})); 681 + 682 + if ($output_selection != OUTPUT_INCLUDE) { 683 + print ".. _$section:\n\n"; 684 + print "**$section**\n\n"; 685 + } 686 + print_lineno($section_start_lines{$section}); 687 + output_highlight_rst($args{'sections'}{$section}); 688 + print "\n"; 689 + } 690 + } 691 + 692 + # 693 + # Apply the RST highlights to a sub-block of text. 694 + # 695 + sub highlight_block($) { 696 + # The dohighlight kludge requires the text be called $contents 697 + my $contents = shift; 698 + eval $dohighlight; 699 + die $@ if $@; 700 + return $contents; 701 + } 702 + 703 + # 704 + # Regexes used only here. 705 + # 706 + my $sphinx_literal = '^[^.].*::$'; 707 + my $sphinx_cblock = '^\.\.\ +code-block::'; 708 + 709 + sub output_highlight_rst { 710 + my $input = join "\n",@_; 711 + my $output = ""; 712 + my $line; 713 + my $in_literal = 0; 714 + my $litprefix; 715 + my $block = ""; 716 + 717 + foreach $line (split "\n",$input) { 718 + # 719 + # If we're in a literal block, see if we should drop out 720 + # of it. Otherwise pass the line straight through unmunged. 721 + # 722 + if ($in_literal) { 723 + if (! ($line =~ /^\s*$/)) { 724 + # 725 + # If this is the first non-blank line in a literal 726 + # block we need to figure out what the proper indent is. 727 + # 728 + if ($litprefix eq "") { 729 + $line =~ /^(\s*)/; 730 + $litprefix = '^' . $1; 731 + $output .= $line . "\n"; 732 + } elsif (! ($line =~ /$litprefix/)) { 733 + $in_literal = 0; 734 + } else { 735 + $output .= $line . "\n"; 736 + } 737 + } else { 738 + $output .= $line . "\n"; 739 + } 740 + } 741 + # 742 + # Not in a literal block (or just dropped out) 743 + # 744 + if (! $in_literal) { 745 + $block .= $line . "\n"; 746 + if (($line =~ /$sphinx_literal/) || ($line =~ /$sphinx_cblock/)) { 747 + $in_literal = 1; 748 + $litprefix = ""; 749 + $output .= highlight_block($block); 750 + $block = "" 751 + } 752 + } 753 + } 754 + 755 + if ($block) { 756 + $output .= highlight_block($block); 757 + } 758 + 759 + $output =~ s/^\n+//g; 760 + $output =~ s/\n+$//g; 761 + 762 + foreach $line (split "\n", $output) { 763 + print $lineprefix . $line . "\n"; 764 + } 765 + } 766 + 767 + sub output_function_rst(%) { 768 + my %args = %{$_[0]}; 769 + my ($parameter, $section); 770 + my $oldprefix = $lineprefix; 771 + 772 + my $signature = ""; 773 + my $func_macro = $args{'func_macro'}; 774 + my $paramcount = $#{$args{'parameterlist'}}; # -1 is empty 775 + 776 + if ($func_macro) { 777 + $signature = $args{'function'}; 778 + } else { 779 + if ($args{'functiontype'}) { 780 + $signature = $args{'functiontype'} . " "; 781 + } 782 + $signature .= $args{'function'} . " ("; 783 + } 784 + 785 + my $count = 0; 786 + foreach my $parameter (@{$args{'parameterlist'}}) { 787 + if ($count ne 0) { 788 + $signature .= ", "; 789 + } 790 + $count++; 791 + $type = $args{'parametertypes'}{$parameter}; 792 + 793 + if ($type =~ m/$function_pointer/) { 794 + # pointer-to-function 795 + $signature .= $1 . $parameter . ") (" . $2 . ")"; 796 + } else { 797 + $signature .= $type; 798 + } 799 + } 800 + 801 + if (!$func_macro) { 802 + $signature .= ")"; 803 + } 804 + 805 + if ($args{'typedef'} || $args{'functiontype'} eq "") { 806 + print ".. c:macro:: ". $args{'function'} . "\n\n"; 807 + 808 + if ($args{'typedef'}) { 809 + print_lineno($declaration_start_line); 810 + print " **Typedef**: "; 811 + $lineprefix = ""; 812 + output_highlight_rst($args{'purpose'}); 813 + print "\n\n**Syntax**\n\n"; 814 + print " ``$signature``\n\n"; 815 + } else { 816 + print "``$signature``\n\n"; 817 + } 818 + } else { 819 + print ".. c:function:: $signature\n\n"; 820 + } 821 + 822 + if (!$args{'typedef'}) { 823 + print_lineno($declaration_start_line); 824 + $lineprefix = " "; 825 + output_highlight_rst($args{'purpose'}); 826 + print "\n"; 827 + } 828 + 829 + # 830 + # Put our descriptive text into a container (thus an HTML <div>) to help 831 + # set the function prototypes apart. 832 + # 833 + $lineprefix = " "; 834 + if ($paramcount >= 0) { 835 + print ".. container:: kernelindent\n\n"; 836 + print $lineprefix . "**Parameters**\n\n"; 837 + } 838 + foreach $parameter (@{$args{'parameterlist'}}) { 839 + my $parameter_name = $parameter; 840 + $parameter_name =~ s/\[.*//; 841 + $type = $args{'parametertypes'}{$parameter}; 842 + 843 + if ($type ne "") { 844 + print $lineprefix . "``$type``\n"; 845 + } else { 846 + print $lineprefix . "``$parameter``\n"; 847 + } 848 + 849 + print_lineno($parameterdesc_start_lines{$parameter_name}); 850 + 851 + $lineprefix = " "; 852 + if (defined($args{'parameterdescs'}{$parameter_name}) && 853 + $args{'parameterdescs'}{$parameter_name} ne $undescribed) { 854 + output_highlight_rst($args{'parameterdescs'}{$parameter_name}); 855 + } else { 856 + print $lineprefix . "*undescribed*\n"; 857 + } 858 + $lineprefix = " "; 859 + print "\n"; 860 + } 861 + 862 + output_section_rst(@_); 863 + $lineprefix = $oldprefix; 864 + } 865 + 866 + sub output_section_rst(%) { 867 + my %args = %{$_[0]}; 868 + my $section; 869 + my $oldprefix = $lineprefix; 870 + 871 + foreach $section (@{$args{'sectionlist'}}) { 872 + print $lineprefix . "**$section**\n\n"; 873 + print_lineno($section_start_lines{$section}); 874 + output_highlight_rst($args{'sections'}{$section}); 875 + print "\n"; 876 + } 877 + print "\n"; 878 + } 879 + 880 + sub output_enum_rst(%) { 881 + my %args = %{$_[0]}; 882 + my ($parameter); 883 + my $oldprefix = $lineprefix; 884 + my $count; 885 + my $outer; 886 + 887 + my $name = $args{'enum'}; 888 + print "\n\n.. c:enum:: " . $name . "\n\n"; 889 + 890 + print_lineno($declaration_start_line); 891 + $lineprefix = " "; 892 + output_highlight_rst($args{'purpose'}); 893 + print "\n"; 894 + 895 + print ".. container:: kernelindent\n\n"; 896 + $outer = $lineprefix . " "; 897 + $lineprefix = $outer . " "; 898 + print $outer . "**Constants**\n\n"; 899 + foreach $parameter (@{$args{'parameterlist'}}) { 900 + print $outer . "``$parameter``\n"; 901 + 902 + if ($args{'parameterdescs'}{$parameter} ne $undescribed) { 903 + output_highlight_rst($args{'parameterdescs'}{$parameter}); 904 + } else { 905 + print $lineprefix . "*undescribed*\n"; 906 + } 907 + print "\n"; 908 + } 909 + print "\n"; 910 + $lineprefix = $oldprefix; 911 + output_section_rst(@_); 912 + } 913 + 914 + sub output_typedef_rst(%) { 915 + my %args = %{$_[0]}; 916 + my ($parameter); 917 + my $oldprefix = $lineprefix; 918 + my $name; 919 + 920 + $name = $args{'typedef'}; 921 + 922 + print "\n\n.. c:type:: " . $name . "\n\n"; 923 + print_lineno($declaration_start_line); 924 + $lineprefix = " "; 925 + output_highlight_rst($args{'purpose'}); 926 + print "\n"; 927 + 928 + $lineprefix = $oldprefix; 929 + output_section_rst(@_); 930 + } 931 + 932 + sub output_struct_rst(%) { 933 + my %args = %{$_[0]}; 934 + my ($parameter); 935 + my $oldprefix = $lineprefix; 936 + 937 + my $name = $args{'struct'}; 938 + if ($args{'type'} eq 'union') { 939 + print "\n\n.. c:union:: " . $name . "\n\n"; 940 + } else { 941 + print "\n\n.. c:struct:: " . $name . "\n\n"; 942 + } 943 + 944 + print_lineno($declaration_start_line); 945 + $lineprefix = " "; 946 + output_highlight_rst($args{'purpose'}); 947 + print "\n"; 948 + 949 + print ".. container:: kernelindent\n\n"; 950 + print $lineprefix . "**Definition**::\n\n"; 951 + my $declaration = $args{'definition'}; 952 + $lineprefix = $lineprefix . " "; 953 + $declaration =~ s/\t/$lineprefix/g; 954 + print $lineprefix . $args{'type'} . " " . $args{'struct'} . " {\n$declaration" . $lineprefix . "};\n\n"; 955 + 956 + $lineprefix = " "; 957 + print $lineprefix . "**Members**\n\n"; 958 + foreach $parameter (@{$args{'parameterlist'}}) { 959 + ($parameter =~ /^#/) && next; 960 + 961 + my $parameter_name = $parameter; 962 + $parameter_name =~ s/\[.*//; 963 + 964 + ($args{'parameterdescs'}{$parameter_name} ne $undescribed) || next; 965 + $type = $args{'parametertypes'}{$parameter}; 966 + print_lineno($parameterdesc_start_lines{$parameter_name}); 967 + print $lineprefix . "``" . $parameter . "``\n"; 968 + $lineprefix = " "; 969 + output_highlight_rst($args{'parameterdescs'}{$parameter_name}); 970 + $lineprefix = " "; 971 + print "\n"; 972 + } 973 + print "\n"; 974 + 975 + $lineprefix = $oldprefix; 976 + output_section_rst(@_); 977 + } 978 + 979 + ## none mode output functions 980 + 981 + sub output_function_none(%) { 982 + } 983 + 984 + sub output_enum_none(%) { 985 + } 986 + 987 + sub output_typedef_none(%) { 988 + } 989 + 990 + sub output_struct_none(%) { 991 + } 992 + 993 + sub output_blockhead_none(%) { 994 + } 995 + 996 + ## 997 + # generic output function for all types (function, struct/union, typedef, enum); 998 + # calls the generated, variable output_ function name based on 999 + # functype and output_mode 1000 + sub output_declaration { 1001 + no strict 'refs'; 1002 + my $name = shift; 1003 + my $functype = shift; 1004 + my $func = "output_${functype}_$output_mode"; 1005 + 1006 + return if (defined($nosymbol_table{$name})); 1007 + 1008 + if (($output_selection == OUTPUT_ALL) || 1009 + (($output_selection == OUTPUT_INCLUDE || 1010 + $output_selection == OUTPUT_EXPORTED) && 1011 + defined($function_table{$name})) || 1012 + ($output_selection == OUTPUT_INTERNAL && 1013 + !($functype eq "function" && defined($function_table{$name})))) 1014 + { 1015 + &$func(@_); 1016 + $section_counter++; 1017 + } 1018 + } 1019 + 1020 + ## 1021 + # generic output function - calls the right one based on current output mode. 1022 + sub output_blockhead { 1023 + no strict 'refs'; 1024 + my $func = "output_blockhead_" . $output_mode; 1025 + &$func(@_); 1026 + $section_counter++; 1027 + } 1028 + 1029 + ## 1030 + # takes a declaration (struct, union, enum, typedef) and 1031 + # invokes the right handler. NOT called for functions. 1032 + sub dump_declaration($$) { 1033 + no strict 'refs'; 1034 + my ($prototype, $file) = @_; 1035 + my $func = "dump_" . $decl_type; 1036 + &$func(@_); 1037 + } 1038 + 1039 + sub dump_union($$) { 1040 + dump_struct(@_); 1041 + } 1042 + 1043 + sub dump_struct($$) { 1044 + my $x = shift; 1045 + my $file = shift; 1046 + my $decl_type; 1047 + my $members; 1048 + my $type = qr{struct|union}; 1049 + # For capturing struct/union definition body, i.e. "{members*}qualifiers*" 1050 + my $qualifiers = qr{$attribute|__packed|__aligned|____cacheline_aligned_in_smp|____cacheline_aligned}; 1051 + my $definition_body = qr{\{(.*)\}\s*$qualifiers*}; 1052 + my $struct_members = qr{($type)([^\{\};]+)\{([^\{\}]*)\}([^\{\}\;]*)\;}; 1053 + 1054 + if ($x =~ /($type)\s+(\w+)\s*$definition_body/) { 1055 + $decl_type = $1; 1056 + $declaration_name = $2; 1057 + $members = $3; 1058 + } elsif ($x =~ /typedef\s+($type)\s*$definition_body\s*(\w+)\s*;/) { 1059 + $decl_type = $1; 1060 + $declaration_name = $3; 1061 + $members = $2; 1062 + } 1063 + 1064 + if ($members) { 1065 + if ($identifier ne $declaration_name) { 1066 + emit_warning("${file}:$.", "expecting prototype for $decl_type $identifier. Prototype was for $decl_type $declaration_name instead\n"); 1067 + return; 1068 + } 1069 + 1070 + # ignore members marked private: 1071 + $members =~ s/\/\*\s*private:.*?\/\*\s*public:.*?\*\///gosi; 1072 + $members =~ s/\/\*\s*private:.*//gosi; 1073 + # strip comments: 1074 + $members =~ s/\/\*.*?\*\///gos; 1075 + # strip attributes 1076 + $members =~ s/\s*$attribute/ /gi; 1077 + $members =~ s/\s*__aligned\s*\([^;]*\)/ /gos; 1078 + $members =~ s/\s*__counted_by\s*\([^;]*\)/ /gos; 1079 + $members =~ s/\s*__counted_by_(le|be)\s*\([^;]*\)/ /gos; 1080 + $members =~ s/\s*__packed\s*/ /gos; 1081 + $members =~ s/\s*CRYPTO_MINALIGN_ATTR/ /gos; 1082 + $members =~ s/\s*____cacheline_aligned_in_smp/ /gos; 1083 + $members =~ s/\s*____cacheline_aligned/ /gos; 1084 + # unwrap struct_group(): 1085 + # - first eat non-declaration parameters and rewrite for final match 1086 + # - then remove macro, outer parens, and trailing semicolon 1087 + $members =~ s/\bstruct_group\s*\(([^,]*,)/STRUCT_GROUP(/gos; 1088 + $members =~ s/\bstruct_group_attr\s*\(([^,]*,){2}/STRUCT_GROUP(/gos; 1089 + $members =~ s/\bstruct_group_tagged\s*\(([^,]*),([^,]*),/struct $1 $2; STRUCT_GROUP(/gos; 1090 + $members =~ s/\b__struct_group\s*\(([^,]*,){3}/STRUCT_GROUP(/gos; 1091 + $members =~ s/\bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*;/$2/gos; 1092 + 1093 + my $args = qr{([^,)]+)}; 1094 + # replace DECLARE_BITMAP 1095 + $members =~ s/__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)/DECLARE_BITMAP($1, __ETHTOOL_LINK_MODE_MASK_NBITS)/gos; 1096 + $members =~ s/DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)/DECLARE_BITMAP($1, PHY_INTERFACE_MODE_MAX)/gos; 1097 + $members =~ s/DECLARE_BITMAP\s*\($args,\s*$args\)/unsigned long $1\[BITS_TO_LONGS($2)\]/gos; 1098 + # replace DECLARE_HASHTABLE 1099 + $members =~ s/DECLARE_HASHTABLE\s*\($args,\s*$args\)/unsigned long $1\[1 << (($2) - 1)\]/gos; 1100 + # replace DECLARE_KFIFO 1101 + $members =~ s/DECLARE_KFIFO\s*\($args,\s*$args,\s*$args\)/$2 \*$1/gos; 1102 + # replace DECLARE_KFIFO_PTR 1103 + $members =~ s/DECLARE_KFIFO_PTR\s*\($args,\s*$args\)/$2 \*$1/gos; 1104 + # replace DECLARE_FLEX_ARRAY 1105 + $members =~ s/(?:__)?DECLARE_FLEX_ARRAY\s*\($args,\s*$args\)/$1 $2\[\]/gos; 1106 + #replace DEFINE_DMA_UNMAP_ADDR 1107 + $members =~ s/DEFINE_DMA_UNMAP_ADDR\s*\($args\)/dma_addr_t $1/gos; 1108 + #replace DEFINE_DMA_UNMAP_LEN 1109 + $members =~ s/DEFINE_DMA_UNMAP_LEN\s*\($args\)/__u32 $1/gos; 1110 + my $declaration = $members; 1111 + 1112 + # Split nested struct/union elements as newer ones 1113 + while ($members =~ m/$struct_members/) { 1114 + my $newmember; 1115 + my $maintype = $1; 1116 + my $ids = $4; 1117 + my $content = $3; 1118 + foreach my $id(split /,/, $ids) { 1119 + $newmember .= "$maintype $id; "; 1120 + 1121 + $id =~ s/[:\[].*//; 1122 + $id =~ s/^\s*\**(\S+)\s*/$1/; 1123 + foreach my $arg (split /;/, $content) { 1124 + next if ($arg =~ m/^\s*$/); 1125 + if ($arg =~ m/^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)/) { 1126 + # pointer-to-function 1127 + my $type = $1; 1128 + my $name = $2; 1129 + my $extra = $3; 1130 + next if (!$name); 1131 + if ($id =~ m/^\s*$/) { 1132 + # anonymous struct/union 1133 + $newmember .= "$type$name$extra; "; 1134 + } else { 1135 + $newmember .= "$type$id.$name$extra; "; 1136 + } 1137 + } else { 1138 + my $type; 1139 + my $names; 1140 + $arg =~ s/^\s+//; 1141 + $arg =~ s/\s+$//; 1142 + # Handle bitmaps 1143 + $arg =~ s/:\s*\d+\s*//g; 1144 + # Handle arrays 1145 + $arg =~ s/\[.*\]//g; 1146 + # The type may have multiple words, 1147 + # and multiple IDs can be defined, like: 1148 + # const struct foo, *bar, foobar 1149 + # So, we remove spaces when parsing the 1150 + # names, in order to match just names 1151 + # and commas for the names 1152 + $arg =~ s/\s*,\s*/,/g; 1153 + if ($arg =~ m/(.*)\s+([\S+,]+)/) { 1154 + $type = $1; 1155 + $names = $2; 1156 + } else { 1157 + $newmember .= "$arg; "; 1158 + next; 1159 + } 1160 + foreach my $name (split /,/, $names) { 1161 + $name =~ s/^\s*\**(\S+)\s*/$1/; 1162 + next if (($name =~ m/^\s*$/)); 1163 + if ($id =~ m/^\s*$/) { 1164 + # anonymous struct/union 1165 + $newmember .= "$type $name; "; 1166 + } else { 1167 + $newmember .= "$type $id.$name; "; 1168 + } 1169 + } 1170 + } 1171 + } 1172 + } 1173 + $members =~ s/$struct_members/$newmember/; 1174 + } 1175 + 1176 + # Ignore other nested elements, like enums 1177 + $members =~ s/(\{[^\{\}]*\})//g; 1178 + 1179 + create_parameterlist($members, ';', $file, $declaration_name); 1180 + check_sections($file, $declaration_name, $decl_type, $sectcheck, $struct_actual); 1181 + 1182 + # Adjust declaration for better display 1183 + $declaration =~ s/([\{;])/$1\n/g; 1184 + $declaration =~ s/\}\s+;/};/g; 1185 + # Better handle inlined enums 1186 + do {} while ($declaration =~ s/(enum\s+\{[^\}]+),([^\n])/$1,\n$2/); 1187 + 1188 + my @def_args = split /\n/, $declaration; 1189 + my $level = 1; 1190 + $declaration = ""; 1191 + foreach my $clause (@def_args) { 1192 + $clause =~ s/^\s+//; 1193 + $clause =~ s/\s+$//; 1194 + $clause =~ s/\s+/ /; 1195 + next if (!$clause); 1196 + $level-- if ($clause =~ m/(\})/ && $level > 1); 1197 + if (!($clause =~ m/^\s*#/)) { 1198 + $declaration .= "\t" x $level; 1199 + } 1200 + $declaration .= "\t" . $clause . "\n"; 1201 + $level++ if ($clause =~ m/(\{)/ && !($clause =~m/\}/)); 1202 + } 1203 + output_declaration($declaration_name, 1204 + 'struct', 1205 + {'struct' => $declaration_name, 1206 + 'module' => $modulename, 1207 + 'definition' => $declaration, 1208 + 'parameterlist' => \@parameterlist, 1209 + 'parameterdescs' => \%parameterdescs, 1210 + 'parametertypes' => \%parametertypes, 1211 + 'sectionlist' => \@sectionlist, 1212 + 'sections' => \%sections, 1213 + 'purpose' => $declaration_purpose, 1214 + 'type' => $decl_type 1215 + }); 1216 + } else { 1217 + print STDERR "${file}:$.: error: Cannot parse struct or union!\n"; 1218 + ++$errors; 1219 + } 1220 + } 1221 + 1222 + 1223 + sub show_warnings($$) { 1224 + my $functype = shift; 1225 + my $name = shift; 1226 + 1227 + return 0 if (defined($nosymbol_table{$name})); 1228 + 1229 + return 1 if ($output_selection == OUTPUT_ALL); 1230 + 1231 + if ($output_selection == OUTPUT_EXPORTED) { 1232 + if (defined($function_table{$name})) { 1233 + return 1; 1234 + } else { 1235 + return 0; 1236 + } 1237 + } 1238 + if ($output_selection == OUTPUT_INTERNAL) { 1239 + if (!($functype eq "function" && defined($function_table{$name}))) { 1240 + return 1; 1241 + } else { 1242 + return 0; 1243 + } 1244 + } 1245 + if ($output_selection == OUTPUT_INCLUDE) { 1246 + if (defined($function_table{$name})) { 1247 + return 1; 1248 + } else { 1249 + return 0; 1250 + } 1251 + } 1252 + die("Please add the new output type at show_warnings()"); 1253 + } 1254 + 1255 + sub dump_enum($$) { 1256 + my $x = shift; 1257 + my $file = shift; 1258 + my $members; 1259 + 1260 + # ignore members marked private: 1261 + $x =~ s/\/\*\s*private:.*?\/\*\s*public:.*?\*\///gosi; 1262 + $x =~ s/\/\*\s*private:.*}/}/gosi; 1263 + 1264 + $x =~ s@/\*.*?\*/@@gos; # strip comments. 1265 + # strip #define macros inside enums 1266 + $x =~ s@#\s*((define|ifdef|if)\s+|endif)[^;]*;@@gos; 1267 + 1268 + if ($x =~ /typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;/) { 1269 + $declaration_name = $2; 1270 + $members = $1; 1271 + } elsif ($x =~ /enum\s+(\w*)\s*\{(.*)\}/) { 1272 + $declaration_name = $1; 1273 + $members = $2; 1274 + } 1275 + 1276 + if ($members) { 1277 + if ($identifier ne $declaration_name) { 1278 + if ($identifier eq "") { 1279 + emit_warning("${file}:$.", "wrong kernel-doc identifier on line:\n"); 1280 + } else { 1281 + emit_warning("${file}:$.", "expecting prototype for enum $identifier. Prototype was for enum $declaration_name instead\n"); 1282 + } 1283 + return; 1284 + } 1285 + $declaration_name = "(anonymous)" if ($declaration_name eq ""); 1286 + 1287 + my %_members; 1288 + 1289 + $members =~ s/\s+$//; 1290 + $members =~ s/\([^;]*?[\)]//g; 1291 + 1292 + foreach my $arg (split ',', $members) { 1293 + $arg =~ s/^\s*(\w+).*/$1/; 1294 + push @parameterlist, $arg; 1295 + if (!$parameterdescs{$arg}) { 1296 + $parameterdescs{$arg} = $undescribed; 1297 + if (show_warnings("enum", $declaration_name)) { 1298 + emit_warning("${file}:$.", "Enum value '$arg' not described in enum '$declaration_name'\n"); 1299 + } 1300 + } 1301 + $_members{$arg} = 1; 1302 + } 1303 + 1304 + while (my ($k, $v) = each %parameterdescs) { 1305 + if (!exists($_members{$k})) { 1306 + if (show_warnings("enum", $declaration_name)) { 1307 + emit_warning("${file}:$.", "Excess enum value '$k' description in '$declaration_name'\n"); 1308 + } 1309 + } 1310 + } 1311 + 1312 + output_declaration($declaration_name, 1313 + 'enum', 1314 + {'enum' => $declaration_name, 1315 + 'module' => $modulename, 1316 + 'parameterlist' => \@parameterlist, 1317 + 'parameterdescs' => \%parameterdescs, 1318 + 'sectionlist' => \@sectionlist, 1319 + 'sections' => \%sections, 1320 + 'purpose' => $declaration_purpose 1321 + }); 1322 + } else { 1323 + print STDERR "${file}:$.: error: Cannot parse enum!\n"; 1324 + ++$errors; 1325 + } 1326 + } 1327 + 1328 + my $typedef_type = qr { ((?:\s+[\w\*]+\b){0,7}\s+(?:\w+\b|\*+))\s* }x; 1329 + my $typedef_ident = qr { \*?\s*(\w\S+)\s* }x; 1330 + my $typedef_args = qr { \s*\((.*)\); }x; 1331 + 1332 + my $typedef1 = qr { typedef$typedef_type\($typedef_ident\)$typedef_args }x; 1333 + my $typedef2 = qr { typedef$typedef_type$typedef_ident$typedef_args }x; 1334 + 1335 + sub dump_typedef($$) { 1336 + my $x = shift; 1337 + my $file = shift; 1338 + 1339 + $x =~ s@/\*.*?\*/@@gos; # strip comments. 1340 + 1341 + # Parse function typedef prototypes 1342 + if ($x =~ $typedef1 || $x =~ $typedef2) { 1343 + $return_type = $1; 1344 + $declaration_name = $2; 1345 + my $args = $3; 1346 + $return_type =~ s/^\s+//; 1347 + 1348 + if ($identifier ne $declaration_name) { 1349 + emit_warning("${file}:$.", "expecting prototype for typedef $identifier. Prototype was for typedef $declaration_name instead\n"); 1350 + return; 1351 + } 1352 + 1353 + create_parameterlist($args, ',', $file, $declaration_name); 1354 + 1355 + output_declaration($declaration_name, 1356 + 'function', 1357 + {'function' => $declaration_name, 1358 + 'typedef' => 1, 1359 + 'module' => $modulename, 1360 + 'functiontype' => $return_type, 1361 + 'parameterlist' => \@parameterlist, 1362 + 'parameterdescs' => \%parameterdescs, 1363 + 'parametertypes' => \%parametertypes, 1364 + 'sectionlist' => \@sectionlist, 1365 + 'sections' => \%sections, 1366 + 'purpose' => $declaration_purpose 1367 + }); 1368 + return; 1369 + } 1370 + 1371 + while (($x =~ /\(*.\)\s*;$/) || ($x =~ /\[*.\]\s*;$/)) { 1372 + $x =~ s/\(*.\)\s*;$/;/; 1373 + $x =~ s/\[*.\]\s*;$/;/; 1374 + } 1375 + 1376 + if ($x =~ /typedef.*\s+(\w+)\s*;/) { 1377 + $declaration_name = $1; 1378 + 1379 + if ($identifier ne $declaration_name) { 1380 + emit_warning("${file}:$.", "expecting prototype for typedef $identifier. Prototype was for typedef $declaration_name instead\n"); 1381 + return; 1382 + } 1383 + 1384 + output_declaration($declaration_name, 1385 + 'typedef', 1386 + {'typedef' => $declaration_name, 1387 + 'module' => $modulename, 1388 + 'sectionlist' => \@sectionlist, 1389 + 'sections' => \%sections, 1390 + 'purpose' => $declaration_purpose 1391 + }); 1392 + } else { 1393 + print STDERR "${file}:$.: error: Cannot parse typedef!\n"; 1394 + ++$errors; 1395 + } 1396 + } 1397 + 1398 + sub save_struct_actual($) { 1399 + my $actual = shift; 1400 + 1401 + # strip all spaces from the actual param so that it looks like one string item 1402 + $actual =~ s/\s*//g; 1403 + $struct_actual = $struct_actual . $actual . " "; 1404 + } 1405 + 1406 + sub create_parameterlist($$$$) { 1407 + my $args = shift; 1408 + my $splitter = shift; 1409 + my $file = shift; 1410 + my $declaration_name = shift; 1411 + my $type; 1412 + my $param; 1413 + 1414 + # temporarily replace commas inside function pointer definition 1415 + my $arg_expr = qr{\([^\),]+}; 1416 + while ($args =~ /$arg_expr,/) { 1417 + $args =~ s/($arg_expr),/$1#/g; 1418 + } 1419 + 1420 + foreach my $arg (split($splitter, $args)) { 1421 + # strip comments 1422 + $arg =~ s/\/\*.*\*\///; 1423 + # ignore argument attributes 1424 + $arg =~ s/\sPOS0?\s/ /; 1425 + # strip leading/trailing spaces 1426 + $arg =~ s/^\s*//; 1427 + $arg =~ s/\s*$//; 1428 + $arg =~ s/\s+/ /; 1429 + 1430 + if ($arg =~ /^#/) { 1431 + # Treat preprocessor directive as a typeless variable just to fill 1432 + # corresponding data structures "correctly". Catch it later in 1433 + # output_* subs. 1434 + push_parameter($arg, "", "", $file); 1435 + } elsif ($arg =~ m/\(.+\)\s*\(/) { 1436 + # pointer-to-function 1437 + $arg =~ tr/#/,/; 1438 + $arg =~ m/[^\(]+\(\*?\s*([\w\[\]\.]*)\s*\)/; 1439 + $param = $1; 1440 + $type = $arg; 1441 + $type =~ s/([^\(]+\(\*?)\s*$param/$1/; 1442 + save_struct_actual($param); 1443 + push_parameter($param, $type, $arg, $file, $declaration_name); 1444 + } elsif ($arg =~ m/\(.+\)\s*\[/) { 1445 + # array-of-pointers 1446 + $arg =~ tr/#/,/; 1447 + $arg =~ m/[^\(]+\(\s*\*\s*([\w\[\]\.]*?)\s*(\s*\[\s*[\w]+\s*\]\s*)*\)/; 1448 + $param = $1; 1449 + $type = $arg; 1450 + $type =~ s/([^\(]+\(\*?)\s*$param/$1/; 1451 + save_struct_actual($param); 1452 + push_parameter($param, $type, $arg, $file, $declaration_name); 1453 + } elsif ($arg) { 1454 + $arg =~ s/\s*:\s*/:/g; 1455 + $arg =~ s/\s*\[/\[/g; 1456 + 1457 + my @args = split('\s*,\s*', $arg); 1458 + if ($args[0] =~ m/\*/) { 1459 + $args[0] =~ s/(\*+)\s*/ $1/; 1460 + } 1461 + 1462 + my @first_arg; 1463 + if ($args[0] =~ /^(.*\s+)(.*?\[.*\].*)$/) { 1464 + shift @args; 1465 + push(@first_arg, split('\s+', $1)); 1466 + push(@first_arg, $2); 1467 + } else { 1468 + @first_arg = split('\s+', shift @args); 1469 + } 1470 + 1471 + unshift(@args, pop @first_arg); 1472 + $type = join " ", @first_arg; 1473 + 1474 + foreach $param (@args) { 1475 + if ($param =~ m/^(\*+)\s*(.*)/) { 1476 + save_struct_actual($2); 1477 + 1478 + push_parameter($2, "$type $1", $arg, $file, $declaration_name); 1479 + } elsif ($param =~ m/(.*?):(\w+)/) { 1480 + if ($type ne "") { # skip unnamed bit-fields 1481 + save_struct_actual($1); 1482 + push_parameter($1, "$type:$2", $arg, $file, $declaration_name) 1483 + } 1484 + } else { 1485 + save_struct_actual($param); 1486 + push_parameter($param, $type, $arg, $file, $declaration_name); 1487 + } 1488 + } 1489 + } 1490 + } 1491 + } 1492 + 1493 + sub push_parameter($$$$$) { 1494 + my $param = shift; 1495 + my $type = shift; 1496 + my $org_arg = shift; 1497 + my $file = shift; 1498 + my $declaration_name = shift; 1499 + 1500 + if (($anon_struct_union == 1) && ($type eq "") && 1501 + ($param eq "}")) { 1502 + return; # ignore the ending }; from anon. struct/union 1503 + } 1504 + 1505 + $anon_struct_union = 0; 1506 + $param =~ s/[\[\)].*//; 1507 + 1508 + if ($type eq "" && $param =~ /\.\.\.$/) 1509 + { 1510 + if (!$param =~ /\w\.\.\.$/) { 1511 + # handles unnamed variable parameters 1512 + $param = "..."; 1513 + } elsif ($param =~ /\w\.\.\.$/) { 1514 + # for named variable parameters of the form `x...`, remove the dots 1515 + $param =~ s/\.\.\.$//; 1516 + } 1517 + if (!defined $parameterdescs{$param} || $parameterdescs{$param} eq "") { 1518 + $parameterdescs{$param} = "variable arguments"; 1519 + } 1520 + } 1521 + elsif ($type eq "" && ($param eq "" or $param eq "void")) 1522 + { 1523 + $param="void"; 1524 + $parameterdescs{void} = "no arguments"; 1525 + } 1526 + elsif ($type eq "" && ($param eq "struct" or $param eq "union")) 1527 + # handle unnamed (anonymous) union or struct: 1528 + { 1529 + $type = $param; 1530 + $param = "{unnamed_" . $param . "}"; 1531 + $parameterdescs{$param} = "anonymous\n"; 1532 + $anon_struct_union = 1; 1533 + } 1534 + elsif ($param =~ "__cacheline_group" ) 1535 + # handle cache group enforcing variables: they do not need be described in header files 1536 + { 1537 + return; # ignore __cacheline_group_begin and __cacheline_group_end 1538 + } 1539 + 1540 + # warn if parameter has no description 1541 + # (but ignore ones starting with # as these are not parameters 1542 + # but inline preprocessor statements); 1543 + # Note: It will also ignore void params and unnamed structs/unions 1544 + if (!defined $parameterdescs{$param} && $param !~ /^#/) { 1545 + $parameterdescs{$param} = $undescribed; 1546 + 1547 + if (show_warnings($type, $declaration_name) && $param !~ /\./) { 1548 + emit_warning("${file}:$.", "Function parameter or struct member '$param' not described in '$declaration_name'\n"); 1549 + } 1550 + } 1551 + 1552 + # strip spaces from $param so that it is one continuous string 1553 + # on @parameterlist; 1554 + # this fixes a problem where check_sections() cannot find 1555 + # a parameter like "addr[6 + 2]" because it actually appears 1556 + # as "addr[6", "+", "2]" on the parameter list; 1557 + # but it's better to maintain the param string unchanged for output, 1558 + # so just weaken the string compare in check_sections() to ignore 1559 + # "[blah" in a parameter string; 1560 + ###$param =~ s/\s*//g; 1561 + push @parameterlist, $param; 1562 + $org_arg =~ s/\s\s+/ /g; 1563 + $parametertypes{$param} = $org_arg; 1564 + } 1565 + 1566 + sub check_sections($$$$$) { 1567 + my ($file, $decl_name, $decl_type, $sectcheck, $prmscheck) = @_; 1568 + my @sects = split ' ', $sectcheck; 1569 + my @prms = split ' ', $prmscheck; 1570 + my $err; 1571 + my ($px, $sx); 1572 + my $prm_clean; # strip trailing "[array size]" and/or beginning "*" 1573 + 1574 + foreach $sx (0 .. $#sects) { 1575 + $err = 1; 1576 + foreach $px (0 .. $#prms) { 1577 + $prm_clean = $prms[$px]; 1578 + $prm_clean =~ s/\[.*\]//; 1579 + $prm_clean =~ s/$attribute//i; 1580 + # ignore array size in a parameter string; 1581 + # however, the original param string may contain 1582 + # spaces, e.g.: addr[6 + 2] 1583 + # and this appears in @prms as "addr[6" since the 1584 + # parameter list is split at spaces; 1585 + # hence just ignore "[..." for the sections check; 1586 + $prm_clean =~ s/\[.*//; 1587 + 1588 + ##$prm_clean =~ s/^\**//; 1589 + if ($prm_clean eq $sects[$sx]) { 1590 + $err = 0; 1591 + last; 1592 + } 1593 + } 1594 + if ($err) { 1595 + if ($decl_type eq "function") { 1596 + emit_warning("${file}:$.", 1597 + "Excess function parameter " . 1598 + "'$sects[$sx]' " . 1599 + "description in '$decl_name'\n"); 1600 + } elsif (($decl_type eq "struct") or 1601 + ($decl_type eq "union")) { 1602 + emit_warning("${file}:$.", 1603 + "Excess $decl_type member " . 1604 + "'$sects[$sx]' " . 1605 + "description in '$decl_name'\n"); 1606 + } 1607 + } 1608 + } 1609 + } 1610 + 1611 + ## 1612 + # Checks the section describing the return value of a function. 1613 + sub check_return_section { 1614 + my $file = shift; 1615 + my $declaration_name = shift; 1616 + my $return_type = shift; 1617 + 1618 + # Ignore an empty return type (It's a macro) 1619 + # Ignore functions with a "void" return type. (But don't ignore "void *") 1620 + if (($return_type eq "") || ($return_type =~ /void\s*\w*\s*$/)) { 1621 + return; 1622 + } 1623 + 1624 + if (!defined($sections{$section_return}) || 1625 + $sections{$section_return} eq "") 1626 + { 1627 + emit_warning("${file}:$.", 1628 + "No description found for return value of " . 1629 + "'$declaration_name'\n"); 1630 + } 1631 + } 1632 + 1633 + ## 1634 + # takes a function prototype and the name of the current file being 1635 + # processed and spits out all the details stored in the global 1636 + # arrays/hashes. 1637 + sub dump_function($$) { 1638 + my $prototype = shift; 1639 + my $file = shift; 1640 + my $func_macro = 0; 1641 + 1642 + print_lineno($new_start_line); 1643 + 1644 + $prototype =~ s/^static +//; 1645 + $prototype =~ s/^extern +//; 1646 + $prototype =~ s/^asmlinkage +//; 1647 + $prototype =~ s/^inline +//; 1648 + $prototype =~ s/^__inline__ +//; 1649 + $prototype =~ s/^__inline +//; 1650 + $prototype =~ s/^__always_inline +//; 1651 + $prototype =~ s/^noinline +//; 1652 + $prototype =~ s/^__FORTIFY_INLINE +//; 1653 + $prototype =~ s/__init +//; 1654 + $prototype =~ s/__init_or_module +//; 1655 + $prototype =~ s/__deprecated +//; 1656 + $prototype =~ s/__flatten +//; 1657 + $prototype =~ s/__meminit +//; 1658 + $prototype =~ s/__must_check +//; 1659 + $prototype =~ s/__weak +//; 1660 + $prototype =~ s/__sched +//; 1661 + $prototype =~ s/_noprof//; 1662 + $prototype =~ s/__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +//; 1663 + $prototype =~ s/__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +//; 1664 + $prototype =~ s/__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +//; 1665 + $prototype =~ s/DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)/$1, $2/; 1666 + my $define = $prototype =~ s/^#\s*define\s+//; #ak added 1667 + $prototype =~ s/__attribute_const__ +//; 1668 + $prototype =~ s/__attribute__\s*\(\( 1669 + (?: 1670 + [\w\s]++ # attribute name 1671 + (?:\([^)]*+\))? # attribute arguments 1672 + \s*+,? # optional comma at the end 1673 + )+ 1674 + \)\)\s+//x; 1675 + 1676 + # Yes, this truly is vile. We are looking for: 1677 + # 1. Return type (may be nothing if we're looking at a macro) 1678 + # 2. Function name 1679 + # 3. Function parameters. 1680 + # 1681 + # All the while we have to watch out for function pointer parameters 1682 + # (which IIRC is what the two sections are for), C types (these 1683 + # regexps don't even start to express all the possibilities), and 1684 + # so on. 1685 + # 1686 + # If you mess with these regexps, it's a good idea to check that 1687 + # the following functions' documentation still comes out right: 1688 + # - parport_register_device (function pointer parameters) 1689 + # - atomic_set (macro) 1690 + # - pci_match_device, __copy_to_user (long return type) 1691 + my $name = qr{[a-zA-Z0-9_~:]+}; 1692 + my $prototype_end1 = qr{[^\(]*}; 1693 + my $prototype_end2 = qr{[^\{]*}; 1694 + my $prototype_end = qr{\(($prototype_end1|$prototype_end2)\)}; 1695 + my $type1 = qr{[\w\s]+}; 1696 + my $type2 = qr{$type1\*+}; 1697 + 1698 + if ($define && $prototype =~ m/^()($name)\s+/) { 1699 + # This is an object-like macro, it has no return type and no parameter 1700 + # list. 1701 + # Function-like macros are not allowed to have spaces between 1702 + # declaration_name and opening parenthesis (notice the \s+). 1703 + $return_type = $1; 1704 + $declaration_name = $2; 1705 + $func_macro = 1; 1706 + } elsif ($prototype =~ m/^()($name)\s*$prototype_end/ || 1707 + $prototype =~ m/^($type1)\s+($name)\s*$prototype_end/ || 1708 + $prototype =~ m/^($type2+)\s*($name)\s*$prototype_end/) { 1709 + $return_type = $1; 1710 + $declaration_name = $2; 1711 + my $args = $3; 1712 + 1713 + create_parameterlist($args, ',', $file, $declaration_name); 1714 + } else { 1715 + emit_warning("${file}:$.", "cannot understand function prototype: '$prototype'\n"); 1716 + return; 1717 + } 1718 + 1719 + if ($identifier ne $declaration_name) { 1720 + emit_warning("${file}:$.", "expecting prototype for $identifier(). Prototype was for $declaration_name() instead\n"); 1721 + return; 1722 + } 1723 + 1724 + my $prms = join " ", @parameterlist; 1725 + check_sections($file, $declaration_name, "function", $sectcheck, $prms); 1726 + 1727 + # This check emits a lot of warnings at the moment, because many 1728 + # functions don't have a 'Return' doc section. So until the number 1729 + # of warnings goes sufficiently down, the check is only performed in 1730 + # -Wreturn mode. 1731 + # TODO: always perform the check. 1732 + if ($Wreturn && !$func_macro) { 1733 + check_return_section($file, $declaration_name, $return_type); 1734 + } 1735 + 1736 + # The function parser can be called with a typedef parameter. 1737 + # Handle it. 1738 + if ($return_type =~ /typedef/) { 1739 + output_declaration($declaration_name, 1740 + 'function', 1741 + {'function' => $declaration_name, 1742 + 'typedef' => 1, 1743 + 'module' => $modulename, 1744 + 'functiontype' => $return_type, 1745 + 'parameterlist' => \@parameterlist, 1746 + 'parameterdescs' => \%parameterdescs, 1747 + 'parametertypes' => \%parametertypes, 1748 + 'sectionlist' => \@sectionlist, 1749 + 'sections' => \%sections, 1750 + 'purpose' => $declaration_purpose, 1751 + 'func_macro' => $func_macro 1752 + }); 1753 + } else { 1754 + output_declaration($declaration_name, 1755 + 'function', 1756 + {'function' => $declaration_name, 1757 + 'module' => $modulename, 1758 + 'functiontype' => $return_type, 1759 + 'parameterlist' => \@parameterlist, 1760 + 'parameterdescs' => \%parameterdescs, 1761 + 'parametertypes' => \%parametertypes, 1762 + 'sectionlist' => \@sectionlist, 1763 + 'sections' => \%sections, 1764 + 'purpose' => $declaration_purpose, 1765 + 'func_macro' => $func_macro 1766 + }); 1767 + } 1768 + } 1769 + 1770 + sub reset_state { 1771 + $function = ""; 1772 + %parameterdescs = (); 1773 + %parametertypes = (); 1774 + @parameterlist = (); 1775 + %sections = (); 1776 + @sectionlist = (); 1777 + $sectcheck = ""; 1778 + $struct_actual = ""; 1779 + $prototype = ""; 1780 + 1781 + $state = STATE_NORMAL; 1782 + $inline_doc_state = STATE_INLINE_NA; 1783 + } 1784 + 1785 + sub tracepoint_munge($) { 1786 + my $file = shift; 1787 + my $tracepointname = 0; 1788 + my $tracepointargs = 0; 1789 + 1790 + if ($prototype =~ m/TRACE_EVENT\((.*?),/) { 1791 + $tracepointname = $1; 1792 + } 1793 + if ($prototype =~ m/DEFINE_SINGLE_EVENT\((.*?),/) { 1794 + $tracepointname = $1; 1795 + } 1796 + if ($prototype =~ m/DEFINE_EVENT\((.*?),(.*?),/) { 1797 + $tracepointname = $2; 1798 + } 1799 + $tracepointname =~ s/^\s+//; #strip leading whitespace 1800 + if ($prototype =~ m/TP_PROTO\((.*?)\)/) { 1801 + $tracepointargs = $1; 1802 + } 1803 + if (($tracepointname eq 0) || ($tracepointargs eq 0)) { 1804 + emit_warning("${file}:$.", "Unrecognized tracepoint format: \n". 1805 + "$prototype\n"); 1806 + } else { 1807 + $prototype = "static inline void trace_$tracepointname($tracepointargs)"; 1808 + $identifier = "trace_$identifier"; 1809 + } 1810 + } 1811 + 1812 + sub syscall_munge() { 1813 + my $void = 0; 1814 + 1815 + $prototype =~ s@[\r\n]+@ @gos; # strip newlines/CR's 1816 + ## if ($prototype =~ m/SYSCALL_DEFINE0\s*\(\s*(a-zA-Z0-9_)*\s*\)/) { 1817 + if ($prototype =~ m/SYSCALL_DEFINE0/) { 1818 + $void = 1; 1819 + ## $prototype = "long sys_$1(void)"; 1820 + } 1821 + 1822 + $prototype =~ s/SYSCALL_DEFINE.*\(/long sys_/; # fix return type & func name 1823 + if ($prototype =~ m/long (sys_.*?),/) { 1824 + $prototype =~ s/,/\(/; 1825 + } elsif ($void) { 1826 + $prototype =~ s/\)/\(void\)/; 1827 + } 1828 + 1829 + # now delete all of the odd-number commas in $prototype 1830 + # so that arg types & arg names don't have a comma between them 1831 + my $count = 0; 1832 + my $len = length($prototype); 1833 + if ($void) { 1834 + $len = 0; # skip the for-loop 1835 + } 1836 + for (my $ix = 0; $ix < $len; $ix++) { 1837 + if (substr($prototype, $ix, 1) eq ',') { 1838 + $count++; 1839 + if ($count % 2 == 1) { 1840 + substr($prototype, $ix, 1) = ' '; 1841 + } 1842 + } 1843 + } 1844 + } 1845 + 1846 + sub process_proto_function($$) { 1847 + my $x = shift; 1848 + my $file = shift; 1849 + 1850 + $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line 1851 + 1852 + if ($x =~ /^#/ && $x !~ /^#\s*define/) { 1853 + # do nothing 1854 + } elsif ($x =~ /([^\{]*)/) { 1855 + $prototype .= $1; 1856 + } 1857 + 1858 + if (($x =~ /\{/) || ($x =~ /\#\s*define/) || ($x =~ /;/)) { 1859 + $prototype =~ s@/\*.*?\*/@@gos; # strip comments. 1860 + $prototype =~ s@[\r\n]+@ @gos; # strip newlines/cr's. 1861 + $prototype =~ s@^\s+@@gos; # strip leading spaces 1862 + 1863 + # Handle prototypes for function pointers like: 1864 + # int (*pcs_config)(struct foo) 1865 + $prototype =~ s@^(\S+\s+)\(\s*\*(\S+)\)@$1$2@gos; 1866 + 1867 + if ($prototype =~ /SYSCALL_DEFINE/) { 1868 + syscall_munge(); 1869 + } 1870 + if ($prototype =~ /TRACE_EVENT/ || $prototype =~ /DEFINE_EVENT/ || 1871 + $prototype =~ /DEFINE_SINGLE_EVENT/) 1872 + { 1873 + tracepoint_munge($file); 1874 + } 1875 + dump_function($prototype, $file); 1876 + reset_state(); 1877 + } 1878 + } 1879 + 1880 + sub process_proto_type($$) { 1881 + my $x = shift; 1882 + my $file = shift; 1883 + 1884 + $x =~ s@[\r\n]+@ @gos; # strip newlines/cr's. 1885 + $x =~ s@^\s+@@gos; # strip leading spaces 1886 + $x =~ s@\s+$@@gos; # strip trailing spaces 1887 + $x =~ s@\/\/.*$@@gos; # strip C99-style comments to end of line 1888 + 1889 + if ($x =~ /^#/) { 1890 + # To distinguish preprocessor directive from regular declaration later. 1891 + $x .= ";"; 1892 + } 1893 + 1894 + while (1) { 1895 + if ( $x =~ /([^\{\};]*)([\{\};])(.*)/ ) { 1896 + if( length $prototype ) { 1897 + $prototype .= " " 1898 + } 1899 + $prototype .= $1 . $2; 1900 + ($2 eq '{') && $brcount++; 1901 + ($2 eq '}') && $brcount--; 1902 + if (($2 eq ';') && ($brcount == 0)) { 1903 + dump_declaration($prototype, $file); 1904 + reset_state(); 1905 + last; 1906 + } 1907 + $x = $3; 1908 + } else { 1909 + $prototype .= $x; 1910 + last; 1911 + } 1912 + } 1913 + } 1914 + 1915 + 1916 + sub map_filename($) { 1917 + my $file; 1918 + my ($orig_file) = @_; 1919 + 1920 + if (defined($ENV{'SRCTREE'})) { 1921 + $file = "$ENV{'SRCTREE'}" . "/" . $orig_file; 1922 + } else { 1923 + $file = $orig_file; 1924 + } 1925 + 1926 + return $file; 1927 + } 1928 + 1929 + sub process_export_file($) { 1930 + my ($orig_file) = @_; 1931 + my $file = map_filename($orig_file); 1932 + 1933 + if (!open(IN,"<$file")) { 1934 + print STDERR "Error: Cannot open file $file\n"; 1935 + ++$errors; 1936 + return; 1937 + } 1938 + 1939 + while (<IN>) { 1940 + if (/$export_symbol/) { 1941 + next if (defined($nosymbol_table{$2})); 1942 + $function_table{$2} = 1; 1943 + } 1944 + if (/$export_symbol_ns/) { 1945 + next if (defined($nosymbol_table{$2})); 1946 + $function_table{$2} = 1; 1947 + } 1948 + } 1949 + 1950 + close(IN); 1951 + } 1952 + 1953 + # 1954 + # Parsers for the various processing states. 1955 + # 1956 + # STATE_NORMAL: looking for the /** to begin everything. 1957 + # 1958 + sub process_normal() { 1959 + if (/$doc_start/o) { 1960 + $state = STATE_NAME; # next line is always the function name 1961 + $declaration_start_line = $. + 1; 1962 + } 1963 + } 1964 + 1965 + # 1966 + # STATE_NAME: Looking for the "name - description" line 1967 + # 1968 + sub process_name($$) { 1969 + my $file = shift; 1970 + my $descr; 1971 + 1972 + if (/$doc_block/o) { 1973 + $state = STATE_DOCBLOCK; 1974 + $contents = ""; 1975 + $new_start_line = $.; 1976 + 1977 + if ( $1 eq "" ) { 1978 + $section = $section_intro; 1979 + } else { 1980 + $section = $1; 1981 + } 1982 + } elsif (/$doc_decl/o) { 1983 + $identifier = $1; 1984 + my $is_kernel_comment = 0; 1985 + my $decl_start = qr{$doc_com}; 1986 + # test for pointer declaration type, foo * bar() - desc 1987 + my $fn_type = qr{\w+\s*\*\s*}; 1988 + my $parenthesis = qr{\(\w*\)}; 1989 + my $decl_end = qr{[-:].*}; 1990 + if (/^$decl_start([\w\s]+?)$parenthesis?\s*$decl_end?$/) { 1991 + $identifier = $1; 1992 + } 1993 + if ($identifier =~ m/^(struct|union|enum|typedef)\b\s*(\S*)/) { 1994 + $decl_type = $1; 1995 + $identifier = $2; 1996 + $is_kernel_comment = 1; 1997 + } 1998 + # Look for foo() or static void foo() - description; or misspelt 1999 + # identifier 2000 + elsif (/^$decl_start$fn_type?(\w+)\s*$parenthesis?\s*$decl_end?$/ || 2001 + /^$decl_start$fn_type?(\w+[^-:]*)$parenthesis?\s*$decl_end$/) { 2002 + $identifier = $1; 2003 + $decl_type = 'function'; 2004 + $identifier =~ s/^define\s+//; 2005 + $is_kernel_comment = 1; 2006 + } 2007 + $identifier =~ s/\s+$//; 2008 + 2009 + $state = STATE_BODY; 2010 + # if there's no @param blocks need to set up default section 2011 + # here 2012 + $contents = ""; 2013 + $section = $section_default; 2014 + $new_start_line = $. + 1; 2015 + if (/[-:](.*)/) { 2016 + # strip leading/trailing/multiple spaces 2017 + $descr= $1; 2018 + $descr =~ s/^\s*//; 2019 + $descr =~ s/\s*$//; 2020 + $descr =~ s/\s+/ /g; 2021 + $declaration_purpose = $descr; 2022 + $state = STATE_BODY_MAYBE; 2023 + } else { 2024 + $declaration_purpose = ""; 2025 + } 2026 + 2027 + if (!$is_kernel_comment) { 2028 + emit_warning("${file}:$.", "This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n$_"); 2029 + $state = STATE_NORMAL; 2030 + } 2031 + 2032 + if (($declaration_purpose eq "") && $Wshort_desc) { 2033 + emit_warning("${file}:$.", "missing initial short description on line:\n$_"); 2034 + } 2035 + 2036 + if ($identifier eq "" && $decl_type ne "enum") { 2037 + emit_warning("${file}:$.", "wrong kernel-doc identifier on line:\n$_"); 2038 + $state = STATE_NORMAL; 2039 + } 2040 + 2041 + if ($verbose) { 2042 + print STDERR "${file}:$.: info: Scanning doc for $decl_type $identifier\n"; 2043 + } 2044 + } else { 2045 + emit_warning("${file}:$.", "Cannot understand $_ on line $. - I thought it was a doc line\n"); 2046 + $state = STATE_NORMAL; 2047 + } 2048 + } 2049 + 2050 + 2051 + # 2052 + # STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment. 2053 + # 2054 + sub process_body($$) { 2055 + my $file = shift; 2056 + 2057 + if ($state == STATE_BODY_WITH_BLANK_LINE && /^\s*\*\s?\S/) { 2058 + dump_section($file, $section, $contents); 2059 + $section = $section_default; 2060 + $new_start_line = $.; 2061 + $contents = ""; 2062 + } 2063 + 2064 + if (/$doc_sect/i) { # case insensitive for supported section names 2065 + $newsection = $1; 2066 + $newcontents = $2; 2067 + 2068 + # map the supported section names to the canonical names 2069 + if ($newsection =~ m/^description$/i) { 2070 + $newsection = $section_default; 2071 + } elsif ($newsection =~ m/^context$/i) { 2072 + $newsection = $section_context; 2073 + } elsif ($newsection =~ m/^returns?$/i) { 2074 + $newsection = $section_return; 2075 + } elsif ($newsection =~ m/^\@return$/) { 2076 + # special: @return is a section, not a param description 2077 + $newsection = $section_return; 2078 + } 2079 + 2080 + if (($contents ne "") && ($contents ne "\n")) { 2081 + dump_section($file, $section, $contents); 2082 + $section = $section_default; 2083 + } 2084 + 2085 + $state = STATE_BODY; 2086 + $contents = $newcontents; 2087 + $new_start_line = $.; 2088 + while (substr($contents, 0, 1) eq " ") { 2089 + $contents = substr($contents, 1); 2090 + } 2091 + if ($contents ne "") { 2092 + $contents .= "\n"; 2093 + } 2094 + $section = $newsection; 2095 + $leading_space = undef; 2096 + } elsif (/$doc_end/) { 2097 + if (($contents ne "") && ($contents ne "\n")) { 2098 + dump_section($file, $section, $contents); 2099 + $section = $section_default; 2100 + $contents = ""; 2101 + } 2102 + # look for doc_com + <text> + doc_end: 2103 + if ($_ =~ m'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') { 2104 + emit_warning("${file}:$.", "suspicious ending line: $_"); 2105 + } 2106 + 2107 + $prototype = ""; 2108 + $state = STATE_PROTO; 2109 + $brcount = 0; 2110 + $new_start_line = $. + 1; 2111 + } elsif (/$doc_content/) { 2112 + if ($1 eq "") { 2113 + if ($section eq $section_context) { 2114 + dump_section($file, $section, $contents); 2115 + $section = $section_default; 2116 + $contents = ""; 2117 + $new_start_line = $.; 2118 + $state = STATE_BODY; 2119 + } else { 2120 + if ($section ne $section_default) { 2121 + $state = STATE_BODY_WITH_BLANK_LINE; 2122 + } else { 2123 + $state = STATE_BODY; 2124 + } 2125 + $contents .= "\n"; 2126 + } 2127 + } elsif ($state == STATE_BODY_MAYBE) { 2128 + # Continued declaration purpose 2129 + chomp($declaration_purpose); 2130 + $declaration_purpose .= " " . $1; 2131 + $declaration_purpose =~ s/\s+/ /g; 2132 + } else { 2133 + my $cont = $1; 2134 + if ($section =~ m/^@/ || $section eq $section_context) { 2135 + if (!defined $leading_space) { 2136 + if ($cont =~ m/^(\s+)/) { 2137 + $leading_space = $1; 2138 + } else { 2139 + $leading_space = ""; 2140 + } 2141 + } 2142 + $cont =~ s/^$leading_space//; 2143 + } 2144 + $contents .= $cont . "\n"; 2145 + } 2146 + } else { 2147 + # i dont know - bad line? ignore. 2148 + emit_warning("${file}:$.", "bad line: $_"); 2149 + } 2150 + } 2151 + 2152 + 2153 + # 2154 + # STATE_PROTO: reading a function/whatever prototype. 2155 + # 2156 + sub process_proto($$) { 2157 + my $file = shift; 2158 + 2159 + if (/$doc_inline_oneline/) { 2160 + $section = $1; 2161 + $contents = $2; 2162 + if ($contents ne "") { 2163 + $contents .= "\n"; 2164 + dump_section($file, $section, $contents); 2165 + $section = $section_default; 2166 + $contents = ""; 2167 + } 2168 + } elsif (/$doc_inline_start/) { 2169 + $state = STATE_INLINE; 2170 + $inline_doc_state = STATE_INLINE_NAME; 2171 + } elsif ($decl_type eq 'function') { 2172 + process_proto_function($_, $file); 2173 + } else { 2174 + process_proto_type($_, $file); 2175 + } 2176 + } 2177 + 2178 + # 2179 + # STATE_DOCBLOCK: within a DOC: block. 2180 + # 2181 + sub process_docblock($$) { 2182 + my $file = shift; 2183 + 2184 + if (/$doc_end/) { 2185 + dump_doc_section($file, $section, $contents); 2186 + $section = $section_default; 2187 + $contents = ""; 2188 + $function = ""; 2189 + %parameterdescs = (); 2190 + %parametertypes = (); 2191 + @parameterlist = (); 2192 + %sections = (); 2193 + @sectionlist = (); 2194 + $prototype = ""; 2195 + $state = STATE_NORMAL; 2196 + } elsif (/$doc_content/) { 2197 + if ( $1 eq "" ) { 2198 + $contents .= $blankline; 2199 + } else { 2200 + $contents .= $1 . "\n"; 2201 + } 2202 + } 2203 + } 2204 + 2205 + # 2206 + # STATE_INLINE: docbook comments within a prototype. 2207 + # 2208 + sub process_inline($$) { 2209 + my $file = shift; 2210 + 2211 + # First line (state 1) needs to be a @parameter 2212 + if ($inline_doc_state == STATE_INLINE_NAME && /$doc_inline_sect/o) { 2213 + $section = $1; 2214 + $contents = $2; 2215 + $new_start_line = $.; 2216 + if ($contents ne "") { 2217 + while (substr($contents, 0, 1) eq " ") { 2218 + $contents = substr($contents, 1); 2219 + } 2220 + $contents .= "\n"; 2221 + } 2222 + $inline_doc_state = STATE_INLINE_TEXT; 2223 + # Documentation block end */ 2224 + } elsif (/$doc_inline_end/) { 2225 + if (($contents ne "") && ($contents ne "\n")) { 2226 + dump_section($file, $section, $contents); 2227 + $section = $section_default; 2228 + $contents = ""; 2229 + } 2230 + $state = STATE_PROTO; 2231 + $inline_doc_state = STATE_INLINE_NA; 2232 + # Regular text 2233 + } elsif (/$doc_content/) { 2234 + if ($inline_doc_state == STATE_INLINE_TEXT) { 2235 + $contents .= $1 . "\n"; 2236 + # nuke leading blank lines 2237 + if ($contents =~ /^\s*$/) { 2238 + $contents = ""; 2239 + } 2240 + } elsif ($inline_doc_state == STATE_INLINE_NAME) { 2241 + $inline_doc_state = STATE_INLINE_ERROR; 2242 + emit_warning("${file}:$.", "Incorrect use of kernel-doc format: $_"); 2243 + } 2244 + } 2245 + } 2246 + 2247 + 2248 + sub process_file($) { 2249 + my $file; 2250 + my ($orig_file) = @_; 2251 + 2252 + $file = map_filename($orig_file); 2253 + 2254 + if (!open(IN_FILE,"<$file")) { 2255 + print STDERR "Error: Cannot open file $file\n"; 2256 + ++$errors; 2257 + return; 2258 + } 2259 + 2260 + $. = 1; 2261 + 2262 + $section_counter = 0; 2263 + while (<IN_FILE>) { 2264 + while (!/^ \*/ && s/\\\s*$//) { 2265 + $_ .= <IN_FILE>; 2266 + } 2267 + # Replace tabs by spaces 2268 + while ($_ =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e) {}; 2269 + # Hand this line to the appropriate state handler 2270 + if ($state == STATE_NORMAL) { 2271 + process_normal(); 2272 + } elsif ($state == STATE_NAME) { 2273 + process_name($file, $_); 2274 + } elsif ($state == STATE_BODY || $state == STATE_BODY_MAYBE || 2275 + $state == STATE_BODY_WITH_BLANK_LINE) { 2276 + process_body($file, $_); 2277 + } elsif ($state == STATE_INLINE) { # scanning for inline parameters 2278 + process_inline($file, $_); 2279 + } elsif ($state == STATE_PROTO) { 2280 + process_proto($file, $_); 2281 + } elsif ($state == STATE_DOCBLOCK) { 2282 + process_docblock($file, $_); 2283 + } 2284 + } 2285 + 2286 + # Make sure we got something interesting. 2287 + if (!$section_counter && $output_mode ne "none") { 2288 + if ($output_selection == OUTPUT_INCLUDE) { 2289 + emit_warning("${file}:1", "'$_' not found\n") 2290 + for keys %function_table; 2291 + } else { 2292 + emit_warning("${file}:1", "no structured comments found\n"); 2293 + } 2294 + } 2295 + close IN_FILE; 2296 + } 2297 + 2298 + $kernelversion = get_kernel_version(); 2299 + 2300 + # generate a sequence of code that will splice in highlighting information 2301 + # using the s// operator. 2302 + for (my $k = 0; $k < @highlights; $k++) { 2303 + my $pattern = $highlights[$k][0]; 2304 + my $result = $highlights[$k][1]; 2305 + # print STDERR "scanning pattern:$pattern, highlight:($result)\n"; 2306 + $dohighlight .= "\$contents =~ s:$pattern:$result:gs;\n"; 2307 + } 2308 + 2309 + if ($output_selection == OUTPUT_EXPORTED || 2310 + $output_selection == OUTPUT_INTERNAL) { 2311 + 2312 + push(@export_file_list, @ARGV); 2313 + 2314 + foreach (@export_file_list) { 2315 + chomp; 2316 + process_export_file($_); 2317 + } 2318 + } 2319 + 2320 + foreach (@ARGV) { 2321 + chomp; 2322 + process_file($_); 2323 + } 2324 + if ($verbose && $errors) { 2325 + print STDERR "$errors errors\n"; 2326 + } 2327 + if ($verbose && $warnings) { 2328 + print STDERR "$warnings warnings\n"; 2329 + } 2330 + 2331 + if ($Werror && $warnings) { 2332 + print STDERR "$warnings warnings as Errors\n"; 2333 + exit($warnings); 2334 + } else { 2335 + exit($output_mode eq "none" ? 0 : $errors) 2336 + } 2337 + 2338 + __END__ 2339 + 2340 + =head1 OPTIONS 2341 + 2342 + =head2 Output format selection (mutually exclusive): 2343 + 2344 + =over 8 2345 + 2346 + =item -man 2347 + 2348 + Output troff manual page format. 2349 + 2350 + =item -rst 2351 + 2352 + Output reStructuredText format. This is the default. 2353 + 2354 + =item -none 2355 + 2356 + Do not output documentation, only warnings. 2357 + 2358 + =back 2359 + 2360 + =head2 Output format modifiers 2361 + 2362 + =head3 reStructuredText only 2363 + 2364 + =head2 Output selection (mutually exclusive): 2365 + 2366 + =over 8 2367 + 2368 + =item -export 2369 + 2370 + Only output documentation for the symbols that have been exported using 2371 + EXPORT_SYMBOL() and related macros in any input FILE or -export-file FILE. 2372 + 2373 + =item -internal 2374 + 2375 + Only output documentation for the symbols that have NOT been exported using 2376 + EXPORT_SYMBOL() and related macros in any input FILE or -export-file FILE. 2377 + 2378 + =item -function NAME 2379 + 2380 + Only output documentation for the given function or DOC: section title. 2381 + All other functions and DOC: sections are ignored. 2382 + 2383 + May be specified multiple times. 2384 + 2385 + =item -nosymbol NAME 2386 + 2387 + Exclude the specified symbol from the output documentation. 2388 + 2389 + May be specified multiple times. 2390 + 2391 + =back 2392 + 2393 + =head2 Output selection modifiers: 2394 + 2395 + =over 8 2396 + 2397 + =item -no-doc-sections 2398 + 2399 + Do not output DOC: sections. 2400 + 2401 + =item -export-file FILE 2402 + 2403 + Specify an additional FILE in which to look for EXPORT_SYMBOL information. 2404 + 2405 + To be used with -export or -internal. 2406 + 2407 + May be specified multiple times. 2408 + 2409 + =back 2410 + 2411 + =head3 reStructuredText only 2412 + 2413 + =over 8 2414 + 2415 + =item -enable-lineno 2416 + 2417 + Enable output of .. LINENO lines. 2418 + 2419 + =back 2420 + 2421 + =head2 Other parameters: 2422 + 2423 + =over 8 2424 + 2425 + =item -h, -help 2426 + 2427 + Print this help. 2428 + 2429 + =item -v 2430 + 2431 + Verbose output, more warnings and other information. 2432 + 2433 + =item -Werror 2434 + 2435 + Treat warnings as errors. 2436 + 2437 + =back 2438 + 2439 + =cut
+315
scripts/kernel-doc.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + # Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>. 4 + # 5 + # pylint: disable=C0103,R0915 6 + # 7 + # Converted from the kernel-doc script originally written in Perl 8 + # under GPLv2, copyrighted since 1998 by the following authors: 9 + # 10 + # Aditya Srivastava <yashsri421@gmail.com> 11 + # Akira Yokosawa <akiyks@gmail.com> 12 + # Alexander A. Klimov <grandmaster@al2klimov.de> 13 + # Alexander Lobakin <aleksander.lobakin@intel.com> 14 + # André Almeida <andrealmeid@igalia.com> 15 + # Andy Shevchenko <andriy.shevchenko@linux.intel.com> 16 + # Anna-Maria Behnsen <anna-maria@linutronix.de> 17 + # Armin Kuster <akuster@mvista.com> 18 + # Bart Van Assche <bart.vanassche@sandisk.com> 19 + # Ben Hutchings <ben@decadent.org.uk> 20 + # Borislav Petkov <bbpetkov@yahoo.de> 21 + # Chen-Yu Tsai <wenst@chromium.org> 22 + # Coco Li <lixiaoyan@google.com> 23 + # Conchúr Navid <conchur@web.de> 24 + # Daniel Santos <daniel.santos@pobox.com> 25 + # Danilo Cesar Lemes de Paula <danilo.cesar@collabora.co.uk> 26 + # Dan Luedtke <mail@danrl.de> 27 + # Donald Hunter <donald.hunter@gmail.com> 28 + # Gabriel Krisman Bertazi <krisman@collabora.co.uk> 29 + # Greg Kroah-Hartman <gregkh@linuxfoundation.org> 30 + # Harvey Harrison <harvey.harrison@gmail.com> 31 + # Horia Geanta <horia.geanta@freescale.com> 32 + # Ilya Dryomov <idryomov@gmail.com> 33 + # Jakub Kicinski <kuba@kernel.org> 34 + # Jani Nikula <jani.nikula@intel.com> 35 + # Jason Baron <jbaron@redhat.com> 36 + # Jason Gunthorpe <jgg@nvidia.com> 37 + # Jérémy Bobbio <lunar@debian.org> 38 + # Johannes Berg <johannes.berg@intel.com> 39 + # Johannes Weiner <hannes@cmpxchg.org> 40 + # Jonathan Cameron <Jonathan.Cameron@huawei.com> 41 + # Jonathan Corbet <corbet@lwn.net> 42 + # Jonathan Neuschäfer <j.neuschaefer@gmx.net> 43 + # Kamil Rytarowski <n54@gmx.com> 44 + # Kees Cook <kees@kernel.org> 45 + # Laurent Pinchart <laurent.pinchart@ideasonboard.com> 46 + # Levin, Alexander (Sasha Levin) <alexander.levin@verizon.com> 47 + # Linus Torvalds <torvalds@linux-foundation.org> 48 + # Lucas De Marchi <lucas.demarchi@profusion.mobi> 49 + # Mark Rutland <mark.rutland@arm.com> 50 + # Markus Heiser <markus.heiser@darmarit.de> 51 + # Martin Waitz <tali@admingilde.org> 52 + # Masahiro Yamada <masahiroy@kernel.org> 53 + # Matthew Wilcox <willy@infradead.org> 54 + # Mauro Carvalho Chehab <mchehab+huawei@kernel.org> 55 + # Michal Wajdeczko <michal.wajdeczko@intel.com> 56 + # Michael Zucchi 57 + # Mike Rapoport <rppt@linux.ibm.com> 58 + # Niklas Söderlund <niklas.soderlund@corigine.com> 59 + # Nishanth Menon <nm@ti.com> 60 + # Paolo Bonzini <pbonzini@redhat.com> 61 + # Pavan Kumar Linga <pavan.kumar.linga@intel.com> 62 + # Pavel Pisa <pisa@cmp.felk.cvut.cz> 63 + # Peter Maydell <peter.maydell@linaro.org> 64 + # Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com> 65 + # Randy Dunlap <rdunlap@infradead.org> 66 + # Richard Kennedy <richard@rsk.demon.co.uk> 67 + # Rich Walker <rw@shadow.org.uk> 68 + # Rolf Eike Beer <eike-kernel@sf-tec.de> 69 + # Sakari Ailus <sakari.ailus@linux.intel.com> 70 + # Silvio Fricke <silvio.fricke@gmail.com> 71 + # Simon Huggins 72 + # Tim Waugh <twaugh@redhat.com> 73 + # Tomasz Warniełło <tomasz.warniello@gmail.com> 74 + # Utkarsh Tripathi <utripathi2002@gmail.com> 75 + # valdis.kletnieks@vt.edu <valdis.kletnieks@vt.edu> 76 + # Vegard Nossum <vegard.nossum@oracle.com> 77 + # Will Deacon <will.deacon@arm.com> 78 + # Yacine Belkadi <yacine.belkadi.1@gmail.com> 79 + # Yujie Liu <yujie.liu@intel.com> 80 + 81 + """ 82 + kernel_doc 83 + ========== 84 + 85 + Print formatted kernel documentation to stdout 86 + 87 + Read C language source or header FILEs, extract embedded 88 + documentation comments, and print formatted documentation 89 + to standard output. 90 + 91 + The documentation comments are identified by the "/**" 92 + opening comment mark. 93 + 94 + See Documentation/doc-guide/kernel-doc.rst for the 95 + documentation comment syntax. 96 + """ 97 + 98 + import argparse 99 + import logging 100 + import os 101 + import sys 102 + 103 + # Import Python modules 104 + 105 + LIB_DIR = "lib/kdoc" 106 + SRC_DIR = os.path.dirname(os.path.realpath(__file__)) 107 + 108 + sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) 109 + 110 + from kdoc_files import KernelFiles # pylint: disable=C0413 111 + from kdoc_output import RestFormat, ManFormat # pylint: disable=C0413 112 + 113 + DESC = """ 114 + Read C language source or header FILEs, extract embedded documentation comments, 115 + and print formatted documentation to standard output. 116 + 117 + The documentation comments are identified by the "/**" opening comment mark. 118 + 119 + See Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax. 120 + """ 121 + 122 + EXPORT_FILE_DESC = """ 123 + Specify an additional FILE in which to look for EXPORT_SYMBOL information. 124 + 125 + May be used multiple times. 126 + """ 127 + 128 + EXPORT_DESC = """ 129 + Only output documentation for the symbols that have been 130 + exported using EXPORT_SYMBOL() and related macros in any input 131 + FILE or -export-file FILE. 132 + """ 133 + 134 + INTERNAL_DESC = """ 135 + Only output documentation for the symbols that have NOT been 136 + exported using EXPORT_SYMBOL() and related macros in any input 137 + FILE or -export-file FILE. 138 + """ 139 + 140 + FUNCTION_DESC = """ 141 + Only output documentation for the given function or DOC: section 142 + title. All other functions and DOC: sections are ignored. 143 + 144 + May be used multiple times. 145 + """ 146 + 147 + NOSYMBOL_DESC = """ 148 + Exclude the specified symbol from the output documentation. 149 + 150 + May be used multiple times. 151 + """ 152 + 153 + FILES_DESC = """ 154 + Header and C source files to be parsed. 155 + """ 156 + 157 + WARN_CONTENTS_BEFORE_SECTIONS_DESC = """ 158 + Warns if there are contents before sections (deprecated). 159 + 160 + This option is kept just for backward-compatibility, but it does nothing, 161 + neither here nor at the original Perl script. 162 + """ 163 + 164 + 165 + class MsgFormatter(logging.Formatter): 166 + """Helper class to format warnings on a similar way to kernel-doc.pl""" 167 + 168 + def format(self, record): 169 + record.levelname = record.levelname.capitalize() 170 + return logging.Formatter.format(self, record) 171 + 172 + def main(): 173 + """Main program""" 174 + 175 + parser = argparse.ArgumentParser(formatter_class=argparse.RawTextHelpFormatter, 176 + description=DESC) 177 + 178 + # Normal arguments 179 + 180 + parser.add_argument("-v", "-verbose", "--verbose", action="store_true", 181 + help="Verbose output, more warnings and other information.") 182 + 183 + parser.add_argument("-d", "-debug", "--debug", action="store_true", 184 + help="Enable debug messages") 185 + 186 + parser.add_argument("-M", "-modulename", "--modulename", 187 + default="Kernel API", 188 + help="Allow setting a module name at the output.") 189 + 190 + parser.add_argument("-l", "-enable-lineno", "--enable_lineno", 191 + action="store_true", 192 + help="Enable line number output (only in ReST mode)") 193 + 194 + # Arguments to control the warning behavior 195 + 196 + parser.add_argument("-Wreturn", "--wreturn", action="store_true", 197 + help="Warns about the lack of a return markup on functions.") 198 + 199 + parser.add_argument("-Wshort-desc", "-Wshort-description", "--wshort-desc", 200 + action="store_true", 201 + help="Warns if initial short description is missing") 202 + 203 + parser.add_argument("-Wcontents-before-sections", 204 + "--wcontents-before-sections", action="store_true", 205 + help=WARN_CONTENTS_BEFORE_SECTIONS_DESC) 206 + 207 + parser.add_argument("-Wall", "--wall", action="store_true", 208 + help="Enable all types of warnings") 209 + 210 + parser.add_argument("-Werror", "--werror", action="store_true", 211 + help="Treat warnings as errors.") 212 + 213 + parser.add_argument("-export-file", "--export-file", action='append', 214 + help=EXPORT_FILE_DESC) 215 + 216 + # Output format mutually-exclusive group 217 + 218 + out_group = parser.add_argument_group("Output format selection (mutually exclusive)") 219 + 220 + out_fmt = out_group.add_mutually_exclusive_group() 221 + 222 + out_fmt.add_argument("-m", "-man", "--man", action="store_true", 223 + help="Output troff manual page format.") 224 + out_fmt.add_argument("-r", "-rst", "--rst", action="store_true", 225 + help="Output reStructuredText format (default).") 226 + out_fmt.add_argument("-N", "-none", "--none", action="store_true", 227 + help="Do not output documentation, only warnings.") 228 + 229 + # Output selection mutually-exclusive group 230 + 231 + sel_group = parser.add_argument_group("Output selection (mutually exclusive)") 232 + sel_mut = sel_group.add_mutually_exclusive_group() 233 + 234 + sel_mut.add_argument("-e", "-export", "--export", action='store_true', 235 + help=EXPORT_DESC) 236 + 237 + sel_mut.add_argument("-i", "-internal", "--internal", action='store_true', 238 + help=INTERNAL_DESC) 239 + 240 + sel_mut.add_argument("-s", "-function", "--symbol", action='append', 241 + help=FUNCTION_DESC) 242 + 243 + # Those are valid for all 3 types of filter 244 + parser.add_argument("-n", "-nosymbol", "--nosymbol", action='append', 245 + help=NOSYMBOL_DESC) 246 + 247 + parser.add_argument("-D", "-no-doc-sections", "--no-doc-sections", 248 + action='store_true', help="Don't outputt DOC sections") 249 + 250 + parser.add_argument("files", metavar="FILE", 251 + nargs="+", help=FILES_DESC) 252 + 253 + args = parser.parse_args() 254 + 255 + if args.wall: 256 + args.wreturn = True 257 + args.wshort_desc = True 258 + args.wcontents_before_sections = True 259 + 260 + logger = logging.getLogger() 261 + 262 + if not args.debug: 263 + logger.setLevel(logging.INFO) 264 + else: 265 + logger.setLevel(logging.DEBUG) 266 + 267 + formatter = MsgFormatter('%(levelname)s: %(message)s') 268 + 269 + handler = logging.StreamHandler() 270 + handler.setFormatter(formatter) 271 + 272 + logger.addHandler(handler) 273 + 274 + if args.man: 275 + out_style = ManFormat(modulename=args.modulename) 276 + elif args.none: 277 + out_style = None 278 + else: 279 + out_style = RestFormat() 280 + 281 + kfiles = KernelFiles(verbose=args.verbose, 282 + out_style=out_style, werror=args.werror, 283 + wreturn=args.wreturn, wshort_desc=args.wshort_desc, 284 + wcontents_before_sections=args.wcontents_before_sections) 285 + 286 + kfiles.parse(args.files, export_file=args.export_file) 287 + 288 + for t in kfiles.msg(enable_lineno=args.enable_lineno, export=args.export, 289 + internal=args.internal, symbol=args.symbol, 290 + nosymbol=args.nosymbol, export_file=args.export_file, 291 + no_doc_sections=args.no_doc_sections): 292 + msg = t[1] 293 + if msg: 294 + print(msg) 295 + 296 + error_count = kfiles.errors 297 + if not error_count: 298 + sys.exit(0) 299 + 300 + if args.werror: 301 + print(f"{error_count} warnings as errors") 302 + sys.exit(error_count) 303 + 304 + if args.verbose: 305 + print(f"{error_count} errors") 306 + 307 + if args.none: 308 + sys.exit(0) 309 + 310 + sys.exit(error_count) 311 + 312 + 313 + # Call main method 314 + if __name__ == "__main__": 315 + main()
+291
scripts/lib/kdoc/kdoc_files.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + # Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>. 4 + # 5 + # pylint: disable=R0903,R0913,R0914,R0917 6 + 7 + """ 8 + Parse lernel-doc tags on multiple kernel source files. 9 + """ 10 + 11 + import argparse 12 + import logging 13 + import os 14 + import re 15 + 16 + from kdoc_parser import KernelDoc 17 + from kdoc_output import OutputFormat 18 + 19 + 20 + class GlobSourceFiles: 21 + """ 22 + Parse C source code file names and directories via an Interactor. 23 + """ 24 + 25 + def __init__(self, srctree=None, valid_extensions=None): 26 + """ 27 + Initialize valid extensions with a tuple. 28 + 29 + If not defined, assume default C extensions (.c and .h) 30 + 31 + It would be possible to use python's glob function, but it is 32 + very slow, and it is not interactive. So, it would wait to read all 33 + directories before actually do something. 34 + 35 + So, let's use our own implementation. 36 + """ 37 + 38 + if not valid_extensions: 39 + self.extensions = (".c", ".h") 40 + else: 41 + self.extensions = valid_extensions 42 + 43 + self.srctree = srctree 44 + 45 + def _parse_dir(self, dirname): 46 + """Internal function to parse files recursively""" 47 + 48 + with os.scandir(dirname) as obj: 49 + for entry in obj: 50 + name = os.path.join(dirname, entry.name) 51 + 52 + if entry.is_dir(): 53 + yield from self._parse_dir(name) 54 + 55 + if not entry.is_file(): 56 + continue 57 + 58 + basename = os.path.basename(name) 59 + 60 + if not basename.endswith(self.extensions): 61 + continue 62 + 63 + yield name 64 + 65 + def parse_files(self, file_list, file_not_found_cb): 66 + """ 67 + Define an interator to parse all source files from file_list, 68 + handling directories if any 69 + """ 70 + 71 + if not file_list: 72 + return 73 + 74 + for fname in file_list: 75 + if self.srctree: 76 + f = os.path.join(self.srctree, fname) 77 + else: 78 + f = fname 79 + 80 + if os.path.isdir(f): 81 + yield from self._parse_dir(f) 82 + elif os.path.isfile(f): 83 + yield f 84 + elif file_not_found_cb: 85 + file_not_found_cb(fname) 86 + 87 + 88 + class KernelFiles(): 89 + """ 90 + Parse kernel-doc tags on multiple kernel source files. 91 + 92 + There are two type of parsers defined here: 93 + - self.parse_file(): parses both kernel-doc markups and 94 + EXPORT_SYMBOL* macros; 95 + - self.process_export_file(): parses only EXPORT_SYMBOL* macros. 96 + """ 97 + 98 + def warning(self, msg): 99 + """Ancillary routine to output a warning and increment error count""" 100 + 101 + self.config.log.warning(msg) 102 + self.errors += 1 103 + 104 + def error(self, msg): 105 + """Ancillary routine to output an error and increment error count""" 106 + 107 + self.config.log.error(msg) 108 + self.errors += 1 109 + 110 + def parse_file(self, fname): 111 + """ 112 + Parse a single Kernel source. 113 + """ 114 + 115 + # Prevent parsing the same file twice if results are cached 116 + if fname in self.files: 117 + return 118 + 119 + doc = KernelDoc(self.config, fname) 120 + export_table, entries = doc.parse_kdoc() 121 + 122 + self.export_table[fname] = export_table 123 + 124 + self.files.add(fname) 125 + self.export_files.add(fname) # parse_kdoc() already check exports 126 + 127 + self.results[fname] = entries 128 + 129 + def process_export_file(self, fname): 130 + """ 131 + Parses EXPORT_SYMBOL* macros from a single Kernel source file. 132 + """ 133 + 134 + # Prevent parsing the same file twice if results are cached 135 + if fname in self.export_files: 136 + return 137 + 138 + doc = KernelDoc(self.config, fname) 139 + export_table = doc.parse_export() 140 + 141 + if not export_table: 142 + self.error(f"Error: Cannot check EXPORT_SYMBOL* on {fname}") 143 + export_table = set() 144 + 145 + self.export_table[fname] = export_table 146 + self.export_files.add(fname) 147 + 148 + def file_not_found_cb(self, fname): 149 + """ 150 + Callback to warn if a file was not found. 151 + """ 152 + 153 + self.error(f"Cannot find file {fname}") 154 + 155 + def __init__(self, verbose=False, out_style=None, 156 + werror=False, wreturn=False, wshort_desc=False, 157 + wcontents_before_sections=False, 158 + logger=None): 159 + """ 160 + Initialize startup variables and parse all files 161 + """ 162 + 163 + if not verbose: 164 + verbose = bool(os.environ.get("KBUILD_VERBOSE", 0)) 165 + 166 + if out_style is None: 167 + out_style = OutputFormat() 168 + 169 + if not werror: 170 + kcflags = os.environ.get("KCFLAGS", None) 171 + if kcflags: 172 + match = re.search(r"(\s|^)-Werror(\s|$)/", kcflags) 173 + if match: 174 + werror = True 175 + 176 + # reading this variable is for backwards compat just in case 177 + # someone was calling it with the variable from outside the 178 + # kernel's build system 179 + kdoc_werror = os.environ.get("KDOC_WERROR", None) 180 + if kdoc_werror: 181 + werror = kdoc_werror 182 + 183 + # Some variables are global to the parser logic as a whole as they are 184 + # used to send control configuration to KernelDoc class. As such, 185 + # those variables are read-only inside the KernelDoc. 186 + self.config = argparse.Namespace 187 + 188 + self.config.verbose = verbose 189 + self.config.werror = werror 190 + self.config.wreturn = wreturn 191 + self.config.wshort_desc = wshort_desc 192 + self.config.wcontents_before_sections = wcontents_before_sections 193 + 194 + if not logger: 195 + self.config.log = logging.getLogger("kernel-doc") 196 + else: 197 + self.config.log = logger 198 + 199 + self.config.warning = self.warning 200 + 201 + self.config.src_tree = os.environ.get("SRCTREE", None) 202 + 203 + # Initialize variables that are internal to KernelFiles 204 + 205 + self.out_style = out_style 206 + 207 + self.errors = 0 208 + self.results = {} 209 + 210 + self.files = set() 211 + self.export_files = set() 212 + self.export_table = {} 213 + 214 + def parse(self, file_list, export_file=None): 215 + """ 216 + Parse all files 217 + """ 218 + 219 + glob = GlobSourceFiles(srctree=self.config.src_tree) 220 + 221 + for fname in glob.parse_files(file_list, self.file_not_found_cb): 222 + self.parse_file(fname) 223 + 224 + for fname in glob.parse_files(export_file, self.file_not_found_cb): 225 + self.process_export_file(fname) 226 + 227 + def out_msg(self, fname, name, arg): 228 + """ 229 + Return output messages from a file name using the output style 230 + filtering. 231 + 232 + If output type was not handled by the syler, return None. 233 + """ 234 + 235 + # NOTE: we can add rules here to filter out unwanted parts, 236 + # although OutputFormat.msg already does that. 237 + 238 + return self.out_style.msg(fname, name, arg) 239 + 240 + def msg(self, enable_lineno=False, export=False, internal=False, 241 + symbol=None, nosymbol=None, no_doc_sections=False, 242 + filenames=None, export_file=None): 243 + """ 244 + Interacts over the kernel-doc results and output messages, 245 + returning kernel-doc markups on each interaction 246 + """ 247 + 248 + self.out_style.set_config(self.config) 249 + 250 + if not filenames: 251 + filenames = sorted(self.results.keys()) 252 + 253 + glob = GlobSourceFiles(srctree=self.config.src_tree) 254 + 255 + for fname in filenames: 256 + function_table = set() 257 + 258 + if internal or export: 259 + if not export_file: 260 + export_file = [fname] 261 + 262 + for f in glob.parse_files(export_file, self.file_not_found_cb): 263 + function_table |= self.export_table[f] 264 + 265 + if symbol: 266 + for s in symbol: 267 + function_table.add(s) 268 + 269 + self.out_style.set_filter(export, internal, symbol, nosymbol, 270 + function_table, enable_lineno, 271 + no_doc_sections) 272 + 273 + msg = "" 274 + if fname not in self.results: 275 + self.config.log.warning("No kernel-doc for file %s", fname) 276 + continue 277 + 278 + for name, arg in self.results[fname]: 279 + m = self.out_msg(fname, name, arg) 280 + 281 + if m is None: 282 + ln = arg.get("ln", 0) 283 + dtype = arg.get('type', "") 284 + 285 + self.config.log.warning("%s:%d Can't handle %s", 286 + fname, ln, dtype) 287 + else: 288 + msg += m 289 + 290 + if msg: 291 + yield fname, msg
+793
scripts/lib/kdoc/kdoc_output.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + # Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>. 4 + # 5 + # pylint: disable=C0301,R0902,R0911,R0912,R0913,R0914,R0915,R0917 6 + 7 + """ 8 + Implement output filters to print kernel-doc documentation. 9 + 10 + The implementation uses a virtual base class (OutputFormat) which 11 + contains a dispatches to virtual methods, and some code to filter 12 + out output messages. 13 + 14 + The actual implementation is done on one separate class per each type 15 + of output. Currently, there are output classes for ReST and man/troff. 16 + """ 17 + 18 + import os 19 + import re 20 + from datetime import datetime 21 + 22 + from kdoc_parser import KernelDoc, type_param 23 + from kdoc_re import KernRe 24 + 25 + 26 + function_pointer = KernRe(r"([^\(]*\(\*)\s*\)\s*\(([^\)]*)\)", cache=False) 27 + 28 + # match expressions used to find embedded type information 29 + type_constant = KernRe(r"\b``([^\`]+)``\b", cache=False) 30 + type_constant2 = KernRe(r"\%([-_*\w]+)", cache=False) 31 + type_func = KernRe(r"(\w+)\(\)", cache=False) 32 + type_param_ref = KernRe(r"([\!~\*]?)\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False) 33 + 34 + # Special RST handling for func ptr params 35 + type_fp_param = KernRe(r"\@(\w+)\(\)", cache=False) 36 + 37 + # Special RST handling for structs with func ptr params 38 + type_fp_param2 = KernRe(r"\@(\w+->\S+)\(\)", cache=False) 39 + 40 + type_env = KernRe(r"(\$\w+)", cache=False) 41 + type_enum = KernRe(r"\&(enum\s*([_\w]+))", cache=False) 42 + type_struct = KernRe(r"\&(struct\s*([_\w]+))", cache=False) 43 + type_typedef = KernRe(r"\&(typedef\s*([_\w]+))", cache=False) 44 + type_union = KernRe(r"\&(union\s*([_\w]+))", cache=False) 45 + type_member = KernRe(r"\&([_\w]+)(\.|->)([_\w]+)", cache=False) 46 + type_fallback = KernRe(r"\&([_\w]+)", cache=False) 47 + type_member_func = type_member + KernRe(r"\(\)", cache=False) 48 + 49 + 50 + class OutputFormat: 51 + """ 52 + Base class for OutputFormat. If used as-is, it means that only 53 + warnings will be displayed. 54 + """ 55 + 56 + # output mode. 57 + OUTPUT_ALL = 0 # output all symbols and doc sections 58 + OUTPUT_INCLUDE = 1 # output only specified symbols 59 + OUTPUT_EXPORTED = 2 # output exported symbols 60 + OUTPUT_INTERNAL = 3 # output non-exported symbols 61 + 62 + # Virtual member to be overriden at the inherited classes 63 + highlights = [] 64 + 65 + def __init__(self): 66 + """Declare internal vars and set mode to OUTPUT_ALL""" 67 + 68 + self.out_mode = self.OUTPUT_ALL 69 + self.enable_lineno = None 70 + self.nosymbol = {} 71 + self.symbol = None 72 + self.function_table = None 73 + self.config = None 74 + self.no_doc_sections = False 75 + 76 + self.data = "" 77 + 78 + def set_config(self, config): 79 + """ 80 + Setup global config variables used by both parser and output. 81 + """ 82 + 83 + self.config = config 84 + 85 + def set_filter(self, export, internal, symbol, nosymbol, function_table, 86 + enable_lineno, no_doc_sections): 87 + """ 88 + Initialize filter variables according with the requested mode. 89 + 90 + Only one choice is valid between export, internal and symbol. 91 + 92 + The nosymbol filter can be used on all modes. 93 + """ 94 + 95 + self.enable_lineno = enable_lineno 96 + self.no_doc_sections = no_doc_sections 97 + self.function_table = function_table 98 + 99 + if symbol: 100 + self.out_mode = self.OUTPUT_INCLUDE 101 + elif export: 102 + self.out_mode = self.OUTPUT_EXPORTED 103 + elif internal: 104 + self.out_mode = self.OUTPUT_INTERNAL 105 + else: 106 + self.out_mode = self.OUTPUT_ALL 107 + 108 + if nosymbol: 109 + self.nosymbol = set(nosymbol) 110 + 111 + 112 + def highlight_block(self, block): 113 + """ 114 + Apply the RST highlights to a sub-block of text. 115 + """ 116 + 117 + for r, sub in self.highlights: 118 + block = r.sub(sub, block) 119 + 120 + return block 121 + 122 + def out_warnings(self, args): 123 + """ 124 + Output warnings for identifiers that will be displayed. 125 + """ 126 + 127 + warnings = args.get('warnings', []) 128 + 129 + for log_msg in warnings: 130 + self.config.warning(log_msg) 131 + 132 + def check_doc(self, name, args): 133 + """Check if DOC should be output""" 134 + 135 + if self.no_doc_sections: 136 + return False 137 + 138 + if name in self.nosymbol: 139 + return False 140 + 141 + if self.out_mode == self.OUTPUT_ALL: 142 + self.out_warnings(args) 143 + return True 144 + 145 + if self.out_mode == self.OUTPUT_INCLUDE: 146 + if name in self.function_table: 147 + self.out_warnings(args) 148 + return True 149 + 150 + return False 151 + 152 + def check_declaration(self, dtype, name, args): 153 + """ 154 + Checks if a declaration should be output or not based on the 155 + filtering criteria. 156 + """ 157 + 158 + if name in self.nosymbol: 159 + return False 160 + 161 + if self.out_mode == self.OUTPUT_ALL: 162 + self.out_warnings(args) 163 + return True 164 + 165 + if self.out_mode in [self.OUTPUT_INCLUDE, self.OUTPUT_EXPORTED]: 166 + if name in self.function_table: 167 + return True 168 + 169 + if self.out_mode == self.OUTPUT_INTERNAL: 170 + if dtype != "function": 171 + self.out_warnings(args) 172 + return True 173 + 174 + if name not in self.function_table: 175 + self.out_warnings(args) 176 + return True 177 + 178 + return False 179 + 180 + def msg(self, fname, name, args): 181 + """ 182 + Handles a single entry from kernel-doc parser 183 + """ 184 + 185 + self.data = "" 186 + 187 + dtype = args.get('type', "") 188 + 189 + if dtype == "doc": 190 + self.out_doc(fname, name, args) 191 + return self.data 192 + 193 + if not self.check_declaration(dtype, name, args): 194 + return self.data 195 + 196 + if dtype == "function": 197 + self.out_function(fname, name, args) 198 + return self.data 199 + 200 + if dtype == "enum": 201 + self.out_enum(fname, name, args) 202 + return self.data 203 + 204 + if dtype == "typedef": 205 + self.out_typedef(fname, name, args) 206 + return self.data 207 + 208 + if dtype in ["struct", "union"]: 209 + self.out_struct(fname, name, args) 210 + return self.data 211 + 212 + # Warn if some type requires an output logic 213 + self.config.log.warning("doesn't now how to output '%s' block", 214 + dtype) 215 + 216 + return None 217 + 218 + # Virtual methods to be overridden by inherited classes 219 + # At the base class, those do nothing. 220 + def out_doc(self, fname, name, args): 221 + """Outputs a DOC block""" 222 + 223 + def out_function(self, fname, name, args): 224 + """Outputs a function""" 225 + 226 + def out_enum(self, fname, name, args): 227 + """Outputs an enum""" 228 + 229 + def out_typedef(self, fname, name, args): 230 + """Outputs a typedef""" 231 + 232 + def out_struct(self, fname, name, args): 233 + """Outputs a struct""" 234 + 235 + 236 + class RestFormat(OutputFormat): 237 + """Consts and functions used by ReST output""" 238 + 239 + highlights = [ 240 + (type_constant, r"``\1``"), 241 + (type_constant2, r"``\1``"), 242 + 243 + # Note: need to escape () to avoid func matching later 244 + (type_member_func, r":c:type:`\1\2\3\\(\\) <\1>`"), 245 + (type_member, r":c:type:`\1\2\3 <\1>`"), 246 + (type_fp_param, r"**\1\\(\\)**"), 247 + (type_fp_param2, r"**\1\\(\\)**"), 248 + (type_func, r"\1()"), 249 + (type_enum, r":c:type:`\1 <\2>`"), 250 + (type_struct, r":c:type:`\1 <\2>`"), 251 + (type_typedef, r":c:type:`\1 <\2>`"), 252 + (type_union, r":c:type:`\1 <\2>`"), 253 + 254 + # in rst this can refer to any type 255 + (type_fallback, r":c:type:`\1`"), 256 + (type_param_ref, r"**\1\2**") 257 + ] 258 + blankline = "\n" 259 + 260 + sphinx_literal = KernRe(r'^[^.].*::$', cache=False) 261 + sphinx_cblock = KernRe(r'^\.\.\ +code-block::', cache=False) 262 + 263 + def __init__(self): 264 + """ 265 + Creates class variables. 266 + 267 + Not really mandatory, but it is a good coding style and makes 268 + pylint happy. 269 + """ 270 + 271 + super().__init__() 272 + self.lineprefix = "" 273 + 274 + def print_lineno(self, ln): 275 + """Outputs a line number""" 276 + 277 + if self.enable_lineno and ln is not None: 278 + ln += 1 279 + self.data += f".. LINENO {ln}\n" 280 + 281 + def output_highlight(self, args): 282 + """ 283 + Outputs a C symbol that may require being converted to ReST using 284 + the self.highlights variable 285 + """ 286 + 287 + input_text = args 288 + output = "" 289 + in_literal = False 290 + litprefix = "" 291 + block = "" 292 + 293 + for line in input_text.strip("\n").split("\n"): 294 + 295 + # If we're in a literal block, see if we should drop out of it. 296 + # Otherwise, pass the line straight through unmunged. 297 + if in_literal: 298 + if line.strip(): # If the line is not blank 299 + # If this is the first non-blank line in a literal block, 300 + # figure out the proper indent. 301 + if not litprefix: 302 + r = KernRe(r'^(\s*)') 303 + if r.match(line): 304 + litprefix = '^' + r.group(1) 305 + else: 306 + litprefix = "" 307 + 308 + output += line + "\n" 309 + elif not KernRe(litprefix).match(line): 310 + in_literal = False 311 + else: 312 + output += line + "\n" 313 + else: 314 + output += line + "\n" 315 + 316 + # Not in a literal block (or just dropped out) 317 + if not in_literal: 318 + block += line + "\n" 319 + if self.sphinx_literal.match(line) or self.sphinx_cblock.match(line): 320 + in_literal = True 321 + litprefix = "" 322 + output += self.highlight_block(block) 323 + block = "" 324 + 325 + # Handle any remaining block 326 + if block: 327 + output += self.highlight_block(block) 328 + 329 + # Print the output with the line prefix 330 + for line in output.strip("\n").split("\n"): 331 + self.data += self.lineprefix + line + "\n" 332 + 333 + def out_section(self, args, out_docblock=False): 334 + """ 335 + Outputs a block section. 336 + 337 + This could use some work; it's used to output the DOC: sections, and 338 + starts by putting out the name of the doc section itself, but that 339 + tends to duplicate a header already in the template file. 340 + """ 341 + 342 + sectionlist = args.get('sectionlist', []) 343 + sections = args.get('sections', {}) 344 + section_start_lines = args.get('section_start_lines', {}) 345 + 346 + for section in sectionlist: 347 + # Skip sections that are in the nosymbol_table 348 + if section in self.nosymbol: 349 + continue 350 + 351 + if out_docblock: 352 + if not self.out_mode == self.OUTPUT_INCLUDE: 353 + self.data += f".. _{section}:\n\n" 354 + self.data += f'{self.lineprefix}**{section}**\n\n' 355 + else: 356 + self.data += f'{self.lineprefix}**{section}**\n\n' 357 + 358 + self.print_lineno(section_start_lines.get(section, 0)) 359 + self.output_highlight(sections[section]) 360 + self.data += "\n" 361 + self.data += "\n" 362 + 363 + def out_doc(self, fname, name, args): 364 + if not self.check_doc(name, args): 365 + return 366 + self.out_section(args, out_docblock=True) 367 + 368 + def out_function(self, fname, name, args): 369 + 370 + oldprefix = self.lineprefix 371 + signature = "" 372 + 373 + func_macro = args.get('func_macro', False) 374 + if func_macro: 375 + signature = args['function'] 376 + else: 377 + if args.get('functiontype'): 378 + signature = args['functiontype'] + " " 379 + signature += args['function'] + " (" 380 + 381 + parameterlist = args.get('parameterlist', []) 382 + parameterdescs = args.get('parameterdescs', {}) 383 + parameterdesc_start_lines = args.get('parameterdesc_start_lines', {}) 384 + 385 + ln = args.get('declaration_start_line', 0) 386 + 387 + count = 0 388 + for parameter in parameterlist: 389 + if count != 0: 390 + signature += ", " 391 + count += 1 392 + dtype = args['parametertypes'].get(parameter, "") 393 + 394 + if function_pointer.search(dtype): 395 + signature += function_pointer.group(1) + parameter + function_pointer.group(3) 396 + else: 397 + signature += dtype 398 + 399 + if not func_macro: 400 + signature += ")" 401 + 402 + self.print_lineno(ln) 403 + if args.get('typedef') or not args.get('functiontype'): 404 + self.data += f".. c:macro:: {args['function']}\n\n" 405 + 406 + if args.get('typedef'): 407 + self.data += " **Typedef**: " 408 + self.lineprefix = "" 409 + self.output_highlight(args.get('purpose', "")) 410 + self.data += "\n\n**Syntax**\n\n" 411 + self.data += f" ``{signature}``\n\n" 412 + else: 413 + self.data += f"``{signature}``\n\n" 414 + else: 415 + self.data += f".. c:function:: {signature}\n\n" 416 + 417 + if not args.get('typedef'): 418 + self.print_lineno(ln) 419 + self.lineprefix = " " 420 + self.output_highlight(args.get('purpose', "")) 421 + self.data += "\n" 422 + 423 + # Put descriptive text into a container (HTML <div>) to help set 424 + # function prototypes apart 425 + self.lineprefix = " " 426 + 427 + if parameterlist: 428 + self.data += ".. container:: kernelindent\n\n" 429 + self.data += f"{self.lineprefix}**Parameters**\n\n" 430 + 431 + for parameter in parameterlist: 432 + parameter_name = KernRe(r'\[.*').sub('', parameter) 433 + dtype = args['parametertypes'].get(parameter, "") 434 + 435 + if dtype: 436 + self.data += f"{self.lineprefix}``{dtype}``\n" 437 + else: 438 + self.data += f"{self.lineprefix}``{parameter}``\n" 439 + 440 + self.print_lineno(parameterdesc_start_lines.get(parameter_name, 0)) 441 + 442 + self.lineprefix = " " 443 + if parameter_name in parameterdescs and \ 444 + parameterdescs[parameter_name] != KernelDoc.undescribed: 445 + 446 + self.output_highlight(parameterdescs[parameter_name]) 447 + self.data += "\n" 448 + else: 449 + self.data += f"{self.lineprefix}*undescribed*\n\n" 450 + self.lineprefix = " " 451 + 452 + self.out_section(args) 453 + self.lineprefix = oldprefix 454 + 455 + def out_enum(self, fname, name, args): 456 + 457 + oldprefix = self.lineprefix 458 + name = args.get('enum', '') 459 + parameterlist = args.get('parameterlist', []) 460 + parameterdescs = args.get('parameterdescs', {}) 461 + ln = args.get('declaration_start_line', 0) 462 + 463 + self.data += f"\n\n.. c:enum:: {name}\n\n" 464 + 465 + self.print_lineno(ln) 466 + self.lineprefix = " " 467 + self.output_highlight(args.get('purpose', '')) 468 + self.data += "\n" 469 + 470 + self.data += ".. container:: kernelindent\n\n" 471 + outer = self.lineprefix + " " 472 + self.lineprefix = outer + " " 473 + self.data += f"{outer}**Constants**\n\n" 474 + 475 + for parameter in parameterlist: 476 + self.data += f"{outer}``{parameter}``\n" 477 + 478 + if parameterdescs.get(parameter, '') != KernelDoc.undescribed: 479 + self.output_highlight(parameterdescs[parameter]) 480 + else: 481 + self.data += f"{self.lineprefix}*undescribed*\n\n" 482 + self.data += "\n" 483 + 484 + self.lineprefix = oldprefix 485 + self.out_section(args) 486 + 487 + def out_typedef(self, fname, name, args): 488 + 489 + oldprefix = self.lineprefix 490 + name = args.get('typedef', '') 491 + ln = args.get('declaration_start_line', 0) 492 + 493 + self.data += f"\n\n.. c:type:: {name}\n\n" 494 + 495 + self.print_lineno(ln) 496 + self.lineprefix = " " 497 + 498 + self.output_highlight(args.get('purpose', '')) 499 + 500 + self.data += "\n" 501 + 502 + self.lineprefix = oldprefix 503 + self.out_section(args) 504 + 505 + def out_struct(self, fname, name, args): 506 + 507 + name = args.get('struct', "") 508 + purpose = args.get('purpose', "") 509 + declaration = args.get('definition', "") 510 + dtype = args.get('type', "struct") 511 + ln = args.get('declaration_start_line', 0) 512 + 513 + parameterlist = args.get('parameterlist', []) 514 + parameterdescs = args.get('parameterdescs', {}) 515 + parameterdesc_start_lines = args.get('parameterdesc_start_lines', {}) 516 + 517 + self.data += f"\n\n.. c:{dtype}:: {name}\n\n" 518 + 519 + self.print_lineno(ln) 520 + 521 + oldprefix = self.lineprefix 522 + self.lineprefix += " " 523 + 524 + self.output_highlight(purpose) 525 + self.data += "\n" 526 + 527 + self.data += ".. container:: kernelindent\n\n" 528 + self.data += f"{self.lineprefix}**Definition**::\n\n" 529 + 530 + self.lineprefix = self.lineprefix + " " 531 + 532 + declaration = declaration.replace("\t", self.lineprefix) 533 + 534 + self.data += f"{self.lineprefix}{dtype} {name}" + ' {' + "\n" 535 + self.data += f"{declaration}{self.lineprefix}" + "};\n\n" 536 + 537 + self.lineprefix = " " 538 + self.data += f"{self.lineprefix}**Members**\n\n" 539 + for parameter in parameterlist: 540 + if not parameter or parameter.startswith("#"): 541 + continue 542 + 543 + parameter_name = parameter.split("[", maxsplit=1)[0] 544 + 545 + if parameterdescs.get(parameter_name) == KernelDoc.undescribed: 546 + continue 547 + 548 + self.print_lineno(parameterdesc_start_lines.get(parameter_name, 0)) 549 + 550 + self.data += f"{self.lineprefix}``{parameter}``\n" 551 + 552 + self.lineprefix = " " 553 + self.output_highlight(parameterdescs[parameter_name]) 554 + self.lineprefix = " " 555 + 556 + self.data += "\n" 557 + 558 + self.data += "\n" 559 + 560 + self.lineprefix = oldprefix 561 + self.out_section(args) 562 + 563 + 564 + class ManFormat(OutputFormat): 565 + """Consts and functions used by man pages output""" 566 + 567 + highlights = ( 568 + (type_constant, r"\1"), 569 + (type_constant2, r"\1"), 570 + (type_func, r"\\fB\1\\fP"), 571 + (type_enum, r"\\fI\1\\fP"), 572 + (type_struct, r"\\fI\1\\fP"), 573 + (type_typedef, r"\\fI\1\\fP"), 574 + (type_union, r"\\fI\1\\fP"), 575 + (type_param, r"\\fI\1\\fP"), 576 + (type_param_ref, r"\\fI\1\2\\fP"), 577 + (type_member, r"\\fI\1\2\3\\fP"), 578 + (type_fallback, r"\\fI\1\\fP") 579 + ) 580 + blankline = "" 581 + 582 + date_formats = [ 583 + "%a %b %d %H:%M:%S %Z %Y", 584 + "%a %b %d %H:%M:%S %Y", 585 + "%Y-%m-%d", 586 + "%b %d %Y", 587 + "%B %d %Y", 588 + "%m %d %Y", 589 + ] 590 + 591 + def __init__(self, modulename): 592 + """ 593 + Creates class variables. 594 + 595 + Not really mandatory, but it is a good coding style and makes 596 + pylint happy. 597 + """ 598 + 599 + super().__init__() 600 + self.modulename = modulename 601 + 602 + dt = None 603 + tstamp = os.environ.get("KBUILD_BUILD_TIMESTAMP") 604 + if tstamp: 605 + for fmt in self.date_formats: 606 + try: 607 + dt = datetime.strptime(tstamp, fmt) 608 + break 609 + except ValueError: 610 + pass 611 + 612 + if not dt: 613 + dt = datetime.now() 614 + 615 + self.man_date = dt.strftime("%B %Y") 616 + 617 + def output_highlight(self, block): 618 + """ 619 + Outputs a C symbol that may require being highlighted with 620 + self.highlights variable using troff syntax 621 + """ 622 + 623 + contents = self.highlight_block(block) 624 + 625 + if isinstance(contents, list): 626 + contents = "\n".join(contents) 627 + 628 + for line in contents.strip("\n").split("\n"): 629 + line = KernRe(r"^\s*").sub("", line) 630 + if not line: 631 + continue 632 + 633 + if line[0] == ".": 634 + self.data += "\\&" + line + "\n" 635 + else: 636 + self.data += line + "\n" 637 + 638 + def out_doc(self, fname, name, args): 639 + sectionlist = args.get('sectionlist', []) 640 + sections = args.get('sections', {}) 641 + 642 + if not self.check_doc(name, args): 643 + return 644 + 645 + self.data += f'.TH "{self.modulename}" 9 "{self.modulename}" "{self.man_date}" "API Manual" LINUX' + "\n" 646 + 647 + for section in sectionlist: 648 + self.data += f'.SH "{section}"' + "\n" 649 + self.output_highlight(sections.get(section)) 650 + 651 + def out_function(self, fname, name, args): 652 + """output function in man""" 653 + 654 + parameterlist = args.get('parameterlist', []) 655 + parameterdescs = args.get('parameterdescs', {}) 656 + sectionlist = args.get('sectionlist', []) 657 + sections = args.get('sections', {}) 658 + 659 + self.data += f'.TH "{args["function"]}" 9 "{args["function"]}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n" 660 + 661 + self.data += ".SH NAME\n" 662 + self.data += f"{args['function']} \\- {args['purpose']}\n" 663 + 664 + self.data += ".SH SYNOPSIS\n" 665 + if args.get('functiontype', ''): 666 + self.data += f'.B "{args["functiontype"]}" {args["function"]}' + "\n" 667 + else: 668 + self.data += f'.B "{args["function"]}' + "\n" 669 + 670 + count = 0 671 + parenth = "(" 672 + post = "," 673 + 674 + for parameter in parameterlist: 675 + if count == len(parameterlist) - 1: 676 + post = ");" 677 + 678 + dtype = args['parametertypes'].get(parameter, "") 679 + if function_pointer.match(dtype): 680 + # Pointer-to-function 681 + self.data += f'".BI "{parenth}{function_pointer.group(1)}" " ") ({function_pointer.group(2)}){post}"' + "\n" 682 + else: 683 + dtype = KernRe(r'([^\*])$').sub(r'\1 ', dtype) 684 + 685 + self.data += f'.BI "{parenth}{dtype}" "{post}"' + "\n" 686 + count += 1 687 + parenth = "" 688 + 689 + if parameterlist: 690 + self.data += ".SH ARGUMENTS\n" 691 + 692 + for parameter in parameterlist: 693 + parameter_name = re.sub(r'\[.*', '', parameter) 694 + 695 + self.data += f'.IP "{parameter}" 12' + "\n" 696 + self.output_highlight(parameterdescs.get(parameter_name, "")) 697 + 698 + for section in sectionlist: 699 + self.data += f'.SH "{section.upper()}"' + "\n" 700 + self.output_highlight(sections[section]) 701 + 702 + def out_enum(self, fname, name, args): 703 + 704 + name = args.get('enum', '') 705 + parameterlist = args.get('parameterlist', []) 706 + sectionlist = args.get('sectionlist', []) 707 + sections = args.get('sections', {}) 708 + 709 + self.data += f'.TH "{self.modulename}" 9 "enum {args["enum"]}" "{self.man_date}" "API Manual" LINUX' + "\n" 710 + 711 + self.data += ".SH NAME\n" 712 + self.data += f"enum {args['enum']} \\- {args['purpose']}\n" 713 + 714 + self.data += ".SH SYNOPSIS\n" 715 + self.data += f"enum {args['enum']}" + " {\n" 716 + 717 + count = 0 718 + for parameter in parameterlist: 719 + self.data += f'.br\n.BI " {parameter}"' + "\n" 720 + if count == len(parameterlist) - 1: 721 + self.data += "\n};\n" 722 + else: 723 + self.data += ", \n.br\n" 724 + 725 + count += 1 726 + 727 + self.data += ".SH Constants\n" 728 + 729 + for parameter in parameterlist: 730 + parameter_name = KernRe(r'\[.*').sub('', parameter) 731 + self.data += f'.IP "{parameter}" 12' + "\n" 732 + self.output_highlight(args['parameterdescs'].get(parameter_name, "")) 733 + 734 + for section in sectionlist: 735 + self.data += f'.SH "{section}"' + "\n" 736 + self.output_highlight(sections[section]) 737 + 738 + def out_typedef(self, fname, name, args): 739 + module = self.modulename 740 + typedef = args.get('typedef') 741 + purpose = args.get('purpose') 742 + sectionlist = args.get('sectionlist', []) 743 + sections = args.get('sections', {}) 744 + 745 + self.data += f'.TH "{module}" 9 "{typedef}" "{self.man_date}" "API Manual" LINUX' + "\n" 746 + 747 + self.data += ".SH NAME\n" 748 + self.data += f"typedef {typedef} \\- {purpose}\n" 749 + 750 + for section in sectionlist: 751 + self.data += f'.SH "{section}"' + "\n" 752 + self.output_highlight(sections.get(section)) 753 + 754 + def out_struct(self, fname, name, args): 755 + module = self.modulename 756 + struct_type = args.get('type') 757 + struct_name = args.get('struct') 758 + purpose = args.get('purpose') 759 + definition = args.get('definition') 760 + sectionlist = args.get('sectionlist', []) 761 + parameterlist = args.get('parameterlist', []) 762 + sections = args.get('sections', {}) 763 + parameterdescs = args.get('parameterdescs', {}) 764 + 765 + self.data += f'.TH "{module}" 9 "{struct_type} {struct_name}" "{self.man_date}" "API Manual" LINUX' + "\n" 766 + 767 + self.data += ".SH NAME\n" 768 + self.data += f"{struct_type} {struct_name} \\- {purpose}\n" 769 + 770 + # Replace tabs with two spaces and handle newlines 771 + declaration = definition.replace("\t", " ") 772 + declaration = KernRe(r"\n").sub('"\n.br\n.BI "', declaration) 773 + 774 + self.data += ".SH SYNOPSIS\n" 775 + self.data += f"{struct_type} {struct_name} " + "{" + "\n.br\n" 776 + self.data += f'.BI "{declaration}\n' + "};\n.br\n\n" 777 + 778 + self.data += ".SH Members\n" 779 + for parameter in parameterlist: 780 + if parameter.startswith("#"): 781 + continue 782 + 783 + parameter_name = re.sub(r"\[.*", "", parameter) 784 + 785 + if parameterdescs.get(parameter_name) == KernelDoc.undescribed: 786 + continue 787 + 788 + self.data += f'.IP "{parameter}" 12' + "\n" 789 + self.output_highlight(parameterdescs.get(parameter_name)) 790 + 791 + for section in sectionlist: 792 + self.data += f'.SH "{section}"' + "\n" 793 + self.output_highlight(sections.get(section))
+1745
scripts/lib/kdoc/kdoc_parser.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + # Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>. 4 + # 5 + # pylint: disable=C0301,C0302,R0904,R0912,R0913,R0914,R0915,R0917,R1702 6 + 7 + """ 8 + kdoc_parser 9 + =========== 10 + 11 + Read a C language source or header FILE and extract embedded 12 + documentation comments 13 + """ 14 + 15 + import re 16 + from pprint import pformat 17 + 18 + from kdoc_re import NestedMatch, KernRe 19 + 20 + 21 + # 22 + # Regular expressions used to parse kernel-doc markups at KernelDoc class. 23 + # 24 + # Let's declare them in lowercase outside any class to make easier to 25 + # convert from the python script. 26 + # 27 + # As those are evaluated at the beginning, no need to cache them 28 + # 29 + 30 + # Allow whitespace at end of comment start. 31 + doc_start = KernRe(r'^/\*\*\s*$', cache=False) 32 + 33 + doc_end = KernRe(r'\*/', cache=False) 34 + doc_com = KernRe(r'\s*\*\s*', cache=False) 35 + doc_com_body = KernRe(r'\s*\* ?', cache=False) 36 + doc_decl = doc_com + KernRe(r'(\w+)', cache=False) 37 + 38 + # @params and a strictly limited set of supported section names 39 + # Specifically: 40 + # Match @word: 41 + # @...: 42 + # @{section-name}: 43 + # while trying to not match literal block starts like "example::" 44 + # 45 + doc_sect = doc_com + \ 46 + KernRe(r'\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:([^:].*)?$', 47 + flags=re.I, cache=False) 48 + 49 + doc_content = doc_com_body + KernRe(r'(.*)', cache=False) 50 + doc_block = doc_com + KernRe(r'DOC:\s*(.*)?', cache=False) 51 + doc_inline_start = KernRe(r'^\s*/\*\*\s*$', cache=False) 52 + doc_inline_sect = KernRe(r'\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)', cache=False) 53 + doc_inline_end = KernRe(r'^\s*\*/\s*$', cache=False) 54 + doc_inline_oneline = KernRe(r'^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$', cache=False) 55 + attribute = KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", 56 + flags=re.I | re.S, cache=False) 57 + 58 + export_symbol = KernRe(r'^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*', cache=False) 59 + export_symbol_ns = KernRe(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+"\)\s*', cache=False) 60 + 61 + type_param = KernRe(r"\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False) 62 + 63 + class state: 64 + """ 65 + State machine enums 66 + """ 67 + 68 + # Parser states 69 + NORMAL = 0 # normal code 70 + NAME = 1 # looking for function name 71 + BODY_MAYBE = 2 # body - or maybe more description 72 + BODY = 3 # the body of the comment 73 + BODY_WITH_BLANK_LINE = 4 # the body which has a blank line 74 + PROTO = 5 # scanning prototype 75 + DOCBLOCK = 6 # documentation block 76 + INLINE = 7 # gathering doc outside main block 77 + 78 + name = [ 79 + "NORMAL", 80 + "NAME", 81 + "BODY_MAYBE", 82 + "BODY", 83 + "BODY_WITH_BLANK_LINE", 84 + "PROTO", 85 + "DOCBLOCK", 86 + "INLINE", 87 + ] 88 + 89 + # Inline documentation state 90 + INLINE_NA = 0 # not applicable ($state != INLINE) 91 + INLINE_NAME = 1 # looking for member name (@foo:) 92 + INLINE_TEXT = 2 # looking for member documentation 93 + INLINE_END = 3 # done 94 + INLINE_ERROR = 4 # error - Comment without header was found. 95 + # Spit a warning as it's not 96 + # proper kernel-doc and ignore the rest. 97 + 98 + inline_name = [ 99 + "", 100 + "_NAME", 101 + "_TEXT", 102 + "_END", 103 + "_ERROR", 104 + ] 105 + 106 + SECTION_DEFAULT = "Description" # default section 107 + 108 + class KernelEntry: 109 + 110 + def __init__(self, config, ln): 111 + self.config = config 112 + 113 + self.contents = "" 114 + self.function = "" 115 + self.sectcheck = "" 116 + self.struct_actual = "" 117 + self.prototype = "" 118 + 119 + self.warnings = [] 120 + 121 + self.parameterlist = [] 122 + self.parameterdescs = {} 123 + self.parametertypes = {} 124 + self.parameterdesc_start_lines = {} 125 + 126 + self.section_start_lines = {} 127 + self.sectionlist = [] 128 + self.sections = {} 129 + 130 + self.anon_struct_union = False 131 + 132 + self.leading_space = None 133 + 134 + # State flags 135 + self.brcount = 0 136 + 137 + self.in_doc_sect = False 138 + self.declaration_start_line = ln + 1 139 + 140 + # TODO: rename to emit_message after removal of kernel-doc.pl 141 + def emit_msg(self, log_msg, warning=True): 142 + """Emit a message""" 143 + 144 + if not warning: 145 + self.config.log.info(log_msg) 146 + return 147 + 148 + # Delegate warning output to output logic, as this way it 149 + # will report warnings/info only for symbols that are output 150 + 151 + self.warnings.append(log_msg) 152 + return 153 + 154 + def dump_section(self, start_new=True): 155 + """ 156 + Dumps section contents to arrays/hashes intended for that purpose. 157 + """ 158 + 159 + name = self.section 160 + contents = self.contents 161 + 162 + if type_param.match(name): 163 + name = type_param.group(1) 164 + 165 + self.parameterdescs[name] = contents 166 + self.parameterdesc_start_lines[name] = self.new_start_line 167 + 168 + self.sectcheck += name + " " 169 + self.new_start_line = 0 170 + 171 + elif name == "@...": 172 + name = "..." 173 + self.parameterdescs[name] = contents 174 + self.sectcheck += name + " " 175 + self.parameterdesc_start_lines[name] = self.new_start_line 176 + self.new_start_line = 0 177 + 178 + else: 179 + if name in self.sections and self.sections[name] != "": 180 + # Only warn on user-specified duplicate section names 181 + if name != SECTION_DEFAULT: 182 + self.emit_msg(self.new_start_line, 183 + f"duplicate section name '{name}'\n") 184 + self.sections[name] += contents 185 + else: 186 + self.sections[name] = contents 187 + self.sectionlist.append(name) 188 + self.section_start_lines[name] = self.new_start_line 189 + self.new_start_line = 0 190 + 191 + # self.config.log.debug("Section: %s : %s", name, pformat(vars(self))) 192 + 193 + if start_new: 194 + self.section = SECTION_DEFAULT 195 + self.contents = "" 196 + 197 + 198 + class KernelDoc: 199 + """ 200 + Read a C language source or header FILE and extract embedded 201 + documentation comments. 202 + """ 203 + 204 + # Section names 205 + 206 + section_intro = "Introduction" 207 + section_context = "Context" 208 + section_return = "Return" 209 + 210 + undescribed = "-- undescribed --" 211 + 212 + def __init__(self, config, fname): 213 + """Initialize internal variables""" 214 + 215 + self.fname = fname 216 + self.config = config 217 + 218 + # Initial state for the state machines 219 + self.state = state.NORMAL 220 + self.inline_doc_state = state.INLINE_NA 221 + 222 + # Store entry currently being processed 223 + self.entry = None 224 + 225 + # Place all potential outputs into an array 226 + self.entries = [] 227 + 228 + def emit_msg(self, ln, msg, warning=True): 229 + """Emit a message""" 230 + 231 + log_msg = f"{self.fname}:{ln} {msg}" 232 + 233 + if self.entry: 234 + self.entry.emit_msg(log_msg, warning) 235 + return 236 + 237 + if warning: 238 + self.config.log.warning(log_msg) 239 + else: 240 + self.config.log.info(log_msg) 241 + 242 + def dump_section(self, start_new=True): 243 + """ 244 + Dumps section contents to arrays/hashes intended for that purpose. 245 + """ 246 + 247 + if self.entry: 248 + self.entry.dump_section(start_new) 249 + 250 + # TODO: rename it to store_declaration after removal of kernel-doc.pl 251 + def output_declaration(self, dtype, name, **args): 252 + """ 253 + Stores the entry into an entry array. 254 + 255 + The actual output and output filters will be handled elsewhere 256 + """ 257 + 258 + # The implementation here is different than the original kernel-doc: 259 + # instead of checking for output filters or actually output anything, 260 + # it just stores the declaration content at self.entries, as the 261 + # output will happen on a separate class. 262 + # 263 + # For now, we're keeping the same name of the function just to make 264 + # easier to compare the source code of both scripts 265 + 266 + args["declaration_start_line"] = self.entry.declaration_start_line 267 + args["type"] = dtype 268 + args["warnings"] = self.entry.warnings 269 + 270 + # TODO: use colletions.OrderedDict to remove sectionlist 271 + 272 + sections = args.get('sections', {}) 273 + sectionlist = args.get('sectionlist', []) 274 + 275 + # Drop empty sections 276 + # TODO: improve empty sections logic to emit warnings 277 + for section in ["Description", "Return"]: 278 + if section in sectionlist: 279 + if not sections[section].rstrip(): 280 + del sections[section] 281 + sectionlist.remove(section) 282 + 283 + self.entries.append((name, args)) 284 + 285 + self.config.log.debug("Output: %s:%s = %s", dtype, name, pformat(args)) 286 + 287 + def reset_state(self, ln): 288 + """ 289 + Ancillary routine to create a new entry. It initializes all 290 + variables used by the state machine. 291 + """ 292 + 293 + self.entry = KernelEntry(self.config, ln) 294 + 295 + # State flags 296 + self.state = state.NORMAL 297 + self.inline_doc_state = state.INLINE_NA 298 + 299 + def push_parameter(self, ln, decl_type, param, dtype, 300 + org_arg, declaration_name): 301 + """ 302 + Store parameters and their descriptions at self.entry. 303 + """ 304 + 305 + if self.entry.anon_struct_union and dtype == "" and param == "}": 306 + return # Ignore the ending }; from anonymous struct/union 307 + 308 + self.entry.anon_struct_union = False 309 + 310 + param = KernRe(r'[\[\)].*').sub('', param, count=1) 311 + 312 + if dtype == "" and param.endswith("..."): 313 + if KernRe(r'\w\.\.\.$').search(param): 314 + # For named variable parameters of the form `x...`, 315 + # remove the dots 316 + param = param[:-3] 317 + else: 318 + # Handles unnamed variable parameters 319 + param = "..." 320 + 321 + if param not in self.entry.parameterdescs or \ 322 + not self.entry.parameterdescs[param]: 323 + 324 + self.entry.parameterdescs[param] = "variable arguments" 325 + 326 + elif dtype == "" and (not param or param == "void"): 327 + param = "void" 328 + self.entry.parameterdescs[param] = "no arguments" 329 + 330 + elif dtype == "" and param in ["struct", "union"]: 331 + # Handle unnamed (anonymous) union or struct 332 + dtype = param 333 + param = "{unnamed_" + param + "}" 334 + self.entry.parameterdescs[param] = "anonymous\n" 335 + self.entry.anon_struct_union = True 336 + 337 + # Handle cache group enforcing variables: they do not need 338 + # to be described in header files 339 + elif "__cacheline_group" in param: 340 + # Ignore __cacheline_group_begin and __cacheline_group_end 341 + return 342 + 343 + # Warn if parameter has no description 344 + # (but ignore ones starting with # as these are not parameters 345 + # but inline preprocessor statements) 346 + if param not in self.entry.parameterdescs and not param.startswith("#"): 347 + self.entry.parameterdescs[param] = self.undescribed 348 + 349 + if "." not in param: 350 + if decl_type == 'function': 351 + dname = f"{decl_type} parameter" 352 + else: 353 + dname = f"{decl_type} member" 354 + 355 + self.emit_msg(ln, 356 + f"{dname} '{param}' not described in '{declaration_name}'") 357 + 358 + # Strip spaces from param so that it is one continuous string on 359 + # parameterlist. This fixes a problem where check_sections() 360 + # cannot find a parameter like "addr[6 + 2]" because it actually 361 + # appears as "addr[6", "+", "2]" on the parameter list. 362 + # However, it's better to maintain the param string unchanged for 363 + # output, so just weaken the string compare in check_sections() 364 + # to ignore "[blah" in a parameter string. 365 + 366 + self.entry.parameterlist.append(param) 367 + org_arg = KernRe(r'\s\s+').sub(' ', org_arg) 368 + self.entry.parametertypes[param] = org_arg 369 + 370 + def save_struct_actual(self, actual): 371 + """ 372 + Strip all spaces from the actual param so that it looks like 373 + one string item. 374 + """ 375 + 376 + actual = KernRe(r'\s*').sub("", actual, count=1) 377 + 378 + self.entry.struct_actual += actual + " " 379 + 380 + def create_parameter_list(self, ln, decl_type, args, 381 + splitter, declaration_name): 382 + """ 383 + Creates a list of parameters, storing them at self.entry. 384 + """ 385 + 386 + # temporarily replace all commas inside function pointer definition 387 + arg_expr = KernRe(r'(\([^\),]+),') 388 + while arg_expr.search(args): 389 + args = arg_expr.sub(r"\1#", args) 390 + 391 + for arg in args.split(splitter): 392 + # Strip comments 393 + arg = KernRe(r'\/\*.*\*\/').sub('', arg) 394 + 395 + # Ignore argument attributes 396 + arg = KernRe(r'\sPOS0?\s').sub(' ', arg) 397 + 398 + # Strip leading/trailing spaces 399 + arg = arg.strip() 400 + arg = KernRe(r'\s+').sub(' ', arg, count=1) 401 + 402 + if arg.startswith('#'): 403 + # Treat preprocessor directive as a typeless variable just to fill 404 + # corresponding data structures "correctly". Catch it later in 405 + # output_* subs. 406 + 407 + # Treat preprocessor directive as a typeless variable 408 + self.push_parameter(ln, decl_type, arg, "", 409 + "", declaration_name) 410 + 411 + elif KernRe(r'\(.+\)\s*\(').search(arg): 412 + # Pointer-to-function 413 + 414 + arg = arg.replace('#', ',') 415 + 416 + r = KernRe(r'[^\(]+\(\*?\s*([\w\[\]\.]*)\s*\)') 417 + if r.match(arg): 418 + param = r.group(1) 419 + else: 420 + self.emit_msg(ln, f"Invalid param: {arg}") 421 + param = arg 422 + 423 + dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) 424 + self.save_struct_actual(param) 425 + self.push_parameter(ln, decl_type, param, dtype, 426 + arg, declaration_name) 427 + 428 + elif KernRe(r'\(.+\)\s*\[').search(arg): 429 + # Array-of-pointers 430 + 431 + arg = arg.replace('#', ',') 432 + r = KernRe(r'[^\(]+\(\s*\*\s*([\w\[\]\.]*?)\s*(\s*\[\s*[\w]+\s*\]\s*)*\)') 433 + if r.match(arg): 434 + param = r.group(1) 435 + else: 436 + self.emit_msg(ln, f"Invalid param: {arg}") 437 + param = arg 438 + 439 + dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) 440 + 441 + self.save_struct_actual(param) 442 + self.push_parameter(ln, decl_type, param, dtype, 443 + arg, declaration_name) 444 + 445 + elif arg: 446 + arg = KernRe(r'\s*:\s*').sub(":", arg) 447 + arg = KernRe(r'\s*\[').sub('[', arg) 448 + 449 + args = KernRe(r'\s*,\s*').split(arg) 450 + if args[0] and '*' in args[0]: 451 + args[0] = re.sub(r'(\*+)\s*', r' \1', args[0]) 452 + 453 + first_arg = [] 454 + r = KernRe(r'^(.*\s+)(.*?\[.*\].*)$') 455 + if args[0] and r.match(args[0]): 456 + args.pop(0) 457 + first_arg.extend(r.group(1)) 458 + first_arg.append(r.group(2)) 459 + else: 460 + first_arg = KernRe(r'\s+').split(args.pop(0)) 461 + 462 + args.insert(0, first_arg.pop()) 463 + dtype = ' '.join(first_arg) 464 + 465 + for param in args: 466 + if KernRe(r'^(\*+)\s*(.*)').match(param): 467 + r = KernRe(r'^(\*+)\s*(.*)') 468 + if not r.match(param): 469 + self.emit_msg(ln, f"Invalid param: {param}") 470 + continue 471 + 472 + param = r.group(1) 473 + 474 + self.save_struct_actual(r.group(2)) 475 + self.push_parameter(ln, decl_type, r.group(2), 476 + f"{dtype} {r.group(1)}", 477 + arg, declaration_name) 478 + 479 + elif KernRe(r'(.*?):(\w+)').search(param): 480 + r = KernRe(r'(.*?):(\w+)') 481 + if not r.match(param): 482 + self.emit_msg(ln, f"Invalid param: {param}") 483 + continue 484 + 485 + if dtype != "": # Skip unnamed bit-fields 486 + self.save_struct_actual(r.group(1)) 487 + self.push_parameter(ln, decl_type, r.group(1), 488 + f"{dtype}:{r.group(2)}", 489 + arg, declaration_name) 490 + else: 491 + self.save_struct_actual(param) 492 + self.push_parameter(ln, decl_type, param, dtype, 493 + arg, declaration_name) 494 + 495 + def check_sections(self, ln, decl_name, decl_type, sectcheck, prmscheck): 496 + """ 497 + Check for errors inside sections, emitting warnings if not found 498 + parameters are described. 499 + """ 500 + 501 + sects = sectcheck.split() 502 + prms = prmscheck.split() 503 + err = False 504 + 505 + for sx in range(len(sects)): # pylint: disable=C0200 506 + err = True 507 + for px in range(len(prms)): # pylint: disable=C0200 508 + prm_clean = prms[px] 509 + prm_clean = KernRe(r'\[.*\]').sub('', prm_clean) 510 + prm_clean = attribute.sub('', prm_clean) 511 + 512 + # ignore array size in a parameter string; 513 + # however, the original param string may contain 514 + # spaces, e.g.: addr[6 + 2] 515 + # and this appears in @prms as "addr[6" since the 516 + # parameter list is split at spaces; 517 + # hence just ignore "[..." for the sections check; 518 + prm_clean = KernRe(r'\[.*').sub('', prm_clean) 519 + 520 + if prm_clean == sects[sx]: 521 + err = False 522 + break 523 + 524 + if err: 525 + if decl_type == 'function': 526 + dname = f"{decl_type} parameter" 527 + else: 528 + dname = f"{decl_type} member" 529 + 530 + self.emit_msg(ln, 531 + f"Excess {dname} '{sects[sx]}' description in '{decl_name}'") 532 + 533 + def check_return_section(self, ln, declaration_name, return_type): 534 + """ 535 + If the function doesn't return void, warns about the lack of a 536 + return description. 537 + """ 538 + 539 + if not self.config.wreturn: 540 + return 541 + 542 + # Ignore an empty return type (It's a macro) 543 + # Ignore functions with a "void" return type (but not "void *") 544 + if not return_type or KernRe(r'void\s*\w*\s*$').search(return_type): 545 + return 546 + 547 + if not self.entry.sections.get("Return", None): 548 + self.emit_msg(ln, 549 + f"No description found for return value of '{declaration_name}'") 550 + 551 + def dump_struct(self, ln, proto): 552 + """ 553 + Store an entry for an struct or union 554 + """ 555 + 556 + type_pattern = r'(struct|union)' 557 + 558 + qualifiers = [ 559 + "__attribute__", 560 + "__packed", 561 + "__aligned", 562 + "____cacheline_aligned_in_smp", 563 + "____cacheline_aligned", 564 + ] 565 + 566 + definition_body = r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) + ")?" 567 + struct_members = KernRe(type_pattern + r'([^\{\};]+)(\{)([^\{\}]*)(\})([^\{\}\;]*)(\;)') 568 + 569 + # Extract struct/union definition 570 + members = None 571 + declaration_name = None 572 + decl_type = None 573 + 574 + r = KernRe(type_pattern + r'\s+(\w+)\s*' + definition_body) 575 + if r.search(proto): 576 + decl_type = r.group(1) 577 + declaration_name = r.group(2) 578 + members = r.group(3) 579 + else: 580 + r = KernRe(r'typedef\s+' + type_pattern + r'\s*' + definition_body + r'\s*(\w+)\s*;') 581 + 582 + if r.search(proto): 583 + decl_type = r.group(1) 584 + declaration_name = r.group(3) 585 + members = r.group(2) 586 + 587 + if not members: 588 + self.emit_msg(ln, f"{proto} error: Cannot parse struct or union!") 589 + return 590 + 591 + if self.entry.identifier != declaration_name: 592 + self.emit_msg(ln, 593 + f"expecting prototype for {decl_type} {self.entry.identifier}. Prototype was for {decl_type} {declaration_name} instead\n") 594 + return 595 + 596 + args_pattern = r'([^,)]+)' 597 + 598 + sub_prefixes = [ 599 + (KernRe(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', re.S | re.I), ''), 600 + (KernRe(r'\/\*\s*private:.*', re.S | re.I), ''), 601 + 602 + # Strip comments 603 + (KernRe(r'\/\*.*?\*\/', re.S), ''), 604 + 605 + # Strip attributes 606 + (attribute, ' '), 607 + (KernRe(r'\s*__aligned\s*\([^;]*\)', re.S), ' '), 608 + (KernRe(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '), 609 + (KernRe(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '), 610 + (KernRe(r'\s*__packed\s*', re.S), ' '), 611 + (KernRe(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '), 612 + (KernRe(r'\s*____cacheline_aligned_in_smp', re.S), ' '), 613 + (KernRe(r'\s*____cacheline_aligned', re.S), ' '), 614 + 615 + # Unwrap struct_group macros based on this definition: 616 + # __struct_group(TAG, NAME, ATTRS, MEMBERS...) 617 + # which has variants like: struct_group(NAME, MEMBERS...) 618 + # Only MEMBERS arguments require documentation. 619 + # 620 + # Parsing them happens on two steps: 621 + # 622 + # 1. drop struct group arguments that aren't at MEMBERS, 623 + # storing them as STRUCT_GROUP(MEMBERS) 624 + # 625 + # 2. remove STRUCT_GROUP() ancillary macro. 626 + # 627 + # The original logic used to remove STRUCT_GROUP() using an 628 + # advanced regex: 629 + # 630 + # \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*; 631 + # 632 + # with two patterns that are incompatible with 633 + # Python re module, as it has: 634 + # 635 + # - a recursive pattern: (?1) 636 + # - an atomic grouping: (?>...) 637 + # 638 + # I tried a simpler version: but it didn't work either: 639 + # \bSTRUCT_GROUP\(([^\)]+)\)[^;]*; 640 + # 641 + # As it doesn't properly match the end parenthesis on some cases. 642 + # 643 + # So, a better solution was crafted: there's now a NestedMatch 644 + # class that ensures that delimiters after a search are properly 645 + # matched. So, the implementation to drop STRUCT_GROUP() will be 646 + # handled in separate. 647 + 648 + (KernRe(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('), 649 + (KernRe(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_GROUP('), 650 + (KernRe(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r'struct \1 \2; STRUCT_GROUP('), 651 + (KernRe(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROUP('), 652 + 653 + # Replace macros 654 + # 655 + # TODO: use NestedMatch for FOO($1, $2, ...) matches 656 + # 657 + # it is better to also move those to the NestedMatch logic, 658 + # to ensure that parenthesis will be properly matched. 659 + 660 + (KernRe(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S), r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'), 661 + (KernRe(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S), r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'), 662 + (KernRe(r'DECLARE_BITMAP\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'), 663 + (KernRe(r'DECLARE_HASHTABLE\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[1 << ((\2) - 1)]'), 664 + (KernRe(r'DECLARE_KFIFO\s*\(' + args_pattern + r',\s*' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'), 665 + (KernRe(r'DECLARE_KFIFO_PTR\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'), 666 + (KernRe(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\1 \2[]'), 667 + (KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + args_pattern + r'\)', re.S), r'dma_addr_t \1'), 668 + (KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + args_pattern + r'\)', re.S), r'__u32 \1'), 669 + ] 670 + 671 + # Regexes here are guaranteed to have the end limiter matching 672 + # the start delimiter. Yet, right now, only one replace group 673 + # is allowed. 674 + 675 + sub_nested_prefixes = [ 676 + (re.compile(r'\bSTRUCT_GROUP\('), r'\1'), 677 + ] 678 + 679 + for search, sub in sub_prefixes: 680 + members = search.sub(sub, members) 681 + 682 + nested = NestedMatch() 683 + 684 + for search, sub in sub_nested_prefixes: 685 + members = nested.sub(search, sub, members) 686 + 687 + # Keeps the original declaration as-is 688 + declaration = members 689 + 690 + # Split nested struct/union elements 691 + # 692 + # This loop was simpler at the original kernel-doc perl version, as 693 + # while ($members =~ m/$struct_members/) { ... } 694 + # reads 'members' string on each interaction. 695 + # 696 + # Python behavior is different: it parses 'members' only once, 697 + # creating a list of tuples from the first interaction. 698 + # 699 + # On other words, this won't get nested structs. 700 + # 701 + # So, we need to have an extra loop on Python to override such 702 + # re limitation. 703 + 704 + while True: 705 + tuples = struct_members.findall(members) 706 + if not tuples: 707 + break 708 + 709 + for t in tuples: 710 + newmember = "" 711 + maintype = t[0] 712 + s_ids = t[5] 713 + content = t[3] 714 + 715 + oldmember = "".join(t) 716 + 717 + for s_id in s_ids.split(','): 718 + s_id = s_id.strip() 719 + 720 + newmember += f"{maintype} {s_id}; " 721 + s_id = KernRe(r'[:\[].*').sub('', s_id) 722 + s_id = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', s_id) 723 + 724 + for arg in content.split(';'): 725 + arg = arg.strip() 726 + 727 + if not arg: 728 + continue 729 + 730 + r = KernRe(r'^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)') 731 + if r.match(arg): 732 + # Pointer-to-function 733 + dtype = r.group(1) 734 + name = r.group(2) 735 + extra = r.group(3) 736 + 737 + if not name: 738 + continue 739 + 740 + if not s_id: 741 + # Anonymous struct/union 742 + newmember += f"{dtype}{name}{extra}; " 743 + else: 744 + newmember += f"{dtype}{s_id}.{name}{extra}; " 745 + 746 + else: 747 + arg = arg.strip() 748 + # Handle bitmaps 749 + arg = KernRe(r':\s*\d+\s*').sub('', arg) 750 + 751 + # Handle arrays 752 + arg = KernRe(r'\[.*\]').sub('', arg) 753 + 754 + # Handle multiple IDs 755 + arg = KernRe(r'\s*,\s*').sub(',', arg) 756 + 757 + r = KernRe(r'(.*)\s+([\S+,]+)') 758 + 759 + if r.search(arg): 760 + dtype = r.group(1) 761 + names = r.group(2) 762 + else: 763 + newmember += f"{arg}; " 764 + continue 765 + 766 + for name in names.split(','): 767 + name = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', name).strip() 768 + 769 + if not name: 770 + continue 771 + 772 + if not s_id: 773 + # Anonymous struct/union 774 + newmember += f"{dtype} {name}; " 775 + else: 776 + newmember += f"{dtype} {s_id}.{name}; " 777 + 778 + members = members.replace(oldmember, newmember) 779 + 780 + # Ignore other nested elements, like enums 781 + members = re.sub(r'(\{[^\{\}]*\})', '', members) 782 + 783 + self.create_parameter_list(ln, decl_type, members, ';', 784 + declaration_name) 785 + self.check_sections(ln, declaration_name, decl_type, 786 + self.entry.sectcheck, self.entry.struct_actual) 787 + 788 + # Adjust declaration for better display 789 + declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration) 790 + declaration = KernRe(r'\}\s+;').sub('};', declaration) 791 + 792 + # Better handle inlined enums 793 + while True: 794 + r = KernRe(r'(enum\s+\{[^\}]+),([^\n])') 795 + if not r.search(declaration): 796 + break 797 + 798 + declaration = r.sub(r'\1,\n\2', declaration) 799 + 800 + def_args = declaration.split('\n') 801 + level = 1 802 + declaration = "" 803 + for clause in def_args: 804 + 805 + clause = clause.strip() 806 + clause = KernRe(r'\s+').sub(' ', clause, count=1) 807 + 808 + if not clause: 809 + continue 810 + 811 + if '}' in clause and level > 1: 812 + level -= 1 813 + 814 + if not KernRe(r'^\s*#').match(clause): 815 + declaration += "\t" * level 816 + 817 + declaration += "\t" + clause + "\n" 818 + if "{" in clause and "}" not in clause: 819 + level += 1 820 + 821 + self.output_declaration(decl_type, declaration_name, 822 + struct=declaration_name, 823 + definition=declaration, 824 + parameterlist=self.entry.parameterlist, 825 + parameterdescs=self.entry.parameterdescs, 826 + parametertypes=self.entry.parametertypes, 827 + parameterdesc_start_lines=self.entry.parameterdesc_start_lines, 828 + sectionlist=self.entry.sectionlist, 829 + sections=self.entry.sections, 830 + section_start_lines=self.entry.section_start_lines, 831 + purpose=self.entry.declaration_purpose) 832 + 833 + def dump_enum(self, ln, proto): 834 + """ 835 + Stores an enum inside self.entries array. 836 + """ 837 + 838 + # Ignore members marked private 839 + proto = KernRe(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', flags=re.S).sub('', proto) 840 + proto = KernRe(r'\/\*\s*private:.*}', flags=re.S).sub('}', proto) 841 + 842 + # Strip comments 843 + proto = KernRe(r'\/\*.*?\*\/', flags=re.S).sub('', proto) 844 + 845 + # Strip #define macros inside enums 846 + proto = KernRe(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=re.S).sub('', proto) 847 + 848 + members = None 849 + declaration_name = None 850 + 851 + r = KernRe(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;') 852 + if r.search(proto): 853 + declaration_name = r.group(2) 854 + members = r.group(1).rstrip() 855 + else: 856 + r = KernRe(r'enum\s+(\w*)\s*\{(.*)\}') 857 + if r.match(proto): 858 + declaration_name = r.group(1) 859 + members = r.group(2).rstrip() 860 + 861 + if not members: 862 + self.emit_msg(ln, f"{proto}: error: Cannot parse enum!") 863 + return 864 + 865 + if self.entry.identifier != declaration_name: 866 + if self.entry.identifier == "": 867 + self.emit_msg(ln, 868 + f"{proto}: wrong kernel-doc identifier on prototype") 869 + else: 870 + self.emit_msg(ln, 871 + f"expecting prototype for enum {self.entry.identifier}. Prototype was for enum {declaration_name} instead") 872 + return 873 + 874 + if not declaration_name: 875 + declaration_name = "(anonymous)" 876 + 877 + member_set = set() 878 + 879 + members = KernRe(r'\([^;]*?[\)]').sub('', members) 880 + 881 + for arg in members.split(','): 882 + if not arg: 883 + continue 884 + arg = KernRe(r'^\s*(\w+).*').sub(r'\1', arg) 885 + self.entry.parameterlist.append(arg) 886 + if arg not in self.entry.parameterdescs: 887 + self.entry.parameterdescs[arg] = self.undescribed 888 + self.emit_msg(ln, 889 + f"Enum value '{arg}' not described in enum '{declaration_name}'") 890 + member_set.add(arg) 891 + 892 + for k in self.entry.parameterdescs: 893 + if k not in member_set: 894 + self.emit_msg(ln, 895 + f"Excess enum value '%{k}' description in '{declaration_name}'") 896 + 897 + self.output_declaration('enum', declaration_name, 898 + enum=declaration_name, 899 + parameterlist=self.entry.parameterlist, 900 + parameterdescs=self.entry.parameterdescs, 901 + parameterdesc_start_lines=self.entry.parameterdesc_start_lines, 902 + sectionlist=self.entry.sectionlist, 903 + sections=self.entry.sections, 904 + section_start_lines=self.entry.section_start_lines, 905 + purpose=self.entry.declaration_purpose) 906 + 907 + def dump_declaration(self, ln, prototype): 908 + """ 909 + Stores a data declaration inside self.entries array. 910 + """ 911 + 912 + if self.entry.decl_type == "enum": 913 + self.dump_enum(ln, prototype) 914 + return 915 + 916 + if self.entry.decl_type == "typedef": 917 + self.dump_typedef(ln, prototype) 918 + return 919 + 920 + if self.entry.decl_type in ["union", "struct"]: 921 + self.dump_struct(ln, prototype) 922 + return 923 + 924 + self.output_declaration(self.entry.decl_type, prototype, 925 + entry=self.entry) 926 + 927 + def dump_function(self, ln, prototype): 928 + """ 929 + Stores a function of function macro inside self.entries array. 930 + """ 931 + 932 + func_macro = False 933 + return_type = '' 934 + decl_type = 'function' 935 + 936 + # Prefixes that would be removed 937 + sub_prefixes = [ 938 + (r"^static +", "", 0), 939 + (r"^extern +", "", 0), 940 + (r"^asmlinkage +", "", 0), 941 + (r"^inline +", "", 0), 942 + (r"^__inline__ +", "", 0), 943 + (r"^__inline +", "", 0), 944 + (r"^__always_inline +", "", 0), 945 + (r"^noinline +", "", 0), 946 + (r"^__FORTIFY_INLINE +", "", 0), 947 + (r"__init +", "", 0), 948 + (r"__init_or_module +", "", 0), 949 + (r"__deprecated +", "", 0), 950 + (r"__flatten +", "", 0), 951 + (r"__meminit +", "", 0), 952 + (r"__must_check +", "", 0), 953 + (r"__weak +", "", 0), 954 + (r"__sched +", "", 0), 955 + (r"_noprof", "", 0), 956 + (r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +", "", 0), 957 + (r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +", "", 0), 958 + (r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +", "", 0), 959 + (r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)", r"\1, \2", 0), 960 + (r"__attribute_const__ +", "", 0), 961 + 962 + # It seems that Python support for re.X is broken: 963 + # At least for me (Python 3.13), this didn't work 964 + # (r""" 965 + # __attribute__\s*\(\( 966 + # (?: 967 + # [\w\s]+ # attribute name 968 + # (?:\([^)]*\))? # attribute arguments 969 + # \s*,? # optional comma at the end 970 + # )+ 971 + # \)\)\s+ 972 + # """, "", re.X), 973 + 974 + # So, remove whitespaces and comments from it 975 + (r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+", "", 0), 976 + ] 977 + 978 + for search, sub, flags in sub_prefixes: 979 + prototype = KernRe(search, flags).sub(sub, prototype) 980 + 981 + # Macros are a special case, as they change the prototype format 982 + new_proto = KernRe(r"^#\s*define\s+").sub("", prototype) 983 + if new_proto != prototype: 984 + is_define_proto = True 985 + prototype = new_proto 986 + else: 987 + is_define_proto = False 988 + 989 + # Yes, this truly is vile. We are looking for: 990 + # 1. Return type (may be nothing if we're looking at a macro) 991 + # 2. Function name 992 + # 3. Function parameters. 993 + # 994 + # All the while we have to watch out for function pointer parameters 995 + # (which IIRC is what the two sections are for), C types (these 996 + # regexps don't even start to express all the possibilities), and 997 + # so on. 998 + # 999 + # If you mess with these regexps, it's a good idea to check that 1000 + # the following functions' documentation still comes out right: 1001 + # - parport_register_device (function pointer parameters) 1002 + # - atomic_set (macro) 1003 + # - pci_match_device, __copy_to_user (long return type) 1004 + 1005 + name = r'[a-zA-Z0-9_~:]+' 1006 + prototype_end1 = r'[^\(]*' 1007 + prototype_end2 = r'[^\{]*' 1008 + prototype_end = fr'\(({prototype_end1}|{prototype_end2})\)' 1009 + 1010 + # Besides compiling, Perl qr{[\w\s]+} works as a non-capturing group. 1011 + # So, this needs to be mapped in Python with (?:...)? or (?:...)+ 1012 + 1013 + type1 = r'(?:[\w\s]+)?' 1014 + type2 = r'(?:[\w\s]+\*+)+' 1015 + 1016 + found = False 1017 + 1018 + if is_define_proto: 1019 + r = KernRe(r'^()(' + name + r')\s+') 1020 + 1021 + if r.search(prototype): 1022 + return_type = '' 1023 + declaration_name = r.group(2) 1024 + func_macro = True 1025 + 1026 + found = True 1027 + 1028 + if not found: 1029 + patterns = [ 1030 + rf'^()({name})\s*{prototype_end}', 1031 + rf'^({type1})\s+({name})\s*{prototype_end}', 1032 + rf'^({type2})\s*({name})\s*{prototype_end}', 1033 + ] 1034 + 1035 + for p in patterns: 1036 + r = KernRe(p) 1037 + 1038 + if r.match(prototype): 1039 + 1040 + return_type = r.group(1) 1041 + declaration_name = r.group(2) 1042 + args = r.group(3) 1043 + 1044 + self.create_parameter_list(ln, decl_type, args, ',', 1045 + declaration_name) 1046 + 1047 + found = True 1048 + break 1049 + if not found: 1050 + self.emit_msg(ln, 1051 + f"cannot understand function prototype: '{prototype}'") 1052 + return 1053 + 1054 + if self.entry.identifier != declaration_name: 1055 + self.emit_msg(ln, 1056 + f"expecting prototype for {self.entry.identifier}(). Prototype was for {declaration_name}() instead") 1057 + return 1058 + 1059 + prms = " ".join(self.entry.parameterlist) 1060 + self.check_sections(ln, declaration_name, "function", 1061 + self.entry.sectcheck, prms) 1062 + 1063 + self.check_return_section(ln, declaration_name, return_type) 1064 + 1065 + if 'typedef' in return_type: 1066 + self.output_declaration(decl_type, declaration_name, 1067 + function=declaration_name, 1068 + typedef=True, 1069 + functiontype=return_type, 1070 + parameterlist=self.entry.parameterlist, 1071 + parameterdescs=self.entry.parameterdescs, 1072 + parametertypes=self.entry.parametertypes, 1073 + parameterdesc_start_lines=self.entry.parameterdesc_start_lines, 1074 + sectionlist=self.entry.sectionlist, 1075 + sections=self.entry.sections, 1076 + section_start_lines=self.entry.section_start_lines, 1077 + purpose=self.entry.declaration_purpose, 1078 + func_macro=func_macro) 1079 + else: 1080 + self.output_declaration(decl_type, declaration_name, 1081 + function=declaration_name, 1082 + typedef=False, 1083 + functiontype=return_type, 1084 + parameterlist=self.entry.parameterlist, 1085 + parameterdescs=self.entry.parameterdescs, 1086 + parametertypes=self.entry.parametertypes, 1087 + parameterdesc_start_lines=self.entry.parameterdesc_start_lines, 1088 + sectionlist=self.entry.sectionlist, 1089 + sections=self.entry.sections, 1090 + section_start_lines=self.entry.section_start_lines, 1091 + purpose=self.entry.declaration_purpose, 1092 + func_macro=func_macro) 1093 + 1094 + def dump_typedef(self, ln, proto): 1095 + """ 1096 + Stores a typedef inside self.entries array. 1097 + """ 1098 + 1099 + typedef_type = r'((?:\s+[\w\*]+\b){0,7}\s+(?:\w+\b|\*+))\s*' 1100 + typedef_ident = r'\*?\s*(\w\S+)\s*' 1101 + typedef_args = r'\s*\((.*)\);' 1102 + 1103 + typedef1 = KernRe(r'typedef' + typedef_type + r'\(' + typedef_ident + r'\)' + typedef_args) 1104 + typedef2 = KernRe(r'typedef' + typedef_type + typedef_ident + typedef_args) 1105 + 1106 + # Strip comments 1107 + proto = KernRe(r'/\*.*?\*/', flags=re.S).sub('', proto) 1108 + 1109 + # Parse function typedef prototypes 1110 + for r in [typedef1, typedef2]: 1111 + if not r.match(proto): 1112 + continue 1113 + 1114 + return_type = r.group(1).strip() 1115 + declaration_name = r.group(2) 1116 + args = r.group(3) 1117 + 1118 + if self.entry.identifier != declaration_name: 1119 + self.emit_msg(ln, 1120 + f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n") 1121 + return 1122 + 1123 + decl_type = 'function' 1124 + self.create_parameter_list(ln, decl_type, args, ',', declaration_name) 1125 + 1126 + self.output_declaration(decl_type, declaration_name, 1127 + function=declaration_name, 1128 + typedef=True, 1129 + functiontype=return_type, 1130 + parameterlist=self.entry.parameterlist, 1131 + parameterdescs=self.entry.parameterdescs, 1132 + parametertypes=self.entry.parametertypes, 1133 + parameterdesc_start_lines=self.entry.parameterdesc_start_lines, 1134 + sectionlist=self.entry.sectionlist, 1135 + sections=self.entry.sections, 1136 + section_start_lines=self.entry.section_start_lines, 1137 + purpose=self.entry.declaration_purpose) 1138 + return 1139 + 1140 + # Handle nested parentheses or brackets 1141 + r = KernRe(r'(\(*.\)\s*|\[*.\]\s*);$') 1142 + while r.search(proto): 1143 + proto = r.sub('', proto) 1144 + 1145 + # Parse simple typedefs 1146 + r = KernRe(r'typedef.*\s+(\w+)\s*;') 1147 + if r.match(proto): 1148 + declaration_name = r.group(1) 1149 + 1150 + if self.entry.identifier != declaration_name: 1151 + self.emit_msg(ln, 1152 + f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n") 1153 + return 1154 + 1155 + self.output_declaration('typedef', declaration_name, 1156 + typedef=declaration_name, 1157 + sectionlist=self.entry.sectionlist, 1158 + sections=self.entry.sections, 1159 + section_start_lines=self.entry.section_start_lines, 1160 + purpose=self.entry.declaration_purpose) 1161 + return 1162 + 1163 + self.emit_msg(ln, "error: Cannot parse typedef!") 1164 + 1165 + @staticmethod 1166 + def process_export(function_set, line): 1167 + """ 1168 + process EXPORT_SYMBOL* tags 1169 + 1170 + This method doesn't use any variable from the class, so declare it 1171 + with a staticmethod decorator. 1172 + """ 1173 + 1174 + # Note: it accepts only one EXPORT_SYMBOL* per line, as having 1175 + # multiple export lines would violate Kernel coding style. 1176 + 1177 + if export_symbol.search(line): 1178 + symbol = export_symbol.group(2) 1179 + function_set.add(symbol) 1180 + return 1181 + 1182 + if export_symbol_ns.search(line): 1183 + symbol = export_symbol_ns.group(2) 1184 + function_set.add(symbol) 1185 + 1186 + def process_normal(self, ln, line): 1187 + """ 1188 + STATE_NORMAL: looking for the /** to begin everything. 1189 + """ 1190 + 1191 + if not doc_start.match(line): 1192 + return 1193 + 1194 + # start a new entry 1195 + self.reset_state(ln) 1196 + self.entry.in_doc_sect = False 1197 + 1198 + # next line is always the function name 1199 + self.state = state.NAME 1200 + 1201 + def process_name(self, ln, line): 1202 + """ 1203 + STATE_NAME: Looking for the "name - description" line 1204 + """ 1205 + 1206 + if doc_block.search(line): 1207 + self.entry.new_start_line = ln 1208 + 1209 + if not doc_block.group(1): 1210 + self.entry.section = self.section_intro 1211 + else: 1212 + self.entry.section = doc_block.group(1) 1213 + 1214 + self.entry.identifier = self.entry.section 1215 + self.state = state.DOCBLOCK 1216 + return 1217 + 1218 + if doc_decl.search(line): 1219 + self.entry.identifier = doc_decl.group(1) 1220 + self.entry.is_kernel_comment = False 1221 + 1222 + decl_start = str(doc_com) # comment block asterisk 1223 + fn_type = r"(?:\w+\s*\*\s*)?" # type (for non-functions) 1224 + parenthesis = r"(?:\(\w*\))?" # optional parenthesis on function 1225 + decl_end = r"(?:[-:].*)" # end of the name part 1226 + 1227 + # test for pointer declaration type, foo * bar() - desc 1228 + r = KernRe(fr"^{decl_start}([\w\s]+?){parenthesis}?\s*{decl_end}?$") 1229 + if r.search(line): 1230 + self.entry.identifier = r.group(1) 1231 + 1232 + # Test for data declaration 1233 + r = KernRe(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)") 1234 + if r.search(line): 1235 + self.entry.decl_type = r.group(1) 1236 + self.entry.identifier = r.group(2) 1237 + self.entry.is_kernel_comment = True 1238 + else: 1239 + # Look for foo() or static void foo() - description; 1240 + # or misspelt identifier 1241 + 1242 + r1 = KernRe(fr"^{decl_start}{fn_type}(\w+)\s*{parenthesis}\s*{decl_end}?$") 1243 + r2 = KernRe(fr"^{decl_start}{fn_type}(\w+[^-:]*){parenthesis}\s*{decl_end}$") 1244 + 1245 + for r in [r1, r2]: 1246 + if r.search(line): 1247 + self.entry.identifier = r.group(1) 1248 + self.entry.decl_type = "function" 1249 + 1250 + r = KernRe(r"define\s+") 1251 + self.entry.identifier = r.sub("", self.entry.identifier) 1252 + self.entry.is_kernel_comment = True 1253 + break 1254 + 1255 + self.entry.identifier = self.entry.identifier.strip(" ") 1256 + 1257 + self.state = state.BODY 1258 + 1259 + # if there's no @param blocks need to set up default section here 1260 + self.entry.section = SECTION_DEFAULT 1261 + self.entry.new_start_line = ln + 1 1262 + 1263 + r = KernRe("[-:](.*)") 1264 + if r.search(line): 1265 + # strip leading/trailing/multiple spaces 1266 + self.entry.descr = r.group(1).strip(" ") 1267 + 1268 + r = KernRe(r"\s+") 1269 + self.entry.descr = r.sub(" ", self.entry.descr) 1270 + self.entry.declaration_purpose = self.entry.descr 1271 + self.state = state.BODY_MAYBE 1272 + else: 1273 + self.entry.declaration_purpose = "" 1274 + 1275 + if not self.entry.is_kernel_comment: 1276 + self.emit_msg(ln, 1277 + f"This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n{line}") 1278 + self.state = state.NORMAL 1279 + 1280 + if not self.entry.declaration_purpose and self.config.wshort_desc: 1281 + self.emit_msg(ln, 1282 + f"missing initial short description on line:\n{line}") 1283 + 1284 + if not self.entry.identifier and self.entry.decl_type != "enum": 1285 + self.emit_msg(ln, 1286 + f"wrong kernel-doc identifier on line:\n{line}") 1287 + self.state = state.NORMAL 1288 + 1289 + if self.config.verbose: 1290 + self.emit_msg(ln, 1291 + f"Scanning doc for {self.entry.decl_type} {self.entry.identifier}", 1292 + warning=False) 1293 + 1294 + return 1295 + 1296 + # Failed to find an identifier. Emit a warning 1297 + self.emit_msg(ln, f"Cannot find identifier on line:\n{line}") 1298 + 1299 + def process_body(self, ln, line): 1300 + """ 1301 + STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment. 1302 + """ 1303 + 1304 + if self.state == state.BODY_WITH_BLANK_LINE: 1305 + r = KernRe(r"\s*\*\s?\S") 1306 + if r.match(line): 1307 + self.dump_section() 1308 + self.entry.section = SECTION_DEFAULT 1309 + self.entry.new_start_line = ln 1310 + self.entry.contents = "" 1311 + 1312 + if doc_sect.search(line): 1313 + self.entry.in_doc_sect = True 1314 + newsection = doc_sect.group(1) 1315 + 1316 + if newsection.lower() in ["description", "context"]: 1317 + newsection = newsection.title() 1318 + 1319 + # Special case: @return is a section, not a param description 1320 + if newsection.lower() in ["@return", "@returns", 1321 + "return", "returns"]: 1322 + newsection = "Return" 1323 + 1324 + # Perl kernel-doc has a check here for contents before sections. 1325 + # the logic there is always false, as in_doc_sect variable is 1326 + # always true. So, just don't implement Wcontents_before_sections 1327 + 1328 + # .title() 1329 + newcontents = doc_sect.group(2) 1330 + if not newcontents: 1331 + newcontents = "" 1332 + 1333 + if self.entry.contents.strip("\n"): 1334 + self.dump_section() 1335 + 1336 + self.entry.new_start_line = ln 1337 + self.entry.section = newsection 1338 + self.entry.leading_space = None 1339 + 1340 + self.entry.contents = newcontents.lstrip() 1341 + if self.entry.contents: 1342 + self.entry.contents += "\n" 1343 + 1344 + self.state = state.BODY 1345 + return 1346 + 1347 + if doc_end.search(line): 1348 + self.dump_section() 1349 + 1350 + # Look for doc_com + <text> + doc_end: 1351 + r = KernRe(r'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') 1352 + if r.match(line): 1353 + self.emit_msg(ln, f"suspicious ending line: {line}") 1354 + 1355 + self.entry.prototype = "" 1356 + self.entry.new_start_line = ln + 1 1357 + 1358 + self.state = state.PROTO 1359 + return 1360 + 1361 + if doc_content.search(line): 1362 + cont = doc_content.group(1) 1363 + 1364 + if cont == "": 1365 + if self.entry.section == self.section_context: 1366 + self.dump_section() 1367 + 1368 + self.entry.new_start_line = ln 1369 + self.state = state.BODY 1370 + else: 1371 + if self.entry.section != SECTION_DEFAULT: 1372 + self.state = state.BODY_WITH_BLANK_LINE 1373 + else: 1374 + self.state = state.BODY 1375 + 1376 + self.entry.contents += "\n" 1377 + 1378 + elif self.state == state.BODY_MAYBE: 1379 + 1380 + # Continued declaration purpose 1381 + self.entry.declaration_purpose = self.entry.declaration_purpose.rstrip() 1382 + self.entry.declaration_purpose += " " + cont 1383 + 1384 + r = KernRe(r"\s+") 1385 + self.entry.declaration_purpose = r.sub(' ', 1386 + self.entry.declaration_purpose) 1387 + 1388 + else: 1389 + if self.entry.section.startswith('@') or \ 1390 + self.entry.section == self.section_context: 1391 + if self.entry.leading_space is None: 1392 + r = KernRe(r'^(\s+)') 1393 + if r.match(cont): 1394 + self.entry.leading_space = len(r.group(1)) 1395 + else: 1396 + self.entry.leading_space = 0 1397 + 1398 + # Double-check if leading space are realy spaces 1399 + pos = 0 1400 + for i in range(0, self.entry.leading_space): 1401 + if cont[i] != " ": 1402 + break 1403 + pos += 1 1404 + 1405 + cont = cont[pos:] 1406 + 1407 + # NEW LOGIC: 1408 + # In case it is different, update it 1409 + if self.entry.leading_space != pos: 1410 + self.entry.leading_space = pos 1411 + 1412 + self.entry.contents += cont + "\n" 1413 + return 1414 + 1415 + # Unknown line, ignore 1416 + self.emit_msg(ln, f"bad line: {line}") 1417 + 1418 + def process_inline(self, ln, line): 1419 + """STATE_INLINE: docbook comments within a prototype.""" 1420 + 1421 + if self.inline_doc_state == state.INLINE_NAME and \ 1422 + doc_inline_sect.search(line): 1423 + self.entry.section = doc_inline_sect.group(1) 1424 + self.entry.new_start_line = ln 1425 + 1426 + self.entry.contents = doc_inline_sect.group(2).lstrip() 1427 + if self.entry.contents != "": 1428 + self.entry.contents += "\n" 1429 + 1430 + self.inline_doc_state = state.INLINE_TEXT 1431 + # Documentation block end */ 1432 + return 1433 + 1434 + if doc_inline_end.search(line): 1435 + if self.entry.contents not in ["", "\n"]: 1436 + self.dump_section() 1437 + 1438 + self.state = state.PROTO 1439 + self.inline_doc_state = state.INLINE_NA 1440 + return 1441 + 1442 + if doc_content.search(line): 1443 + if self.inline_doc_state == state.INLINE_TEXT: 1444 + self.entry.contents += doc_content.group(1) + "\n" 1445 + if not self.entry.contents.strip(" ").rstrip("\n"): 1446 + self.entry.contents = "" 1447 + 1448 + elif self.inline_doc_state == state.INLINE_NAME: 1449 + self.emit_msg(ln, 1450 + f"Incorrect use of kernel-doc format: {line}") 1451 + 1452 + self.inline_doc_state = state.INLINE_ERROR 1453 + 1454 + def syscall_munge(self, ln, proto): # pylint: disable=W0613 1455 + """ 1456 + Handle syscall definitions 1457 + """ 1458 + 1459 + is_void = False 1460 + 1461 + # Strip newlines/CR's 1462 + proto = re.sub(r'[\r\n]+', ' ', proto) 1463 + 1464 + # Check if it's a SYSCALL_DEFINE0 1465 + if 'SYSCALL_DEFINE0' in proto: 1466 + is_void = True 1467 + 1468 + # Replace SYSCALL_DEFINE with correct return type & function name 1469 + proto = KernRe(r'SYSCALL_DEFINE.*\(').sub('long sys_', proto) 1470 + 1471 + r = KernRe(r'long\s+(sys_.*?),') 1472 + if r.search(proto): 1473 + proto = KernRe(',').sub('(', proto, count=1) 1474 + elif is_void: 1475 + proto = KernRe(r'\)').sub('(void)', proto, count=1) 1476 + 1477 + # Now delete all of the odd-numbered commas in the proto 1478 + # so that argument types & names don't have a comma between them 1479 + count = 0 1480 + length = len(proto) 1481 + 1482 + if is_void: 1483 + length = 0 # skip the loop if is_void 1484 + 1485 + for ix in range(length): 1486 + if proto[ix] == ',': 1487 + count += 1 1488 + if count % 2 == 1: 1489 + proto = proto[:ix] + ' ' + proto[ix + 1:] 1490 + 1491 + return proto 1492 + 1493 + def tracepoint_munge(self, ln, proto): 1494 + """ 1495 + Handle tracepoint definitions 1496 + """ 1497 + 1498 + tracepointname = None 1499 + tracepointargs = None 1500 + 1501 + # Match tracepoint name based on different patterns 1502 + r = KernRe(r'TRACE_EVENT\((.*?),') 1503 + if r.search(proto): 1504 + tracepointname = r.group(1) 1505 + 1506 + r = KernRe(r'DEFINE_SINGLE_EVENT\((.*?),') 1507 + if r.search(proto): 1508 + tracepointname = r.group(1) 1509 + 1510 + r = KernRe(r'DEFINE_EVENT\((.*?),(.*?),') 1511 + if r.search(proto): 1512 + tracepointname = r.group(2) 1513 + 1514 + if tracepointname: 1515 + tracepointname = tracepointname.lstrip() 1516 + 1517 + r = KernRe(r'TP_PROTO\((.*?)\)') 1518 + if r.search(proto): 1519 + tracepointargs = r.group(1) 1520 + 1521 + if not tracepointname or not tracepointargs: 1522 + self.emit_msg(ln, 1523 + f"Unrecognized tracepoint format:\n{proto}\n") 1524 + else: 1525 + proto = f"static inline void trace_{tracepointname}({tracepointargs})" 1526 + self.entry.identifier = f"trace_{self.entry.identifier}" 1527 + 1528 + return proto 1529 + 1530 + def process_proto_function(self, ln, line): 1531 + """Ancillary routine to process a function prototype""" 1532 + 1533 + # strip C99-style comments to end of line 1534 + r = KernRe(r"\/\/.*$", re.S) 1535 + line = r.sub('', line) 1536 + 1537 + if KernRe(r'\s*#\s*define').match(line): 1538 + self.entry.prototype = line 1539 + elif line.startswith('#'): 1540 + # Strip other macros like #ifdef/#ifndef/#endif/... 1541 + pass 1542 + else: 1543 + r = KernRe(r'([^\{]*)') 1544 + if r.match(line): 1545 + self.entry.prototype += r.group(1) + " " 1546 + 1547 + if '{' in line or ';' in line or KernRe(r'\s*#\s*define').match(line): 1548 + # strip comments 1549 + r = KernRe(r'/\*.*?\*/') 1550 + self.entry.prototype = r.sub('', self.entry.prototype) 1551 + 1552 + # strip newlines/cr's 1553 + r = KernRe(r'[\r\n]+') 1554 + self.entry.prototype = r.sub(' ', self.entry.prototype) 1555 + 1556 + # strip leading spaces 1557 + r = KernRe(r'^\s+') 1558 + self.entry.prototype = r.sub('', self.entry.prototype) 1559 + 1560 + # Handle self.entry.prototypes for function pointers like: 1561 + # int (*pcs_config)(struct foo) 1562 + 1563 + r = KernRe(r'^(\S+\s+)\(\s*\*(\S+)\)') 1564 + self.entry.prototype = r.sub(r'\1\2', self.entry.prototype) 1565 + 1566 + if 'SYSCALL_DEFINE' in self.entry.prototype: 1567 + self.entry.prototype = self.syscall_munge(ln, 1568 + self.entry.prototype) 1569 + 1570 + r = KernRe(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT') 1571 + if r.search(self.entry.prototype): 1572 + self.entry.prototype = self.tracepoint_munge(ln, 1573 + self.entry.prototype) 1574 + 1575 + self.dump_function(ln, self.entry.prototype) 1576 + self.reset_state(ln) 1577 + 1578 + def process_proto_type(self, ln, line): 1579 + """Ancillary routine to process a type""" 1580 + 1581 + # Strip newlines/cr's. 1582 + line = KernRe(r'[\r\n]+', re.S).sub(' ', line) 1583 + 1584 + # Strip leading spaces 1585 + line = KernRe(r'^\s+', re.S).sub('', line) 1586 + 1587 + # Strip trailing spaces 1588 + line = KernRe(r'\s+$', re.S).sub('', line) 1589 + 1590 + # Strip C99-style comments to the end of the line 1591 + line = KernRe(r"\/\/.*$", re.S).sub('', line) 1592 + 1593 + # To distinguish preprocessor directive from regular declaration later. 1594 + if line.startswith('#'): 1595 + line += ";" 1596 + 1597 + r = KernRe(r'([^\{\};]*)([\{\};])(.*)') 1598 + while True: 1599 + if r.search(line): 1600 + if self.entry.prototype: 1601 + self.entry.prototype += " " 1602 + self.entry.prototype += r.group(1) + r.group(2) 1603 + 1604 + self.entry.brcount += r.group(2).count('{') 1605 + self.entry.brcount -= r.group(2).count('}') 1606 + 1607 + self.entry.brcount = max(self.entry.brcount, 0) 1608 + 1609 + if r.group(2) == ';' and self.entry.brcount == 0: 1610 + self.dump_declaration(ln, self.entry.prototype) 1611 + self.reset_state(ln) 1612 + break 1613 + 1614 + line = r.group(3) 1615 + else: 1616 + self.entry.prototype += line 1617 + break 1618 + 1619 + def process_proto(self, ln, line): 1620 + """STATE_PROTO: reading a function/whatever prototype.""" 1621 + 1622 + if doc_inline_oneline.search(line): 1623 + self.entry.section = doc_inline_oneline.group(1) 1624 + self.entry.contents = doc_inline_oneline.group(2) 1625 + 1626 + if self.entry.contents != "": 1627 + self.entry.contents += "\n" 1628 + self.dump_section(start_new=False) 1629 + 1630 + elif doc_inline_start.search(line): 1631 + self.state = state.INLINE 1632 + self.inline_doc_state = state.INLINE_NAME 1633 + 1634 + elif self.entry.decl_type == 'function': 1635 + self.process_proto_function(ln, line) 1636 + 1637 + else: 1638 + self.process_proto_type(ln, line) 1639 + 1640 + def process_docblock(self, ln, line): 1641 + """STATE_DOCBLOCK: within a DOC: block.""" 1642 + 1643 + if doc_end.search(line): 1644 + self.dump_section() 1645 + self.output_declaration("doc", self.entry.identifier, 1646 + sectionlist=self.entry.sectionlist, 1647 + sections=self.entry.sections, 1648 + section_start_lines=self.entry.section_start_lines) 1649 + self.reset_state(ln) 1650 + 1651 + elif doc_content.search(line): 1652 + self.entry.contents += doc_content.group(1) + "\n" 1653 + 1654 + def parse_export(self): 1655 + """ 1656 + Parses EXPORT_SYMBOL* macros from a single Kernel source file. 1657 + """ 1658 + 1659 + export_table = set() 1660 + 1661 + try: 1662 + with open(self.fname, "r", encoding="utf8", 1663 + errors="backslashreplace") as fp: 1664 + 1665 + for line in fp: 1666 + self.process_export(export_table, line) 1667 + 1668 + except IOError: 1669 + return None 1670 + 1671 + return export_table 1672 + 1673 + def parse_kdoc(self): 1674 + """ 1675 + Open and process each line of a C source file. 1676 + The parsing is controlled via a state machine, and the line is passed 1677 + to a different process function depending on the state. The process 1678 + function may update the state as needed. 1679 + 1680 + Besides parsing kernel-doc tags, it also parses export symbols. 1681 + """ 1682 + 1683 + cont = False 1684 + prev = "" 1685 + prev_ln = None 1686 + export_table = set() 1687 + 1688 + try: 1689 + with open(self.fname, "r", encoding="utf8", 1690 + errors="backslashreplace") as fp: 1691 + for ln, line in enumerate(fp): 1692 + 1693 + line = line.expandtabs().strip("\n") 1694 + 1695 + # Group continuation lines on prototypes 1696 + if self.state == state.PROTO: 1697 + if line.endswith("\\"): 1698 + prev += line.rstrip("\\") 1699 + cont = True 1700 + 1701 + if not prev_ln: 1702 + prev_ln = ln 1703 + 1704 + continue 1705 + 1706 + if cont: 1707 + ln = prev_ln 1708 + line = prev + line 1709 + prev = "" 1710 + cont = False 1711 + prev_ln = None 1712 + 1713 + self.config.log.debug("%d %s%s: %s", 1714 + ln, state.name[self.state], 1715 + state.inline_name[self.inline_doc_state], 1716 + line) 1717 + 1718 + # This is an optimization over the original script. 1719 + # There, when export_file was used for the same file, 1720 + # it was read twice. Here, we use the already-existing 1721 + # loop to parse exported symbols as well. 1722 + # 1723 + # TODO: It should be noticed that not all states are 1724 + # needed here. On a future cleanup, process export only 1725 + # at the states that aren't handling comment markups. 1726 + self.process_export(export_table, line) 1727 + 1728 + # Hand this line to the appropriate state handler 1729 + if self.state == state.NORMAL: 1730 + self.process_normal(ln, line) 1731 + elif self.state == state.NAME: 1732 + self.process_name(ln, line) 1733 + elif self.state in [state.BODY, state.BODY_MAYBE, 1734 + state.BODY_WITH_BLANK_LINE]: 1735 + self.process_body(ln, line) 1736 + elif self.state == state.INLINE: # scanning for inline parameters 1737 + self.process_inline(ln, line) 1738 + elif self.state == state.PROTO: 1739 + self.process_proto(ln, line) 1740 + elif self.state == state.DOCBLOCK: 1741 + self.process_docblock(ln, line) 1742 + except OSError: 1743 + self.config.log.error(f"Error: Cannot open file {self.fname}") 1744 + 1745 + return export_table, self.entries
+273
scripts/lib/kdoc/kdoc_re.py
··· 1 + #!/usr/bin/env python3 2 + # SPDX-License-Identifier: GPL-2.0 3 + # Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>. 4 + 5 + """ 6 + Regular expression ancillary classes. 7 + 8 + Those help caching regular expressions and do matching for kernel-doc. 9 + """ 10 + 11 + import re 12 + 13 + # Local cache for regular expressions 14 + re_cache = {} 15 + 16 + 17 + class KernRe: 18 + """ 19 + Helper class to simplify regex declaration and usage, 20 + 21 + It calls re.compile for a given pattern. It also allows adding 22 + regular expressions and define sub at class init time. 23 + 24 + Regular expressions can be cached via an argument, helping to speedup 25 + searches. 26 + """ 27 + 28 + def _add_regex(self, string, flags): 29 + """ 30 + Adds a new regex or re-use it from the cache. 31 + """ 32 + 33 + if string in re_cache: 34 + self.regex = re_cache[string] 35 + else: 36 + self.regex = re.compile(string, flags=flags) 37 + 38 + if self.cache: 39 + re_cache[string] = self.regex 40 + 41 + def __init__(self, string, cache=True, flags=0): 42 + """ 43 + Compile a regular expression and initialize internal vars. 44 + """ 45 + 46 + self.cache = cache 47 + self.last_match = None 48 + 49 + self._add_regex(string, flags) 50 + 51 + def __str__(self): 52 + """ 53 + Return the regular expression pattern. 54 + """ 55 + return self.regex.pattern 56 + 57 + def __add__(self, other): 58 + """ 59 + Allows adding two regular expressions into one. 60 + """ 61 + 62 + return KernRe(str(self) + str(other), cache=self.cache or other.cache, 63 + flags=self.regex.flags | other.regex.flags) 64 + 65 + def match(self, string): 66 + """ 67 + Handles a re.match storing its results 68 + """ 69 + 70 + self.last_match = self.regex.match(string) 71 + return self.last_match 72 + 73 + def search(self, string): 74 + """ 75 + Handles a re.search storing its results 76 + """ 77 + 78 + self.last_match = self.regex.search(string) 79 + return self.last_match 80 + 81 + def findall(self, string): 82 + """ 83 + Alias to re.findall 84 + """ 85 + 86 + return self.regex.findall(string) 87 + 88 + def split(self, string): 89 + """ 90 + Alias to re.split 91 + """ 92 + 93 + return self.regex.split(string) 94 + 95 + def sub(self, sub, string, count=0): 96 + """ 97 + Alias to re.sub 98 + """ 99 + 100 + return self.regex.sub(sub, string, count=count) 101 + 102 + def group(self, num): 103 + """ 104 + Returns the group results of the last match 105 + """ 106 + 107 + return self.last_match.group(num) 108 + 109 + 110 + class NestedMatch: 111 + """ 112 + Finding nested delimiters is hard with regular expressions. It is 113 + even harder on Python with its normal re module, as there are several 114 + advanced regular expressions that are missing. 115 + 116 + This is the case of this pattern: 117 + 118 + '\\bSTRUCT_GROUP(\\(((?:(?>[^)(]+)|(?1))*)\\))[^;]*;' 119 + 120 + which is used to properly match open/close parenthesis of the 121 + string search STRUCT_GROUP(), 122 + 123 + Add a class that counts pairs of delimiters, using it to match and 124 + replace nested expressions. 125 + 126 + The original approach was suggested by: 127 + https://stackoverflow.com/questions/5454322/python-how-to-match-nested-parentheses-with-regex 128 + 129 + Although I re-implemented it to make it more generic and match 3 types 130 + of delimiters. The logic checks if delimiters are paired. If not, it 131 + will ignore the search string. 132 + """ 133 + 134 + # TODO: make NestedMatch handle multiple match groups 135 + # 136 + # Right now, regular expressions to match it are defined only up to 137 + # the start delimiter, e.g.: 138 + # 139 + # \bSTRUCT_GROUP\( 140 + # 141 + # is similar to: STRUCT_GROUP\((.*)\) 142 + # except that the content inside the match group is delimiter's aligned. 143 + # 144 + # The content inside parenthesis are converted into a single replace 145 + # group (e.g. r`\1'). 146 + # 147 + # It would be nice to change such definition to support multiple 148 + # match groups, allowing a regex equivalent to. 149 + # 150 + # FOO\((.*), (.*), (.*)\) 151 + # 152 + # it is probably easier to define it not as a regular expression, but 153 + # with some lexical definition like: 154 + # 155 + # FOO(arg1, arg2, arg3) 156 + 157 + DELIMITER_PAIRS = { 158 + '{': '}', 159 + '(': ')', 160 + '[': ']', 161 + } 162 + 163 + RE_DELIM = re.compile(r'[\{\}\[\]\(\)]') 164 + 165 + def _search(self, regex, line): 166 + """ 167 + Finds paired blocks for a regex that ends with a delimiter. 168 + 169 + The suggestion of using finditer to match pairs came from: 170 + https://stackoverflow.com/questions/5454322/python-how-to-match-nested-parentheses-with-regex 171 + but I ended using a different implementation to align all three types 172 + of delimiters and seek for an initial regular expression. 173 + 174 + The algorithm seeks for open/close paired delimiters and place them 175 + into a stack, yielding a start/stop position of each match when the 176 + stack is zeroed. 177 + 178 + The algorithm shoud work fine for properly paired lines, but will 179 + silently ignore end delimiters that preceeds an start delimiter. 180 + This should be OK for kernel-doc parser, as unaligned delimiters 181 + would cause compilation errors. So, we don't need to rise exceptions 182 + to cover such issues. 183 + """ 184 + 185 + stack = [] 186 + 187 + for match_re in regex.finditer(line): 188 + start = match_re.start() 189 + offset = match_re.end() 190 + 191 + d = line[offset - 1] 192 + if d not in self.DELIMITER_PAIRS: 193 + continue 194 + 195 + end = self.DELIMITER_PAIRS[d] 196 + stack.append(end) 197 + 198 + for match in self.RE_DELIM.finditer(line[offset:]): 199 + pos = match.start() + offset 200 + 201 + d = line[pos] 202 + 203 + if d in self.DELIMITER_PAIRS: 204 + end = self.DELIMITER_PAIRS[d] 205 + 206 + stack.append(end) 207 + continue 208 + 209 + # Does the end delimiter match what it is expected? 210 + if stack and d == stack[-1]: 211 + stack.pop() 212 + 213 + if not stack: 214 + yield start, offset, pos + 1 215 + break 216 + 217 + def search(self, regex, line): 218 + """ 219 + This is similar to re.search: 220 + 221 + It matches a regex that it is followed by a delimiter, 222 + returning occurrences only if all delimiters are paired. 223 + """ 224 + 225 + for t in self._search(regex, line): 226 + 227 + yield line[t[0]:t[2]] 228 + 229 + def sub(self, regex, sub, line, count=0): 230 + """ 231 + This is similar to re.sub: 232 + 233 + It matches a regex that it is followed by a delimiter, 234 + replacing occurrences only if all delimiters are paired. 235 + 236 + if r'\1' is used, it works just like re: it places there the 237 + matched paired data with the delimiter stripped. 238 + 239 + If count is different than zero, it will replace at most count 240 + items. 241 + """ 242 + out = "" 243 + 244 + cur_pos = 0 245 + n = 0 246 + 247 + for start, end, pos in self._search(regex, line): 248 + out += line[cur_pos:start] 249 + 250 + # Value, ignoring start/end delimiters 251 + value = line[end:pos - 1] 252 + 253 + # replaces \1 at the sub string, if \1 is used there 254 + new_sub = sub 255 + new_sub = new_sub.replace(r'\1', value) 256 + 257 + out += new_sub 258 + 259 + # Drop end ';' if any 260 + if line[pos] == ';': 261 + pos += 1 262 + 263 + cur_pos = pos 264 + n += 1 265 + 266 + if count and count >= n: 267 + break 268 + 269 + # Append the remaining string 270 + l = len(line) 271 + out += line[cur_pos:l] 272 + 273 + return out
+7
tools/tracing/rtla/README.txt
··· 13 13 - libtraceevent 14 14 - libcpupower (optional, for --deepest-idle-state) 15 15 16 + For BPF sample collection support, the following extra dependencies are 17 + required: 18 + 19 + - libbpf 1.0.0 or later 20 + - bpftool with skeleton support 21 + - clang with BPF CO-RE support 22 + 16 23 It also depends on python3-docutils to compile man pages. 17 24 18 25 For development, we suggest the following steps for compiling rtla: