Pull documentation updates from Jonathan Corbet: "It has been a relatively busy cycle in docsland, with changes all over: - Bring the kernel memory-model docs into the Sphinx build in the "literal include" mode. - Lots of build-infrastructure work, further cleaning up long-term kernel-doc technical debt. The sphinx-pre-install tool has been converted to Python and updated for current systems. - A new tool to detect when documents have been moved and generate HTML redirects; this can be used on kernel.org (or any other site hosting the rendered docs) to avoid breaking links. - Automated processing of the YAML files describing the netlink protocol. - A significant update of the maintainer's PGP guide. ... and a seemingly endless series of typo fixes, build-problem fixes, etc" * tag 'docs-6.18' of git://git.lwn.net/linux: (193 commits) Documentation/features: Update feature lists for 6.17-rc7 docs: remove cdomain.py Documentation/process: submitting-patches: fix typo in "were do" docs: dev-tools/lkmm: Fix typo of missing file extension Documentation: trace: histogram: Convert ftrace docs cross-reference Documentation: trace: histogram-design: Wrap introductory note in note:: directive Documentation: trace: historgram-design: Separate sched_waking histogram section heading and the following diagram Documentation: trace: histogram-design: Trim trailing vertices in diagram explanation text Documentation: trace: histogram: Fix histogram trigger subsection number order docs: driver-api: fix spelling of "buses". Documentation: fbcon: Use admonition directives Documentation: fbcon: Reindent 8th step of attach/detach/unload Documentation: fbcon: Add boot options and attach/detach/unload section headings docs: filesystems: sysfs: add remaining top level sysfs directory descriptions docs: filesystems: sysfs: clarify symlink destinations in dev and bus/devices descriptions docs: filesystems: sysfs: remove top level sysfs net directory docs: maintainer: Fix ambiguous subheading formatting docs: kdoc: a few more dump_typedef() tweaks docs: kdoc: remove redundant comment stripping in dump_typedef() docs: kdoc: remove some dead code in dump_typedef() ...
Unified · Split
| @@ -1,2 +1,2 @@ | ||
| 1 | 1 | [MASTER] |
| 2 | −init-hook='import sys; sys.path += ["scripts/lib/kdoc", "scripts/lib/abi"]' | |
| 2 | +init-hook='import sys; sys.path += ["scripts/lib/kdoc", "scripts/lib/abi", "tools/docs/lib"]' | |
| @@ -0,0 +1,1191 @@ | ||
| 1 | +80211/cfg80211 driver-api/80211/cfg80211 | |
| 2 | +80211/index driver-api/80211/index | |
| 3 | +80211/introduction driver-api/80211/introduction | |
| 4 | +80211/mac80211 driver-api/80211/mac80211 | |
| 5 | +80211/mac80211-advanced driver-api/80211/mac80211-advanced | |
| 6 | +EDID/howto admin-guide/edid | |
| 7 | +PCI/picebus-howto PCI/pciebus-howto | |
| 8 | +RAS/address-translation admin-guide/RAS/address-translation | |
| 9 | +RAS/error-decoding admin-guide/RAS/error-decoding | |
| 10 | +RAS/ras admin-guide/RAS/error-decoding | |
| 11 | +accelerators/ocxl userspace-api/accelerators/ocxl | |
| 12 | +admin-guide/gpio/sysfs userspace-api/gpio/sysfs | |
| 13 | +admin-guide/l1tf admin-guide/hw-vuln/l1tf | |
| 14 | +admin-guide/media/v4l-with-ir admin-guide/media/remote-controller | |
| 15 | +admin-guide/ras admin-guide/RAS/main | |
| 16 | +admin-guide/security-bugs process/security-bugs | |
| 17 | +aoe/aoe admin-guide/aoe/aoe | |
| 18 | +aoe/examples admin-guide/aoe/examples | |
| 19 | +aoe/index admin-guide/aoe/index | |
| 20 | +aoe/todo admin-guide/aoe/todo | |
| 21 | +arc/arc arch/arc/arc | |
| 22 | +arc/features arch/arc/features | |
| 23 | +arc/index arch/arc/index | |
| 24 | +arch/x86/resctrl filesystems/resctrl | |
| 25 | +arm/arm arch/arm/arm | |
| 26 | +arm/booting arch/arm/booting | |
| 27 | +arm/cluster-pm-race-avoidance arch/arm/cluster-pm-race-avoidance | |
| 28 | +arm/features arch/arm/features | |
| 29 | +arm/firmware arch/arm/firmware | |
| 30 | +arm/google/chromebook-boot-flow arch/arm/google/chromebook-boot-flow | |
| 31 | +arm/index arch/arm/index | |
| 32 | +arm/interrupts arch/arm/interrupts | |
| 33 | +arm/ixp4xx arch/arm/ixp4xx | |
| 34 | +arm/kernel_mode_neon arch/arm/kernel_mode_neon | |
| 35 | +arm/kernel_user_helpers arch/arm/kernel_user_helpers | |
| 36 | +arm/keystone/knav-qmss arch/arm/keystone/knav-qmss | |
| 37 | +arm/keystone/overview arch/arm/keystone/overview | |
| 38 | +arm/marvel arch/arm/marvell | |
| 39 | +arm/marvell arch/arm/marvell | |
| 40 | +arm/mem_alignment arch/arm/mem_alignment | |
| 41 | +arm/memory arch/arm/memory | |
| 42 | +arm/microchip arch/arm/microchip | |
| 43 | +arm/netwinder arch/arm/netwinder | |
| 44 | +arm/nwfpe/index arch/arm/nwfpe/index | |
| 45 | +arm/nwfpe/netwinder-fpe arch/arm/nwfpe/netwinder-fpe | |
| 46 | +arm/nwfpe/notes arch/arm/nwfpe/notes | |
| 47 | +arm/nwfpe/nwfpe arch/arm/nwfpe/nwfpe | |
| 48 | +arm/nwfpe/todo arch/arm/nwfpe/todo | |
| 49 | +arm/omap/dss arch/arm/omap/dss | |
| 50 | +arm/omap/index arch/arm/omap/index | |
| 51 | +arm/omap/omap arch/arm/omap/omap | |
| 52 | +arm/omap/omap_pm arch/arm/omap/omap_pm | |
| 53 | +arm/porting arch/arm/porting | |
| 54 | +arm/pxa/mfp arch/arm/pxa/mfp | |
| 55 | +arm/sa1100/assabet arch/arm/sa1100/assabet | |
| 56 | +arm/sa1100/cerf arch/arm/sa1100/cerf | |
| 57 | +arm/sa1100/index arch/arm/sa1100/index | |
| 58 | +arm/sa1100/lart arch/arm/sa1100/lart | |
| 59 | +arm/sa1100/serial_uart arch/arm/sa1100/serial_uart | |
| 60 | +arm/samsung/bootloader-interface arch/arm/samsung/bootloader-interface | |
| 61 | +arm/samsung/gpio arch/arm/samsung/gpio | |
| 62 | +arm/samsung/index arch/arm/samsung/index | |
| 63 | +arm/samsung/overview arch/arm/samsung/overview | |
| 64 | +arm/setup arch/arm/setup | |
| 65 | +arm/spear/overview arch/arm/spear/overview | |
| 66 | +arm/sti/overview arch/arm/sti/overview | |
| 67 | +arm/sti/stih407-overview arch/arm/sti/stih407-overview | |
| 68 | +arm/sti/stih418-overview arch/arm/sti/stih418-overview | |
| 69 | +arm/stm32/overview arch/arm/stm32/overview | |
| 70 | +arm/stm32/stm32-dma-mdma-chaining arch/arm/stm32/stm32-dma-mdma-chaining | |
| 71 | +arm/stm32/stm32f429-overview arch/arm/stm32/stm32f429-overview | |
| 72 | +arm/stm32/stm32f746-overview arch/arm/stm32/stm32f746-overview | |
| 73 | +arm/stm32/stm32f769-overview arch/arm/stm32/stm32f769-overview | |
| 74 | +arm/stm32/stm32h743-overview arch/arm/stm32/stm32h743-overview | |
| 75 | +arm/stm32/stm32h750-overview arch/arm/stm32/stm32h750-overview | |
| 76 | +arm/stm32/stm32mp13-overview arch/arm/stm32/stm32mp13-overview | |
| 77 | +arm/stm32/stm32mp151-overview arch/arm/stm32/stm32mp151-overview | |
| 78 | +arm/stm32/stm32mp157-overview arch/arm/stm32/stm32mp157-overview | |
| 79 | +arm/sunxi arch/arm/sunxi | |
| 80 | +arm/sunxi/clocks arch/arm/sunxi/clocks | |
| 81 | +arm/swp_emulation arch/arm/swp_emulation | |
| 82 | +arm/tcm arch/arm/tcm | |
| 83 | +arm/uefi arch/arm/uefi | |
| 84 | +arm/vfp/release-notes arch/arm/vfp/release-notes | |
| 85 | +arm/vlocks arch/arm/vlocks | |
| 86 | +arm64/acpi_object_usage arch/arm64/acpi_object_usage | |
| 87 | +arm64/amu arch/arm64/amu | |
| 88 | +arm64/arm-acpi arch/arm64/arm-acpi | |
| 89 | +arm64/asymmetric-32bit arch/arm64/asymmetric-32bit | |
| 90 | +arm64/booting arch/arm64/booting | |
| 91 | +arm64/cpu-feature-registers arch/arm64/cpu-feature-registers | |
| 92 | +arm64/elf_hwcaps arch/arm64/elf_hwcaps | |
| 93 | +arm64/features arch/arm64/features | |
| 94 | +arm64/hugetlbpage arch/arm64/hugetlbpage | |
| 95 | +arm64/index arch/arm64/index | |
| 96 | +arm64/legacy_instructions arch/arm64/legacy_instructions | |
| 97 | +arm64/memory arch/arm64/memory | |
| 98 | +arm64/memory-tagging-extension arch/arm64/memory-tagging-extension | |
| 99 | +arm64/perf arch/arm64/perf | |
| 100 | +arm64/pointer-authentication arch/arm64/pointer-authentication | |
| 101 | +arm64/silicon-errata arch/arm64/silicon-errata | |
| 102 | +arm64/sme arch/arm64/sme | |
| 103 | +arm64/sve arch/arm64/sve | |
| 104 | +arm64/tagged-address-abi arch/arm64/tagged-address-abi | |
| 105 | +arm64/tagged-pointers arch/arm64/tagged-pointers | |
| 106 | +asm-annotations core-api/asm-annotations | |
| 107 | +auxdisplay/lcd-panel-cgram admin-guide/lcd-panel-cgram | |
| 108 | +backlight/lp855x-driver driver-api/backlight/lp855x-driver | |
| 109 | +blockdev/drbd/data-structure-v9 admin-guide/blockdev/drbd/data-structure-v9 | |
| 110 | +blockdev/drbd/figures admin-guide/blockdev/drbd/figures | |
| 111 | +blockdev/drbd/index admin-guide/blockdev/drbd/index | |
| 112 | +blockdev/floppy admin-guide/blockdev/floppy | |
| 113 | +blockdev/index admin-guide/blockdev/index | |
| 114 | +blockdev/nbd admin-guide/blockdev/nbd | |
| 115 | +blockdev/paride admin-guide/blockdev/paride | |
| 116 | +blockdev/ramdisk admin-guide/blockdev/ramdisk | |
| 117 | +blockdev/zram admin-guide/blockdev/zram | |
| 118 | +bpf/README bpf/index | |
| 119 | +bpf/bpf_lsm bpf/prog_lsm | |
| 120 | +bpf/instruction-set bpf/standardization/instruction-set | |
| 121 | +bpf/libbpf/libbpf bpf/libbpf/index | |
| 122 | +bpf/standardization/linux-notes bpf/linux-notes | |
| 123 | +bus-devices/ti-gpmc driver-api/memory-devices/ti-gpmc | |
| 124 | +cgroup-v1/blkio-controller admin-guide/cgroup-v1/blkio-controller | |
| 125 | +cgroup-v1/cgroups admin-guide/cgroup-v1/cgroups | |
| 126 | +cgroup-v1/cpuacct admin-guide/cgroup-v1/cpuacct | |
| 127 | +cgroup-v1/cpusets admin-guide/cgroup-v1/cpusets | |
| 128 | +cgroup-v1/devices admin-guide/cgroup-v1/devices | |
| 129 | +cgroup-v1/freezer-subsystem admin-guide/cgroup-v1/freezer-subsystem | |
| 130 | +cgroup-v1/hugetlb admin-guide/cgroup-v1/hugetlb | |
| 131 | +cgroup-v1/index admin-guide/cgroup-v1/index | |
| 132 | +cgroup-v1/memcg_test admin-guide/cgroup-v1/memcg_test | |
| 133 | +cgroup-v1/memory admin-guide/cgroup-v1/memory | |
| 134 | +cgroup-v1/net_cls admin-guide/cgroup-v1/net_cls | |
| 135 | +cgroup-v1/net_prio admin-guide/cgroup-v1/net_prio | |
| 136 | +cgroup-v1/pids admin-guide/cgroup-v1/pids | |
| 137 | +cgroup-v1/rdma admin-guide/cgroup-v1/rdma | |
| 138 | +cma/debugfs admin-guide/mm/cma_debugfs | |
| 139 | +connector/connector driver-api/connector | |
| 140 | +console/console driver-api/console | |
| 141 | +core-api/gcc-plugins kbuild/gcc-plugins | |
| 142 | +core-api/ioctl driver-api/ioctl | |
| 143 | +core-api/memory-hotplug-notifier core-api/memory-hotplug | |
| 144 | +dev-tools/gdb-kernel-debugging process/debugging/gdb-kernel-debugging | |
| 145 | +dev-tools/kgdb process/debugging/kgdb | |
| 146 | +dev-tools/tools dev-tools/index | |
| 147 | +development-process/1.Intro process/1.Intro | |
| 148 | +development-process/2.Process process/2.Process | |
| 149 | +development-process/3.Early-stage process/3.Early-stage | |
| 150 | +development-process/4.Coding process/4.Coding | |
| 151 | +development-process/5.Posting process/5.Posting | |
| 152 | +development-process/6.Followthrough process/6.Followthrough | |
| 153 | +development-process/7.AdvancedTopics process/7.AdvancedTopics | |
| 154 | +development-process/8.Conclusion process/8.Conclusion | |
| 155 | +development-process/development-process process/development-process | |
| 156 | +development-process/index process/index | |
| 157 | +device-mapper/cache admin-guide/device-mapper/cache | |
| 158 | +device-mapper/cache-policies admin-guide/device-mapper/cache-policies | |
| 159 | +device-mapper/delay admin-guide/device-mapper/delay | |
| 160 | +device-mapper/dm-crypt admin-guide/device-mapper/dm-crypt | |
| 161 | +device-mapper/dm-flakey admin-guide/device-mapper/dm-flakey | |
| 162 | +device-mapper/dm-init admin-guide/device-mapper/dm-init | |
| 163 | +device-mapper/dm-integrity admin-guide/device-mapper/dm-integrity | |
| 164 | +device-mapper/dm-io admin-guide/device-mapper/dm-io | |
| 165 | +device-mapper/dm-log admin-guide/device-mapper/dm-log | |
| 166 | +device-mapper/dm-queue-length admin-guide/device-mapper/dm-queue-length | |
| 167 | +device-mapper/dm-raid admin-guide/device-mapper/dm-raid | |
| 168 | +device-mapper/dm-service-time admin-guide/device-mapper/dm-service-time | |
| 169 | +device-mapper/dm-uevent admin-guide/device-mapper/dm-uevent | |
| 170 | +device-mapper/dm-zoned admin-guide/device-mapper/dm-zoned | |
| 171 | +device-mapper/era admin-guide/device-mapper/era | |
| 172 | +device-mapper/index admin-guide/device-mapper/index | |
| 173 | +device-mapper/kcopyd admin-guide/device-mapper/kcopyd | |
| 174 | +device-mapper/linear admin-guide/device-mapper/linear | |
| 175 | +device-mapper/log-writes admin-guide/device-mapper/log-writes | |
| 176 | +device-mapper/persistent-data admin-guide/device-mapper/persistent-data | |
| 177 | +device-mapper/snapshot admin-guide/device-mapper/snapshot | |
| 178 | +device-mapper/statistics admin-guide/device-mapper/statistics | |
| 179 | +device-mapper/striped admin-guide/device-mapper/striped | |
| 180 | +device-mapper/switch admin-guide/device-mapper/switch | |
| 181 | +device-mapper/thin-provisioning admin-guide/device-mapper/thin-provisioning | |
| 182 | +device-mapper/unstriped admin-guide/device-mapper/unstriped | |
| 183 | +device-mapper/verity admin-guide/device-mapper/verity | |
| 184 | +device-mapper/writecache admin-guide/device-mapper/writecache | |
| 185 | +device-mapper/zero admin-guide/device-mapper/zero | |
| 186 | +devicetree/writing-schema devicetree/bindings/writing-schema | |
| 187 | +driver-api/bt8xxgpio driver-api/gpio/bt8xxgpio | |
| 188 | +driver-api/cxl/access-coordinates driver-api/cxl/linux/access-coordinates | |
| 189 | +driver-api/cxl/memory-devices driver-api/cxl/theory-of-operation | |
| 190 | +driver-api/dcdbas userspace-api/dcdbas | |
| 191 | +driver-api/dell_rbu admin-guide/dell_rbu | |
| 192 | +driver-api/edid admin-guide/edid | |
| 193 | +driver-api/gpio driver-api/gpio/index | |
| 194 | +driver-api/hte/tegra194-hte driver-api/hte/tegra-hte | |
| 195 | +driver-api/isapnp userspace-api/isapnp | |
| 196 | +driver-api/media/drivers/v4l-drivers/zoran driver-api/media/drivers/zoran | |
| 197 | +driver-api/mtd/intel-spi driver-api/mtd/spi-intel | |
| 198 | +driver-api/pci driver-api/pci/pci | |
| 199 | +driver-api/pinctl driver-api/pin-control | |
| 200 | +driver-api/rapidio admin-guide/rapidio | |
| 201 | +driver-api/serial/moxa-smartio driver-api/tty/moxa-smartio | |
| 202 | +driver-api/serial/n_gsm driver-api/tty/n_gsm | |
| 203 | +driver-api/serial/tty driver-api/tty/tty_ldisc | |
| 204 | +driver-api/thermal/intel_powerclamp admin-guide/thermal/intel_powerclamp | |
| 205 | +driver-api/usb driver-api/usb/usb | |
| 206 | +driver-model/binding driver-api/driver-model/binding | |
| 207 | +driver-model/bus driver-api/driver-model/bus | |
| 208 | +driver-model/design-patterns driver-api/driver-model/design-patterns | |
| 209 | +driver-model/device driver-api/driver-model/device | |
| 210 | +driver-model/devres driver-api/driver-model/devres | |
| 211 | +driver-model/driver driver-api/driver-model/driver | |
| 212 | +driver-model/index driver-api/driver-model/index | |
| 213 | +driver-model/overview driver-api/driver-model/overview | |
| 214 | +driver-model/platform driver-api/driver-model/platform | |
| 215 | +driver-model/porting driver-api/driver-model/porting | |
| 216 | +early-userspace/buffer-format driver-api/early-userspace/buffer-format | |
| 217 | +early-userspace/early_userspace_support driver-api/early-userspace/early_userspace_support | |
| 218 | +early-userspace/index driver-api/early-userspace/index | |
| 219 | +errseq core-api/errseq | |
| 220 | +filesystems/binderfs admin-guide/binderfs | |
| 221 | +filesystems/cifs/cifsd filesystems/smb/ksmbd | |
| 222 | +filesystems/cifs/cifsroot filesystems/smb/cifsroot | |
| 223 | +filesystems/cifs/index filesystems/smb/index | |
| 224 | +filesystems/cifs/ksmbd filesystems/smb/ksmbd | |
| 225 | +filesystems/ext4/ext4 admin-guide/ext4 | |
| 226 | +filesystems/ext4/ondisk/about filesystems/ext4/about | |
| 227 | +filesystems/ext4/ondisk/allocators filesystems/ext4/allocators | |
| 228 | +filesystems/ext4/ondisk/attributes filesystems/ext4/attributes | |
| 229 | +filesystems/ext4/ondisk/bigalloc filesystems/ext4/bigalloc | |
| 230 | +filesystems/ext4/ondisk/bitmaps filesystems/ext4/bitmaps | |
| 231 | +filesystems/ext4/ondisk/blockgroup filesystems/ext4/blockgroup | |
| 232 | +filesystems/ext4/ondisk/blockmap filesystems/ext4/blockmap | |
| 233 | +filesystems/ext4/ondisk/blocks filesystems/ext4/blocks | |
| 234 | +filesystems/ext4/ondisk/checksums filesystems/ext4/checksums | |
| 235 | +filesystems/ext4/ondisk/directory filesystems/ext4/directory | |
| 236 | +filesystems/ext4/ondisk/dynamic filesystems/ext4/dynamic | |
| 237 | +filesystems/ext4/ondisk/eainode filesystems/ext4/eainode | |
| 238 | +filesystems/ext4/ondisk/globals filesystems/ext4/globals | |
| 239 | +filesystems/ext4/ondisk/group_descr filesystems/ext4/group_descr | |
| 240 | +filesystems/ext4/ondisk/ifork filesystems/ext4/ifork | |
| 241 | +filesystems/ext4/ondisk/inlinedata filesystems/ext4/inlinedata | |
| 242 | +filesystems/ext4/ondisk/inodes filesystems/ext4/inodes | |
| 243 | +filesystems/ext4/ondisk/journal filesystems/ext4/journal | |
| 244 | +filesystems/ext4/ondisk/mmp filesystems/ext4/mmp | |
| 245 | +filesystems/ext4/ondisk/overview filesystems/ext4/overview | |
| 246 | +filesystems/ext4/ondisk/special_inodes filesystems/ext4/special_inodes | |
| 247 | +filesystems/ext4/ondisk/super filesystems/ext4/super | |
| 248 | +filesystems/sysfs-pci PCI/sysfs-pci | |
| 249 | +filesystems/sysfs-tagging networking/sysfs-tagging | |
| 250 | +filesystems/xfs-delayed-logging-design filesystems/xfs/xfs-delayed-logging-design | |
| 251 | +filesystems/xfs-maintainer-entry-profile filesystems/xfs/xfs-maintainer-entry-profile | |
| 252 | +filesystems/xfs-online-fsck-design filesystems/xfs/xfs-online-fsck-design | |
| 253 | +filesystems/xfs-self-describing-metadata filesystems/xfs/xfs-self-describing-metadata | |
| 254 | +gpio/index admin-guide/gpio/index | |
| 255 | +gpio/sysfs userspace-api/gpio/sysfs | |
| 256 | +gpu/amdgpu gpu/amdgpu/index | |
| 257 | +hte/hte driver-api/hte/hte | |
| 258 | +hte/index driver-api/hte/index | |
| 259 | +hte/tegra194-hte driver-api/hte/tegra-hte | |
| 260 | +input/alps input/devices/alps | |
| 261 | +input/amijoy input/devices/amijoy | |
| 262 | +input/appletouch input/devices/appletouch | |
| 263 | +input/atarikbd input/devices/atarikbd | |
| 264 | +input/bcm5974 input/devices/bcm5974 | |
| 265 | +input/cma3000_d0x input/devices/cma3000_d0x | |
| 266 | +input/cs461x input/devices/cs461x | |
| 267 | +input/edt-ft5x06 input/devices/edt-ft5x06 | |
| 268 | +input/elantech input/devices/elantech | |
| 269 | +input/iforce-protocol input/devices/iforce-protocol | |
| 270 | +input/joystick input/joydev/joystick | |
| 271 | +input/joystick-api input/joydev/joystick-api | |
| 272 | +input/joystick-parport input/devices/joystick-parport | |
| 273 | +input/ntrig input/devices/ntrig | |
| 274 | +input/rotary-encoder input/devices/rotary-encoder | |
| 275 | +input/sentelic input/devices/sentelic | |
| 276 | +input/walkera0701 input/devices/walkera0701 | |
| 277 | +input/xpad input/devices/xpad | |
| 278 | +input/yealink input/devices/yealink | |
| 279 | +interconnect/interconnect driver-api/interconnect | |
| 280 | +ioctl/botching-up-ioctls process/botching-up-ioctls | |
| 281 | +ioctl/cdrom userspace-api/ioctl/cdrom | |
| 282 | +ioctl/hdio userspace-api/ioctl/hdio | |
| 283 | +ioctl/index userspace-api/ioctl/index | |
| 284 | +ioctl/ioctl-decoding userspace-api/ioctl/ioctl-decoding | |
| 285 | +ioctl/ioctl-number userspace-api/ioctl/ioctl-number | |
| 286 | +kbuild/namespaces core-api/symbol-namespaces | |
| 287 | +kdump/index admin-guide/kdump/index | |
| 288 | +kdump/kdump admin-guide/kdump/kdump | |
| 289 | +kdump/vmcoreinfo admin-guide/kdump/vmcoreinfo | |
| 290 | +kernel-documentation doc-guide/kernel-doc | |
| 291 | +laptops/asus-laptop admin-guide/laptops/asus-laptop | |
| 292 | +laptops/disk-shock-protection admin-guide/laptops/disk-shock-protection | |
| 293 | +laptops/index admin-guide/laptops/index | |
| 294 | +laptops/laptop-mode admin-guide/laptops/laptop-mode | |
| 295 | +laptops/lg-laptop admin-guide/laptops/lg-laptop | |
| 296 | +laptops/sony-laptop admin-guide/laptops/sony-laptop | |
| 297 | +laptops/sonypi admin-guide/laptops/sonypi | |
| 298 | +laptops/thinkpad-acpi admin-guide/laptops/thinkpad-acpi | |
| 299 | +laptops/toshiba_haps admin-guide/laptops/toshiba_haps | |
| 300 | +loongarch/booting arch/loongarch/booting | |
| 301 | +loongarch/features arch/loongarch/features | |
| 302 | +loongarch/index arch/loongarch/index | |
| 303 | +loongarch/introduction arch/loongarch/introduction | |
| 304 | +loongarch/irq-chip-model arch/loongarch/irq-chip-model | |
| 305 | +m68k/buddha-driver arch/m68k/buddha-driver | |
| 306 | +m68k/features arch/m68k/features | |
| 307 | +m68k/index arch/m68k/index | |
| 308 | +m68k/kernel-options arch/m68k/kernel-options | |
| 309 | +md/index driver-api/md/index | |
| 310 | +md/md-cluster driver-api/md/md-cluster | |
| 311 | +md/raid5-cache driver-api/md/raid5-cache | |
| 312 | +md/raid5-ppl driver-api/md/raid5-ppl | |
| 313 | +media/dvb-drivers/avermedia admin-guide/media/avermedia | |
| 314 | +media/dvb-drivers/bt8xx admin-guide/media/bt8xx | |
| 315 | +media/dvb-drivers/ci admin-guide/media/ci | |
| 316 | +media/dvb-drivers/contributors driver-api/media/drivers/contributors | |
| 317 | +media/dvb-drivers/dvb-usb driver-api/media/drivers/dvb-usb | |
| 318 | +media/dvb-drivers/faq admin-guide/media/faq | |
| 319 | +media/dvb-drivers/frontends driver-api/media/drivers/frontends | |
| 320 | +media/dvb-drivers/index driver-api/media/drivers/index | |
| 321 | +media/dvb-drivers/lmedm04 admin-guide/media/lmedm04 | |
| 322 | +media/dvb-drivers/opera-firmware admin-guide/media/opera-firmware | |
| 323 | +media/dvb-drivers/technisat admin-guide/media/technisat | |
| 324 | +media/dvb-drivers/ttusb-dec admin-guide/media/ttusb-dec | |
| 325 | +media/intro userspace-api/media/intro | |
| 326 | +media/kapi/cec-core driver-api/media/cec-core | |
| 327 | +media/kapi/dtv-ca driver-api/media/dtv-ca | |
| 328 | +media/kapi/dtv-common driver-api/media/dtv-common | |
| 329 | +media/kapi/dtv-core driver-api/media/dtv-core | |
| 330 | +media/kapi/dtv-demux driver-api/media/dtv-demux | |
| 331 | +media/kapi/dtv-frontend driver-api/media/dtv-frontend | |
| 332 | +media/kapi/dtv-net driver-api/media/dtv-net | |
| 333 | +media/kapi/mc-core driver-api/media/mc-core | |
| 334 | +media/kapi/rc-core driver-api/media/rc-core | |
| 335 | +media/kapi/v4l2-async driver-api/media/v4l2-async | |
| 336 | +media/kapi/v4l2-common driver-api/media/v4l2-common | |
| 337 | +media/kapi/v4l2-controls driver-api/media/v4l2-controls | |
| 338 | +media/kapi/v4l2-core driver-api/media/v4l2-core | |
| 339 | +media/kapi/v4l2-dev driver-api/media/v4l2-dev | |
| 340 | +media/kapi/v4l2-device driver-api/media/v4l2-device | |
| 341 | +media/kapi/v4l2-dv-timings driver-api/media/v4l2-dv-timings | |
| 342 | +media/kapi/v4l2-event driver-api/media/v4l2-event | |
| 343 | +media/kapi/v4l2-fh driver-api/media/v4l2-fh | |
| 344 | +media/kapi/v4l2-flash-led-class driver-api/media/v4l2-flash-led-class | |
| 345 | +media/kapi/v4l2-fwnode driver-api/media/v4l2-fwnode | |
| 346 | +media/kapi/v4l2-intro driver-api/media/v4l2-intro | |
| 347 | +media/kapi/v4l2-mc driver-api/media/v4l2-mc | |
| 348 | +media/kapi/v4l2-mediabus driver-api/media/v4l2-mediabus | |
| 349 | +media/kapi/v4l2-mem2mem driver-api/media/v4l2-mem2mem | |
| 350 | +media/kapi/v4l2-rect driver-api/media/v4l2-rect | |
| 351 | +media/kapi/v4l2-subdev driver-api/media/v4l2-subdev | |
| 352 | +media/kapi/v4l2-tuner driver-api/media/v4l2-tuner | |
| 353 | +media/kapi/v4l2-tveeprom driver-api/media/v4l2-tveeprom | |
| 354 | +media/kapi/v4l2-videobuf2 driver-api/media/v4l2-videobuf2 | |
| 355 | +media/media_kapi driver-api/media/index | |
| 356 | +media/media_uapi userspace-api/media/index | |
| 357 | +media/uapi/cec/cec-api userspace-api/media/cec/cec-api | |
| 358 | +media/uapi/cec/cec-func-close userspace-api/media/cec/cec-func-close | |
| 359 | +media/uapi/cec/cec-func-ioctl userspace-api/media/cec/cec-func-ioctl | |
| 360 | +media/uapi/cec/cec-func-open userspace-api/media/cec/cec-func-open | |
| 361 | +media/uapi/cec/cec-func-poll userspace-api/media/cec/cec-func-poll | |
| 362 | +media/uapi/cec/cec-funcs userspace-api/media/cec/cec-funcs | |
| 363 | +media/uapi/cec/cec-header userspace-api/media/cec/cec-header | |
| 364 | +media/uapi/cec/cec-intro userspace-api/media/cec/cec-intro | |
| 365 | +media/uapi/cec/cec-ioc-adap-g-caps userspace-api/media/cec/cec-ioc-adap-g-caps | |
| 366 | +media/uapi/cec/cec-ioc-adap-g-conn-info userspace-api/media/cec/cec-ioc-adap-g-conn-info | |
| 367 | +media/uapi/cec/cec-ioc-adap-g-log-addrs userspace-api/media/cec/cec-ioc-adap-g-log-addrs | |
| 368 | +media/uapi/cec/cec-ioc-adap-g-phys-addr userspace-api/media/cec/cec-ioc-adap-g-phys-addr | |
| 369 | +media/uapi/cec/cec-ioc-dqevent userspace-api/media/cec/cec-ioc-dqevent | |
| 370 | +media/uapi/cec/cec-ioc-g-mode userspace-api/media/cec/cec-ioc-g-mode | |
| 371 | +media/uapi/cec/cec-ioc-receive userspace-api/media/cec/cec-ioc-receive | |
| 372 | +media/uapi/cec/cec-pin-error-inj userspace-api/media/cec/cec-pin-error-inj | |
| 373 | +media/uapi/dvb/ca userspace-api/media/dvb/ca | |
| 374 | +media/uapi/dvb/ca-fclose userspace-api/media/dvb/ca-fclose | |
| 375 | +media/uapi/dvb/ca-fopen userspace-api/media/dvb/ca-fopen | |
| 376 | +media/uapi/dvb/ca-get-cap userspace-api/media/dvb/ca-get-cap | |
| 377 | +media/uapi/dvb/ca-get-descr-info userspace-api/media/dvb/ca-get-descr-info | |
| 378 | +media/uapi/dvb/ca-get-msg userspace-api/media/dvb/ca-get-msg | |
| 379 | +media/uapi/dvb/ca-get-slot-info userspace-api/media/dvb/ca-get-slot-info | |
| 380 | +media/uapi/dvb/ca-reset userspace-api/media/dvb/ca-reset | |
| 381 | +media/uapi/dvb/ca-send-msg userspace-api/media/dvb/ca-send-msg | |
| 382 | +media/uapi/dvb/ca-set-descr userspace-api/media/dvb/ca-set-descr | |
| 383 | +media/uapi/dvb/ca_data_types userspace-api/media/dvb/ca_data_types | |
| 384 | +media/uapi/dvb/ca_function_calls userspace-api/media/dvb/ca_function_calls | |
| 385 | +media/uapi/dvb/ca_high_level userspace-api/media/dvb/ca_high_level | |
| 386 | +media/uapi/dvb/demux userspace-api/media/dvb/demux | |
| 387 | +media/uapi/dvb/dmx-add-pid userspace-api/media/dvb/dmx-add-pid | |
| 388 | +media/uapi/dvb/dmx-expbuf userspace-api/media/dvb/dmx-expbuf | |
| 389 | +media/uapi/dvb/dmx-fclose userspace-api/media/dvb/dmx-fclose | |
| 390 | +media/uapi/dvb/dmx-fopen userspace-api/media/dvb/dmx-fopen | |
| 391 | +media/uapi/dvb/dmx-fread userspace-api/media/dvb/dmx-fread | |
| 392 | +media/uapi/dvb/dmx-fwrite userspace-api/media/dvb/dmx-fwrite | |
| 393 | +media/uapi/dvb/dmx-get-pes-pids userspace-api/media/dvb/dmx-get-pes-pids | |
| 394 | +media/uapi/dvb/dmx-get-stc userspace-api/media/dvb/dmx-get-stc | |
| 395 | +media/uapi/dvb/dmx-mmap userspace-api/media/dvb/dmx-mmap | |
| 396 | +media/uapi/dvb/dmx-munmap userspace-api/media/dvb/dmx-munmap | |
| 397 | +media/uapi/dvb/dmx-qbuf userspace-api/media/dvb/dmx-qbuf | |
| 398 | +media/uapi/dvb/dmx-querybuf userspace-api/media/dvb/dmx-querybuf | |
| 399 | +media/uapi/dvb/dmx-remove-pid userspace-api/media/dvb/dmx-remove-pid | |
| 400 | +media/uapi/dvb/dmx-reqbufs userspace-api/media/dvb/dmx-reqbufs | |
| 401 | +media/uapi/dvb/dmx-set-buffer-size userspace-api/media/dvb/dmx-set-buffer-size | |
| 402 | +media/uapi/dvb/dmx-set-filter userspace-api/media/dvb/dmx-set-filter | |
| 403 | +media/uapi/dvb/dmx-set-pes-filter userspace-api/media/dvb/dmx-set-pes-filter | |
| 404 | +media/uapi/dvb/dmx-start userspace-api/media/dvb/dmx-start | |
| 405 | +media/uapi/dvb/dmx-stop userspace-api/media/dvb/dmx-stop | |
| 406 | +media/uapi/dvb/dmx_fcalls userspace-api/media/dvb/dmx_fcalls | |
| 407 | +media/uapi/dvb/dmx_types userspace-api/media/dvb/dmx_types | |
| 408 | +media/uapi/dvb/dvb-fe-read-status userspace-api/media/dvb/dvb-fe-read-status | |
| 409 | +media/uapi/dvb/dvb-frontend-event userspace-api/media/dvb/dvb-frontend-event | |
| 410 | +media/uapi/dvb/dvb-frontend-parameters userspace-api/media/dvb/dvb-frontend-parameters | |
| 411 | +media/uapi/dvb/dvbapi userspace-api/media/dvb/dvbapi | |
| 412 | +media/uapi/dvb/dvbproperty userspace-api/media/dvb/dvbproperty | |
| 413 | +media/uapi/dvb/examples userspace-api/media/dvb/examples | |
| 414 | +media/uapi/dvb/fe-bandwidth-t userspace-api/media/dvb/fe-bandwidth-t | |
| 415 | +media/uapi/dvb/fe-diseqc-recv-slave-reply userspace-api/media/dvb/fe-diseqc-recv-slave-reply | |
| 416 | +media/uapi/dvb/fe-diseqc-reset-overload userspace-api/media/dvb/fe-diseqc-reset-overload | |
| 417 | +media/uapi/dvb/fe-diseqc-send-burst userspace-api/media/dvb/fe-diseqc-send-burst | |
| 418 | +media/uapi/dvb/fe-diseqc-send-master-cmd userspace-api/media/dvb/fe-diseqc-send-master-cmd | |
| 419 | +media/uapi/dvb/fe-dishnetwork-send-legacy-cmd userspace-api/media/dvb/fe-dishnetwork-send-legacy-cmd | |
| 420 | +media/uapi/dvb/fe-enable-high-lnb-voltage userspace-api/media/dvb/fe-enable-high-lnb-voltage | |
| 421 | +media/uapi/dvb/fe-get-event userspace-api/media/dvb/fe-get-event | |
| 422 | +media/uapi/dvb/fe-get-frontend userspace-api/media/dvb/fe-get-frontend | |
| 423 | +media/uapi/dvb/fe-get-info userspace-api/media/dvb/fe-get-info | |
| 424 | +media/uapi/dvb/fe-get-property userspace-api/media/dvb/fe-get-property | |
| 425 | +media/uapi/dvb/fe-read-ber userspace-api/media/dvb/fe-read-ber | |
| 426 | +media/uapi/dvb/fe-read-signal-strength userspace-api/media/dvb/fe-read-signal-strength | |
| 427 | +media/uapi/dvb/fe-read-snr userspace-api/media/dvb/fe-read-snr | |
| 428 | +media/uapi/dvb/fe-read-status userspace-api/media/dvb/fe-read-status | |
| 429 | +media/uapi/dvb/fe-read-uncorrected-blocks userspace-api/media/dvb/fe-read-uncorrected-blocks | |
| 430 | +media/uapi/dvb/fe-set-frontend userspace-api/media/dvb/fe-set-frontend | |
| 431 | +media/uapi/dvb/fe-set-frontend-tune-mode userspace-api/media/dvb/fe-set-frontend-tune-mode | |
| 432 | +media/uapi/dvb/fe-set-tone userspace-api/media/dvb/fe-set-tone | |
| 433 | +media/uapi/dvb/fe-set-voltage userspace-api/media/dvb/fe-set-voltage | |
| 434 | +media/uapi/dvb/fe-type-t userspace-api/media/dvb/fe-type-t | |
| 435 | +media/uapi/dvb/fe_property_parameters userspace-api/media/dvb/fe_property_parameters | |
| 436 | +media/uapi/dvb/frontend userspace-api/media/dvb/frontend | |
| 437 | +media/uapi/dvb/frontend-header userspace-api/media/dvb/frontend-header | |
| 438 | +media/uapi/dvb/frontend-property-cable-systems userspace-api/media/dvb/frontend-property-cable-systems | |
| 439 | +media/uapi/dvb/frontend-property-satellite-systems userspace-api/media/dvb/frontend-property-satellite-systems | |
| 440 | +media/uapi/dvb/frontend-property-terrestrial-systems userspace-api/media/dvb/frontend-property-terrestrial-systems | |
| 441 | +media/uapi/dvb/frontend-stat-properties userspace-api/media/dvb/frontend-stat-properties | |
| 442 | +media/uapi/dvb/frontend_f_close userspace-api/media/dvb/frontend_f_close | |
| 443 | +media/uapi/dvb/frontend_f_open userspace-api/media/dvb/frontend_f_open | |
| 444 | +media/uapi/dvb/frontend_fcalls userspace-api/media/dvb/frontend_fcalls | |
| 445 | +media/uapi/dvb/frontend_legacy_api userspace-api/media/dvb/frontend_legacy_api | |
| 446 | +media/uapi/dvb/frontend_legacy_dvbv3_api userspace-api/media/dvb/frontend_legacy_dvbv3_api | |
| 447 | +media/uapi/dvb/headers userspace-api/media/dvb/headers | |
| 448 | +media/uapi/dvb/intro userspace-api/media/dvb/intro | |
| 449 | +media/uapi/dvb/legacy_dvb_apis userspace-api/media/dvb/legacy_dvb_apis | |
| 450 | +media/uapi/dvb/net userspace-api/media/dvb/net | |
| 451 | +media/uapi/dvb/net-add-if userspace-api/media/dvb/net-add-if | |
| 452 | +media/uapi/dvb/net-get-if userspace-api/media/dvb/net-get-if | |
| 453 | +media/uapi/dvb/net-remove-if userspace-api/media/dvb/net-remove-if | |
| 454 | +media/uapi/dvb/net-types userspace-api/media/dvb/net-types | |
| 455 | +media/uapi/dvb/query-dvb-frontend-info userspace-api/media/dvb/query-dvb-frontend-info | |
| 456 | +media/uapi/fdl-appendix userspace-api/media/fdl-appendix | |
| 457 | +media/uapi/gen-errors userspace-api/media/gen-errors | |
| 458 | +media/uapi/mediactl/media-controller userspace-api/media/mediactl/media-controller | |
| 459 | +media/uapi/mediactl/media-controller-intro userspace-api/media/mediactl/media-controller-intro | |
| 460 | +media/uapi/mediactl/media-controller-model userspace-api/media/mediactl/media-controller-model | |
| 461 | +media/uapi/mediactl/media-func-close userspace-api/media/mediactl/media-func-close | |
| 462 | +media/uapi/mediactl/media-func-ioctl userspace-api/media/mediactl/media-func-ioctl | |
| 463 | +media/uapi/mediactl/media-func-open userspace-api/media/mediactl/media-func-open | |
| 464 | +media/uapi/mediactl/media-funcs userspace-api/media/mediactl/media-funcs | |
| 465 | +media/uapi/mediactl/media-header userspace-api/media/mediactl/media-header | |
| 466 | +media/uapi/mediactl/media-ioc-device-info userspace-api/media/mediactl/media-ioc-device-info | |
| 467 | +media/uapi/mediactl/media-ioc-enum-entities userspace-api/media/mediactl/media-ioc-enum-entities | |
| 468 | +media/uapi/mediactl/media-ioc-enum-links userspace-api/media/mediactl/media-ioc-enum-links | |
| 469 | +media/uapi/mediactl/media-ioc-g-topology userspace-api/media/mediactl/media-ioc-g-topology | |
| 470 | +media/uapi/mediactl/media-ioc-request-alloc userspace-api/media/mediactl/media-ioc-request-alloc | |
| 471 | +media/uapi/mediactl/media-ioc-setup-link userspace-api/media/mediactl/media-ioc-setup-link | |
| 472 | +media/uapi/mediactl/media-request-ioc-queue userspace-api/media/mediactl/media-request-ioc-queue | |
| 473 | +media/uapi/mediactl/media-request-ioc-reinit userspace-api/media/mediactl/media-request-ioc-reinit | |
| 474 | +media/uapi/mediactl/media-types userspace-api/media/mediactl/media-types | |
| 475 | +media/uapi/mediactl/request-api userspace-api/media/mediactl/request-api | |
| 476 | +media/uapi/mediactl/request-func-close userspace-api/media/mediactl/request-func-close | |
| 477 | +media/uapi/mediactl/request-func-ioctl userspace-api/media/mediactl/request-func-ioctl | |
| 478 | +media/uapi/mediactl/request-func-poll userspace-api/media/mediactl/request-func-poll | |
| 479 | +media/uapi/rc/keytable.c userspace-api/media/rc/keytable.c | |
| 480 | +media/uapi/rc/lirc-dev userspace-api/media/rc/lirc-dev | |
| 481 | +media/uapi/rc/lirc-dev-intro userspace-api/media/rc/lirc-dev-intro | |
| 482 | +media/uapi/rc/lirc-func userspace-api/media/rc/lirc-func | |
| 483 | +media/uapi/rc/lirc-get-features userspace-api/media/rc/lirc-get-features | |
| 484 | +media/uapi/rc/lirc-get-rec-mode userspace-api/media/rc/lirc-get-rec-mode | |
| 485 | +media/uapi/rc/lirc-get-rec-resolution userspace-api/media/rc/lirc-get-rec-resolution | |
| 486 | +media/uapi/rc/lirc-get-send-mode userspace-api/media/rc/lirc-get-send-mode | |
| 487 | +media/uapi/rc/lirc-get-timeout userspace-api/media/rc/lirc-get-timeout | |
| 488 | +media/uapi/rc/lirc-header userspace-api/media/rc/lirc-header | |
| 489 | +media/uapi/rc/lirc-read userspace-api/media/rc/lirc-read | |
| 490 | +media/uapi/rc/lirc-set-measure-carrier-mode userspace-api/media/rc/lirc-set-measure-carrier-mode | |
| 491 | +media/uapi/rc/lirc-set-rec-carrier userspace-api/media/rc/lirc-set-rec-carrier | |
| 492 | +media/uapi/rc/lirc-set-rec-carrier-range userspace-api/media/rc/lirc-set-rec-carrier-range | |
| 493 | +media/uapi/rc/lirc-set-rec-timeout userspace-api/media/rc/lirc-set-rec-timeout | |
| 494 | +media/uapi/rc/lirc-set-send-carrier userspace-api/media/rc/lirc-set-send-carrier | |
| 495 | +media/uapi/rc/lirc-set-send-duty-cycle userspace-api/media/rc/lirc-set-send-duty-cycle | |
| 496 | +media/uapi/rc/lirc-set-transmitter-mask userspace-api/media/rc/lirc-set-transmitter-mask | |
| 497 | +media/uapi/rc/lirc-set-wideband-receiver userspace-api/media/rc/lirc-set-wideband-receiver | |
| 498 | +media/uapi/rc/lirc-write userspace-api/media/rc/lirc-write | |
| 499 | +media/uapi/rc/rc-intro userspace-api/media/rc/rc-intro | |
| 500 | +media/uapi/rc/rc-protos userspace-api/media/rc/rc-protos | |
| 501 | +media/uapi/rc/rc-sysfs-nodes userspace-api/media/rc/rc-sysfs-nodes | |
| 502 | +media/uapi/rc/rc-table-change userspace-api/media/rc/rc-table-change | |
| 503 | +media/uapi/rc/rc-tables userspace-api/media/rc/rc-tables | |
| 504 | +media/uapi/rc/remote_controllers userspace-api/media/rc/remote_controllers | |
| 505 | +media/uapi/v4l/app-pri userspace-api/media/v4l/app-pri | |
| 506 | +media/uapi/v4l/audio userspace-api/media/v4l/audio | |
| 507 | +media/uapi/v4l/biblio userspace-api/media/v4l/biblio | |
| 508 | +media/uapi/v4l/buffer userspace-api/media/v4l/buffer | |
| 509 | +media/uapi/v4l/capture-example userspace-api/media/v4l/capture-example | |
| 510 | +media/uapi/v4l/capture.c userspace-api/media/v4l/capture.c | |
| 511 | +media/uapi/v4l/colorspaces userspace-api/media/v4l/colorspaces | |
| 512 | +media/uapi/v4l/colorspaces-defs userspace-api/media/v4l/colorspaces-defs | |
| 513 | +media/uapi/v4l/colorspaces-details userspace-api/media/v4l/colorspaces-details | |
| 514 | +media/uapi/v4l/common userspace-api/media/v4l/common | |
| 515 | +media/uapi/v4l/common-defs userspace-api/media/v4l/common-defs | |
| 516 | +media/uapi/v4l/compat userspace-api/media/v4l/compat | |
| 517 | +media/uapi/v4l/control userspace-api/media/v4l/control | |
| 518 | +media/uapi/v4l/crop userspace-api/media/v4l/crop | |
| 519 | +media/uapi/v4l/depth-formats userspace-api/media/v4l/depth-formats | |
| 520 | +media/uapi/v4l/dev-capture userspace-api/media/v4l/dev-capture | |
| 521 | +media/uapi/v4l/dev-codec userspace-api/media/v4l/dev-mem2mem | |
| 522 | +media/uapi/v4l/dev-decoder userspace-api/media/v4l/dev-decoder | |
| 523 | +media/uapi/v4l/dev-event userspace-api/media/v4l/dev-event | |
| 524 | +media/uapi/v4l/dev-mem2mem userspace-api/media/v4l/dev-mem2mem | |
| 525 | +media/uapi/v4l/dev-meta userspace-api/media/v4l/dev-meta | |
| 526 | +media/uapi/v4l/dev-osd userspace-api/media/v4l/dev-osd | |
| 527 | +media/uapi/v4l/dev-output userspace-api/media/v4l/dev-output | |
| 528 | +media/uapi/v4l/dev-overlay userspace-api/media/v4l/dev-overlay | |
| 529 | +media/uapi/v4l/dev-radio userspace-api/media/v4l/dev-radio | |
| 530 | +media/uapi/v4l/dev-raw-vbi userspace-api/media/v4l/dev-raw-vbi | |
| 531 | +media/uapi/v4l/dev-rds userspace-api/media/v4l/dev-rds | |
| 532 | +media/uapi/v4l/dev-sdr userspace-api/media/v4l/dev-sdr | |
| 533 | +media/uapi/v4l/dev-sliced-vbi userspace-api/media/v4l/dev-sliced-vbi | |
| 534 | +media/uapi/v4l/dev-stateless-decoder userspace-api/media/v4l/dev-stateless-decoder | |
| 535 | +media/uapi/v4l/dev-subdev userspace-api/media/v4l/dev-subdev | |
| 536 | +media/uapi/v4l/dev-touch userspace-api/media/v4l/dev-touch | |
| 537 | +media/uapi/v4l/devices userspace-api/media/v4l/devices | |
| 538 | +media/uapi/v4l/diff-v4l userspace-api/media/v4l/diff-v4l | |
| 539 | +media/uapi/v4l/dmabuf userspace-api/media/v4l/dmabuf | |
| 540 | +media/uapi/v4l/dv-timings userspace-api/media/v4l/dv-timings | |
| 541 | +media/uapi/v4l/ext-ctrls-camera userspace-api/media/v4l/ext-ctrls-camera | |
| 542 | +media/uapi/v4l/ext-ctrls-codec userspace-api/media/v4l/ext-ctrls-codec | |
| 543 | +media/uapi/v4l/ext-ctrls-detect userspace-api/media/v4l/ext-ctrls-detect | |
| 544 | +media/uapi/v4l/ext-ctrls-dv userspace-api/media/v4l/ext-ctrls-dv | |
| 545 | +media/uapi/v4l/ext-ctrls-flash userspace-api/media/v4l/ext-ctrls-flash | |
| 546 | +media/uapi/v4l/ext-ctrls-fm-rx userspace-api/media/v4l/ext-ctrls-fm-rx | |
| 547 | +media/uapi/v4l/ext-ctrls-fm-tx userspace-api/media/v4l/ext-ctrls-fm-tx | |
| 548 | +media/uapi/v4l/ext-ctrls-image-process userspace-api/media/v4l/ext-ctrls-image-process | |
| 549 | +media/uapi/v4l/ext-ctrls-image-source userspace-api/media/v4l/ext-ctrls-image-source | |
| 550 | +media/uapi/v4l/ext-ctrls-jpeg userspace-api/media/v4l/ext-ctrls-jpeg | |
| 551 | +media/uapi/v4l/ext-ctrls-rf-tuner userspace-api/media/v4l/ext-ctrls-rf-tuner | |
| 552 | +media/uapi/v4l/extended-controls userspace-api/media/v4l/extended-controls | |
| 553 | +media/uapi/v4l/field-order userspace-api/media/v4l/field-order | |
| 554 | +media/uapi/v4l/format userspace-api/media/v4l/format | |
| 555 | +media/uapi/v4l/func-close userspace-api/media/v4l/func-close | |
| 556 | +media/uapi/v4l/func-ioctl userspace-api/media/v4l/func-ioctl | |
| 557 | +media/uapi/v4l/func-mmap userspace-api/media/v4l/func-mmap | |
| 558 | +media/uapi/v4l/func-munmap userspace-api/media/v4l/func-munmap | |
| 559 | +media/uapi/v4l/func-open userspace-api/media/v4l/func-open | |
| 560 | +media/uapi/v4l/func-poll userspace-api/media/v4l/func-poll | |
| 561 | +media/uapi/v4l/func-read userspace-api/media/v4l/func-read | |
| 562 | +media/uapi/v4l/func-select userspace-api/media/v4l/func-select | |
| 563 | +media/uapi/v4l/func-write userspace-api/media/v4l/func-write | |
| 564 | +media/uapi/v4l/hist-v4l2 userspace-api/media/v4l/hist-v4l2 | |
| 565 | +media/uapi/v4l/hsv-formats userspace-api/media/v4l/hsv-formats | |
| 566 | +media/uapi/v4l/io userspace-api/media/v4l/io | |
| 567 | +media/uapi/v4l/libv4l userspace-api/media/v4l/libv4l | |
| 568 | +media/uapi/v4l/libv4l-introduction userspace-api/media/v4l/libv4l-introduction | |
| 569 | +media/uapi/v4l/meta-formats userspace-api/media/v4l/meta-formats | |
| 570 | +media/uapi/v4l/mmap userspace-api/media/v4l/mmap | |
| 571 | +media/uapi/v4l/open userspace-api/media/v4l/open | |
| 572 | +media/uapi/v4l/pixfmt userspace-api/media/v4l/pixfmt | |
| 573 | +media/uapi/v4l/pixfmt-002 userspace-api/media/v4l/pixfmt-v4l2 | |
| 574 | +media/uapi/v4l/pixfmt-003 userspace-api/media/v4l/pixfmt-v4l2-mplane | |
| 575 | +media/uapi/v4l/pixfmt-004 userspace-api/media/v4l/pixfmt-intro | |
| 576 | +media/uapi/v4l/pixfmt-006 userspace-api/media/v4l/colorspaces-defs | |
| 577 | +media/uapi/v4l/pixfmt-007 userspace-api/media/v4l/colorspaces-details | |
| 578 | +media/uapi/v4l/pixfmt-013 userspace-api/media/v4l/pixfmt-compressed | |
| 579 | +media/uapi/v4l/pixfmt-bayer userspace-api/media/v4l/pixfmt-bayer | |
| 580 | +media/uapi/v4l/pixfmt-cnf4 userspace-api/media/v4l/pixfmt-cnf4 | |
| 581 | +media/uapi/v4l/pixfmt-compressed userspace-api/media/v4l/pixfmt-compressed | |
| 582 | +media/uapi/v4l/pixfmt-indexed userspace-api/media/v4l/pixfmt-indexed | |
| 583 | +media/uapi/v4l/pixfmt-intro userspace-api/media/v4l/pixfmt-intro | |
| 584 | +media/uapi/v4l/pixfmt-inzi userspace-api/media/v4l/pixfmt-inzi | |
| 585 | +media/uapi/v4l/pixfmt-m420 userspace-api/media/v4l/pixfmt-m420 | |
| 586 | +media/uapi/v4l/pixfmt-meta-d4xx userspace-api/media/v4l/metafmt-d4xx | |
| 587 | +media/uapi/v4l/pixfmt-meta-intel-ipu3 userspace-api/media/v4l/metafmt-intel-ipu3 | |
| 588 | +media/uapi/v4l/pixfmt-meta-uvc userspace-api/media/v4l/metafmt-uvc | |
| 589 | +media/uapi/v4l/pixfmt-meta-vivid userspace-api/media/v4l/metafmt-vivid | |
| 590 | +media/uapi/v4l/pixfmt-meta-vsp1-hgo userspace-api/media/v4l/metafmt-vsp1-hgo | |
| 591 | +media/uapi/v4l/pixfmt-meta-vsp1-hgt userspace-api/media/v4l/metafmt-vsp1-hgt | |
| 592 | +media/uapi/v4l/pixfmt-packed-hsv userspace-api/media/v4l/pixfmt-packed-hsv | |
| 593 | +media/uapi/v4l/pixfmt-packed-yuv userspace-api/media/v4l/pixfmt-packed-yuv | |
| 594 | +media/uapi/v4l/pixfmt-reserved userspace-api/media/v4l/pixfmt-reserved | |
| 595 | +media/uapi/v4l/pixfmt-rgb userspace-api/media/v4l/pixfmt-rgb | |
| 596 | +media/uapi/v4l/pixfmt-sbggr16 userspace-api/media/v4l/pixfmt-srggb16 | |
| 597 | +media/uapi/v4l/pixfmt-sdr-cs08 userspace-api/media/v4l/pixfmt-sdr-cs08 | |
| 598 | +media/uapi/v4l/pixfmt-sdr-cs14le userspace-api/media/v4l/pixfmt-sdr-cs14le | |
| 599 | +media/uapi/v4l/pixfmt-sdr-cu08 userspace-api/media/v4l/pixfmt-sdr-cu08 | |
| 600 | +media/uapi/v4l/pixfmt-sdr-cu16le userspace-api/media/v4l/pixfmt-sdr-cu16le | |
| 601 | +media/uapi/v4l/pixfmt-sdr-pcu16be userspace-api/media/v4l/pixfmt-sdr-pcu16be | |
| 602 | +media/uapi/v4l/pixfmt-sdr-pcu18be userspace-api/media/v4l/pixfmt-sdr-pcu18be | |
| 603 | +media/uapi/v4l/pixfmt-sdr-pcu20be userspace-api/media/v4l/pixfmt-sdr-pcu20be | |
| 604 | +media/uapi/v4l/pixfmt-sdr-ru12le userspace-api/media/v4l/pixfmt-sdr-ru12le | |
| 605 | +media/uapi/v4l/pixfmt-srggb10 userspace-api/media/v4l/pixfmt-srggb10 | |
| 606 | +media/uapi/v4l/pixfmt-srggb10-ipu3 userspace-api/media/v4l/pixfmt-srggb10-ipu3 | |
| 607 | +media/uapi/v4l/pixfmt-srggb10alaw8 userspace-api/media/v4l/pixfmt-srggb10alaw8 | |
| 608 | +media/uapi/v4l/pixfmt-srggb10dpcm8 userspace-api/media/v4l/pixfmt-srggb10dpcm8 | |
| 609 | +media/uapi/v4l/pixfmt-srggb10p userspace-api/media/v4l/pixfmt-srggb10p | |
| 610 | +media/uapi/v4l/pixfmt-srggb12 userspace-api/media/v4l/pixfmt-srggb12 | |
| 611 | +media/uapi/v4l/pixfmt-srggb12p userspace-api/media/v4l/pixfmt-srggb12p | |
| 612 | +media/uapi/v4l/pixfmt-srggb14 userspace-api/media/v4l/pixfmt-srggb14 | |
| 613 | +media/uapi/v4l/pixfmt-srggb14p userspace-api/media/v4l/pixfmt-srggb14p | |
| 614 | +media/uapi/v4l/pixfmt-srggb16 userspace-api/media/v4l/pixfmt-srggb16 | |
| 615 | +media/uapi/v4l/pixfmt-srggb8 userspace-api/media/v4l/pixfmt-srggb8 | |
| 616 | +media/uapi/v4l/pixfmt-tch-td08 userspace-api/media/v4l/pixfmt-tch-td08 | |
| 617 | +media/uapi/v4l/pixfmt-tch-td16 userspace-api/media/v4l/pixfmt-tch-td16 | |
| 618 | +media/uapi/v4l/pixfmt-tch-tu08 userspace-api/media/v4l/pixfmt-tch-tu08 | |
| 619 | +media/uapi/v4l/pixfmt-tch-tu16 userspace-api/media/v4l/pixfmt-tch-tu16 | |
| 620 | +media/uapi/v4l/pixfmt-uv8 userspace-api/media/v4l/pixfmt-uv8 | |
| 621 | +media/uapi/v4l/pixfmt-v4l2 userspace-api/media/v4l/pixfmt-v4l2 | |
| 622 | +media/uapi/v4l/pixfmt-v4l2-mplane userspace-api/media/v4l/pixfmt-v4l2-mplane | |
| 623 | +media/uapi/v4l/pixfmt-y12i userspace-api/media/v4l/pixfmt-y12i | |
| 624 | +media/uapi/v4l/pixfmt-y8i userspace-api/media/v4l/pixfmt-y8i | |
| 625 | +media/uapi/v4l/pixfmt-z16 userspace-api/media/v4l/pixfmt-z16 | |
| 626 | +media/uapi/v4l/planar-apis userspace-api/media/v4l/planar-apis | |
| 627 | +media/uapi/v4l/querycap userspace-api/media/v4l/querycap | |
| 628 | +media/uapi/v4l/rw userspace-api/media/v4l/rw | |
| 629 | +media/uapi/v4l/sdr-formats userspace-api/media/v4l/sdr-formats | |
| 630 | +media/uapi/v4l/selection-api userspace-api/media/v4l/selection-api | |
| 631 | +media/uapi/v4l/selection-api-002 userspace-api/media/v4l/selection-api-intro | |
| 632 | +media/uapi/v4l/selection-api-003 userspace-api/media/v4l/selection-api-targets | |
| 633 | +media/uapi/v4l/selection-api-004 userspace-api/media/v4l/selection-api-configuration | |
| 634 | +media/uapi/v4l/selection-api-005 userspace-api/media/v4l/selection-api-vs-crop-api | |
| 635 | +media/uapi/v4l/selection-api-006 userspace-api/media/v4l/selection-api-examples | |
| 636 | +media/uapi/v4l/selection-api-configuration userspace-api/media/v4l/selection-api-configuration | |
| 637 | +media/uapi/v4l/selection-api-examples userspace-api/media/v4l/selection-api-examples | |
| 638 | +media/uapi/v4l/selection-api-intro userspace-api/media/v4l/selection-api-intro | |
| 639 | +media/uapi/v4l/selection-api-targets userspace-api/media/v4l/selection-api-targets | |
| 640 | +media/uapi/v4l/selection-api-vs-crop-api userspace-api/media/v4l/selection-api-vs-crop-api | |
| 641 | +media/uapi/v4l/selections-common userspace-api/media/v4l/selections-common | |
| 642 | +media/uapi/v4l/standard userspace-api/media/v4l/standard | |
| 643 | +media/uapi/v4l/streaming-par userspace-api/media/v4l/streaming-par | |
| 644 | +media/uapi/v4l/subdev-formats userspace-api/media/v4l/subdev-formats | |
| 645 | +media/uapi/v4l/tch-formats userspace-api/media/v4l/tch-formats | |
| 646 | +media/uapi/v4l/tuner userspace-api/media/v4l/tuner | |
| 647 | +media/uapi/v4l/user-func userspace-api/media/v4l/user-func | |
| 648 | +media/uapi/v4l/userp userspace-api/media/v4l/userp | |
| 649 | +media/uapi/v4l/v4l2 userspace-api/media/v4l/v4l2 | |
| 650 | +media/uapi/v4l/v4l2-selection-flags userspace-api/media/v4l/v4l2-selection-flags | |
| 651 | +media/uapi/v4l/v4l2-selection-targets userspace-api/media/v4l/v4l2-selection-targets | |
| 652 | +media/uapi/v4l/v4l2grab-example userspace-api/media/v4l/v4l2grab-example | |
| 653 | +media/uapi/v4l/v4l2grab.c userspace-api/media/v4l/v4l2grab.c | |
| 654 | +media/uapi/v4l/video userspace-api/media/v4l/video | |
| 655 | +media/uapi/v4l/videodev userspace-api/media/v4l/videodev | |
| 656 | +media/uapi/v4l/vidioc-create-bufs userspace-api/media/v4l/vidioc-create-bufs | |
| 657 | +media/uapi/v4l/vidioc-cropcap userspace-api/media/v4l/vidioc-cropcap | |
| 658 | +media/uapi/v4l/vidioc-dbg-g-chip-info userspace-api/media/v4l/vidioc-dbg-g-chip-info | |
| 659 | +media/uapi/v4l/vidioc-dbg-g-register userspace-api/media/v4l/vidioc-dbg-g-register | |
| 660 | +media/uapi/v4l/vidioc-decoder-cmd userspace-api/media/v4l/vidioc-decoder-cmd | |
| 661 | +media/uapi/v4l/vidioc-dqevent userspace-api/media/v4l/vidioc-dqevent | |
| 662 | +media/uapi/v4l/vidioc-dv-timings-cap userspace-api/media/v4l/vidioc-dv-timings-cap | |
| 663 | +media/uapi/v4l/vidioc-encoder-cmd userspace-api/media/v4l/vidioc-encoder-cmd | |
| 664 | +media/uapi/v4l/vidioc-enum-dv-timings userspace-api/media/v4l/vidioc-enum-dv-timings | |
| 665 | +media/uapi/v4l/vidioc-enum-fmt userspace-api/media/v4l/vidioc-enum-fmt | |
| 666 | +media/uapi/v4l/vidioc-enum-frameintervals userspace-api/media/v4l/vidioc-enum-frameintervals | |
| 667 | +media/uapi/v4l/vidioc-enum-framesizes userspace-api/media/v4l/vidioc-enum-framesizes | |
| 668 | +media/uapi/v4l/vidioc-enum-freq-bands userspace-api/media/v4l/vidioc-enum-freq-bands | |
| 669 | +media/uapi/v4l/vidioc-enumaudio userspace-api/media/v4l/vidioc-enumaudio | |
| 670 | +media/uapi/v4l/vidioc-enumaudioout userspace-api/media/v4l/vidioc-enumaudioout | |
| 671 | +media/uapi/v4l/vidioc-enuminput userspace-api/media/v4l/vidioc-enuminput | |
| 672 | +media/uapi/v4l/vidioc-enumoutput userspace-api/media/v4l/vidioc-enumoutput | |
| 673 | +media/uapi/v4l/vidioc-enumstd userspace-api/media/v4l/vidioc-enumstd | |
| 674 | +media/uapi/v4l/vidioc-expbuf userspace-api/media/v4l/vidioc-expbuf | |
| 675 | +media/uapi/v4l/vidioc-g-audio userspace-api/media/v4l/vidioc-g-audio | |
| 676 | +media/uapi/v4l/vidioc-g-audioout userspace-api/media/v4l/vidioc-g-audioout | |
| 677 | +media/uapi/v4l/vidioc-g-crop userspace-api/media/v4l/vidioc-g-crop | |
| 678 | +media/uapi/v4l/vidioc-g-ctrl userspace-api/media/v4l/vidioc-g-ctrl | |
| 679 | +media/uapi/v4l/vidioc-g-dv-timings userspace-api/media/v4l/vidioc-g-dv-timings | |
| 680 | +media/uapi/v4l/vidioc-g-edid userspace-api/media/v4l/vidioc-g-edid | |
| 681 | +media/uapi/v4l/vidioc-g-enc-index userspace-api/media/v4l/vidioc-g-enc-index | |
| 682 | +media/uapi/v4l/vidioc-g-ext-ctrls userspace-api/media/v4l/vidioc-g-ext-ctrls | |
| 683 | +media/uapi/v4l/vidioc-g-fbuf userspace-api/media/v4l/vidioc-g-fbuf | |
| 684 | +media/uapi/v4l/vidioc-g-fmt userspace-api/media/v4l/vidioc-g-fmt | |
| 685 | +media/uapi/v4l/vidioc-g-frequency userspace-api/media/v4l/vidioc-g-frequency | |
| 686 | +media/uapi/v4l/vidioc-g-input userspace-api/media/v4l/vidioc-g-input | |
| 687 | +media/uapi/v4l/vidioc-g-jpegcomp userspace-api/media/v4l/vidioc-g-jpegcomp | |
| 688 | +media/uapi/v4l/vidioc-g-modulator userspace-api/media/v4l/vidioc-g-modulator | |
| 689 | +media/uapi/v4l/vidioc-g-output userspace-api/media/v4l/vidioc-g-output | |
| 690 | +media/uapi/v4l/vidioc-g-parm userspace-api/media/v4l/vidioc-g-parm | |
| 691 | +media/uapi/v4l/vidioc-g-priority userspace-api/media/v4l/vidioc-g-priority | |
| 692 | +media/uapi/v4l/vidioc-g-selection userspace-api/media/v4l/vidioc-g-selection | |
| 693 | +media/uapi/v4l/vidioc-g-sliced-vbi-cap userspace-api/media/v4l/vidioc-g-sliced-vbi-cap | |
| 694 | +media/uapi/v4l/vidioc-g-std userspace-api/media/v4l/vidioc-g-std | |
| 695 | +media/uapi/v4l/vidioc-g-tuner userspace-api/media/v4l/vidioc-g-tuner | |
| 696 | +media/uapi/v4l/vidioc-log-status userspace-api/media/v4l/vidioc-log-status | |
| 697 | +media/uapi/v4l/vidioc-overlay userspace-api/media/v4l/vidioc-overlay | |
| 698 | +media/uapi/v4l/vidioc-prepare-buf userspace-api/media/v4l/vidioc-prepare-buf | |
| 699 | +media/uapi/v4l/vidioc-qbuf userspace-api/media/v4l/vidioc-qbuf | |
| 700 | +media/uapi/v4l/vidioc-query-dv-timings userspace-api/media/v4l/vidioc-query-dv-timings | |
| 701 | +media/uapi/v4l/vidioc-querybuf userspace-api/media/v4l/vidioc-querybuf | |
| 702 | +media/uapi/v4l/vidioc-querycap userspace-api/media/v4l/vidioc-querycap | |
| 703 | +media/uapi/v4l/vidioc-queryctrl userspace-api/media/v4l/vidioc-queryctrl | |
| 704 | +media/uapi/v4l/vidioc-querystd userspace-api/media/v4l/vidioc-querystd | |
| 705 | +media/uapi/v4l/vidioc-reqbufs userspace-api/media/v4l/vidioc-reqbufs | |
| 706 | +media/uapi/v4l/vidioc-s-hw-freq-seek userspace-api/media/v4l/vidioc-s-hw-freq-seek | |
| 707 | +media/uapi/v4l/vidioc-streamon userspace-api/media/v4l/vidioc-streamon | |
| 708 | +media/uapi/v4l/vidioc-subdev-enum-frame-interval userspace-api/media/v4l/vidioc-subdev-enum-frame-interval | |
| 709 | +media/uapi/v4l/vidioc-subdev-enum-frame-size userspace-api/media/v4l/vidioc-subdev-enum-frame-size | |
| 710 | +media/uapi/v4l/vidioc-subdev-enum-mbus-code userspace-api/media/v4l/vidioc-subdev-enum-mbus-code | |
| 711 | +media/uapi/v4l/vidioc-subdev-g-crop userspace-api/media/v4l/vidioc-subdev-g-crop | |
| 712 | +media/uapi/v4l/vidioc-subdev-g-fmt userspace-api/media/v4l/vidioc-subdev-g-fmt | |
| 713 | +media/uapi/v4l/vidioc-subdev-g-frame-interval userspace-api/media/v4l/vidioc-subdev-g-frame-interval | |
| 714 | +media/uapi/v4l/vidioc-subdev-g-selection userspace-api/media/v4l/vidioc-subdev-g-selection | |
| 715 | +media/uapi/v4l/vidioc-subscribe-event userspace-api/media/v4l/vidioc-subscribe-event | |
| 716 | +media/uapi/v4l/yuv-formats userspace-api/media/v4l/yuv-formats | |
| 717 | +media/v4l-drivers/au0828-cardlist admin-guide/media/au0828-cardlist | |
| 718 | +media/v4l-drivers/bttv admin-guide/media/bttv | |
| 719 | +media/v4l-drivers/bttv-cardlist admin-guide/media/bttv-cardlist | |
| 720 | +media/v4l-drivers/bttv-devel driver-api/media/drivers/bttv-devel | |
| 721 | +media/v4l-drivers/cafe_ccic admin-guide/media/cafe_ccic | |
| 722 | +media/v4l-drivers/cardlist admin-guide/media/cardlist | |
| 723 | +media/v4l-drivers/cx2341x driver-api/media/drivers/cx2341x-devel | |
| 724 | +media/v4l-drivers/cx2341x-devel driver-api/media/drivers/cx2341x-devel | |
| 725 | +media/v4l-drivers/cx2341x-uapi userspace-api/media/drivers/cx2341x-uapi | |
| 726 | +media/v4l-drivers/cx23885-cardlist admin-guide/media/cx23885-cardlist | |
| 727 | +media/v4l-drivers/cx88 admin-guide/media/cx88 | |
| 728 | +media/v4l-drivers/cx88-cardlist admin-guide/media/cx88-cardlist | |
| 729 | +media/v4l-drivers/cx88-devel driver-api/media/drivers/cx88-devel | |
| 730 | +media/v4l-drivers/em28xx-cardlist admin-guide/media/em28xx-cardlist | |
| 731 | +media/v4l-drivers/fimc admin-guide/media/fimc | |
| 732 | +media/v4l-drivers/fimc-devel driver-api/media/drivers/fimc-devel | |
| 733 | +media/v4l-drivers/fourcc userspace-api/media/v4l/fourcc | |
| 734 | +media/v4l-drivers/gspca-cardlist admin-guide/media/gspca-cardlist | |
| 735 | +media/v4l-drivers/imx admin-guide/media/imx | |
| 736 | +media/v4l-drivers/imx-uapi userspace-api/media/drivers/imx-uapi | |
| 737 | +media/v4l-drivers/imx7 admin-guide/media/imx7 | |
| 738 | +media/v4l-drivers/index userspace-api/media/drivers/index | |
| 739 | +media/v4l-drivers/ipu3 admin-guide/media/ipu3 | |
| 740 | +media/v4l-drivers/ivtv admin-guide/media/ivtv | |
| 741 | +media/v4l-drivers/ivtv-cardlist admin-guide/media/ivtv-cardlist | |
| 742 | +media/v4l-drivers/max2175 userspace-api/media/drivers/max2175 | |
| 743 | +media/v4l-drivers/omap3isp admin-guide/media/omap3isp | |
| 744 | +media/v4l-drivers/omap3isp-uapi userspace-api/media/drivers/omap3isp-uapi | |
| 745 | +media/v4l-drivers/philips admin-guide/media/philips | |
| 746 | +media/v4l-drivers/pvrusb2 driver-api/media/drivers/pvrusb2 | |
| 747 | +media/v4l-drivers/pxa_camera driver-api/media/drivers/pxa_camera | |
| 748 | +media/v4l-drivers/qcom_camss admin-guide/media/qcom_camss | |
| 749 | +media/v4l-drivers/radiotrack driver-api/media/drivers/radiotrack | |
| 750 | +media/v4l-drivers/rcar-fdp1 admin-guide/media/rcar-fdp1 | |
| 751 | +media/v4l-drivers/saa7134 admin-guide/media/saa7134 | |
| 752 | +media/v4l-drivers/saa7134-cardlist admin-guide/media/saa7134-cardlist | |
| 753 | +media/v4l-drivers/saa7134-devel driver-api/media/drivers/saa7134-devel | |
| 754 | +media/v4l-drivers/saa7164-cardlist admin-guide/media/saa7164-cardlist | |
| 755 | +media/v4l-drivers/sh_mobile_ceu_camera driver-api/media/drivers/sh_mobile_ceu_camera | |
| 756 | +media/v4l-drivers/si470x admin-guide/media/si470x | |
| 757 | +media/v4l-drivers/si4713 admin-guide/media/si4713 | |
| 758 | +media/v4l-drivers/si476x admin-guide/media/si476x | |
| 759 | +media/v4l-drivers/tuner-cardlist admin-guide/media/tuner-cardlist | |
| 760 | +media/v4l-drivers/tuners driver-api/media/drivers/tuners | |
| 761 | +media/v4l-drivers/uvcvideo userspace-api/media/drivers/uvcvideo | |
| 762 | +media/v4l-drivers/v4l-with-ir admin-guide/media/remote-controller | |
| 763 | +media/v4l-drivers/vimc admin-guide/media/vimc | |
| 764 | +media/v4l-drivers/vimc-devel driver-api/media/drivers/vimc-devel | |
| 765 | +media/v4l-drivers/vivid admin-guide/media/vivid | |
| 766 | +media/v4l-drivers/zoran driver-api/media/drivers/zoran | |
| 767 | +memory-devices/ti-emif driver-api/memory-devices/ti-emif | |
| 768 | +mips/booting arch/mips/booting | |
| 769 | +mips/features arch/mips/features | |
| 770 | +mips/index arch/mips/index | |
| 771 | +mips/ingenic-tcu arch/mips/ingenic-tcu | |
| 772 | +mm/slub admin-guide/mm/slab | |
| 773 | +mmc/index driver-api/mmc/index | |
| 774 | +mmc/mmc-async-req driver-api/mmc/mmc-async-req | |
| 775 | +mmc/mmc-dev-attrs driver-api/mmc/mmc-dev-attrs | |
| 776 | +mmc/mmc-dev-parts driver-api/mmc/mmc-dev-parts | |
| 777 | +mmc/mmc-tools driver-api/mmc/mmc-tools | |
| 778 | +mtd/index driver-api/mtd/index | |
| 779 | +mtd/intel-spi driver-api/mtd/spi-intel | |
| 780 | +mtd/nand_ecc driver-api/mtd/nand_ecc | |
| 781 | +mtd/spi-nor driver-api/mtd/spi-nor | |
| 782 | +namespaces/compatibility-list admin-guide/namespaces/compatibility-list | |
| 783 | +namespaces/index admin-guide/namespaces/index | |
| 784 | +namespaces/resource-control admin-guide/namespaces/resource-control | |
| 785 | +networking/altera_tse networking/device_drivers/ethernet/altera/altera_tse | |
| 786 | +networking/baycom networking/device_drivers/hamradio/baycom | |
| 787 | +networking/bpf_flow_dissector bpf/prog_flow_dissector | |
| 788 | +networking/cxacru networking/device_drivers/atm/cxacru | |
| 789 | +networking/defza networking/device_drivers/fddi/defza | |
| 790 | +networking/device_drivers/3com/3c509 networking/device_drivers/ethernet/3com/3c509 | |
| 791 | +networking/device_drivers/3com/vortex networking/device_drivers/ethernet/3com/vortex | |
| 792 | +networking/device_drivers/amazon/ena networking/device_drivers/ethernet/amazon/ena | |
| 793 | +networking/device_drivers/aquantia/atlantic networking/device_drivers/ethernet/aquantia/atlantic | |
| 794 | +networking/device_drivers/chelsio/cxgb networking/device_drivers/ethernet/chelsio/cxgb | |
| 795 | +networking/device_drivers/cirrus/cs89x0 networking/device_drivers/ethernet/cirrus/cs89x0 | |
| 796 | +networking/device_drivers/davicom/dm9000 networking/device_drivers/ethernet/davicom/dm9000 | |
| 797 | +networking/device_drivers/dec/dmfe networking/device_drivers/ethernet/dec/dmfe | |
| 798 | +networking/device_drivers/dlink/dl2k networking/device_drivers/ethernet/dlink/dl2k | |
| 799 | +networking/device_drivers/freescale/dpaa networking/device_drivers/ethernet/freescale/dpaa | |
| 800 | +networking/device_drivers/freescale/dpaa2/dpio-driver networking/device_drivers/ethernet/freescale/dpaa2/dpio-driver | |
| 801 | +networking/device_drivers/freescale/dpaa2/ethernet-driver networking/device_drivers/ethernet/freescale/dpaa2/ethernet-driver | |
| 802 | +networking/device_drivers/freescale/dpaa2/index networking/device_drivers/ethernet/freescale/dpaa2/index | |
| 803 | +networking/device_drivers/freescale/dpaa2/mac-phy-support networking/device_drivers/ethernet/freescale/dpaa2/mac-phy-support | |
| 804 | +networking/device_drivers/freescale/dpaa2/overview networking/device_drivers/ethernet/freescale/dpaa2/overview | |
| 805 | +networking/device_drivers/freescale/gianfar networking/device_drivers/ethernet/freescale/gianfar | |
| 806 | +networking/device_drivers/google/gve networking/device_drivers/ethernet/google/gve | |
| 807 | +networking/device_drivers/intel/e100 networking/device_drivers/ethernet/intel/e100 | |
| 808 | +networking/device_drivers/intel/e1000 networking/device_drivers/ethernet/intel/e1000 | |
| 809 | +networking/device_drivers/intel/e1000e networking/device_drivers/ethernet/intel/e1000e | |
| 810 | +networking/device_drivers/intel/fm10k networking/device_drivers/ethernet/intel/fm10k | |
| 811 | +networking/device_drivers/intel/i40e networking/device_drivers/ethernet/intel/i40e | |
| 812 | +networking/device_drivers/intel/iavf networking/device_drivers/ethernet/intel/iavf | |
| 813 | +networking/device_drivers/intel/ice networking/device_drivers/ethernet/intel/ice | |
| 814 | +networking/device_drivers/intel/igb networking/device_drivers/ethernet/intel/igb | |
| 815 | +networking/device_drivers/intel/igbvf networking/device_drivers/ethernet/intel/igbvf | |
| 816 | +networking/device_drivers/intel/ipw2100 networking/device_drivers/wifi/intel/ipw2100 | |
| 817 | +networking/device_drivers/intel/ipw2200 networking/device_drivers/wifi/intel/ipw2200 | |
| 818 | +networking/device_drivers/intel/ixgbe networking/device_drivers/ethernet/intel/ixgbe | |
| 819 | +networking/device_drivers/intel/ixgbevf networking/device_drivers/ethernet/intel/ixgbevf | |
| 820 | +networking/device_drivers/marvell/octeontx2 networking/device_drivers/ethernet/marvell/octeontx2 | |
| 821 | +networking/device_drivers/microsoft/netvsc networking/device_drivers/ethernet/microsoft/netvsc | |
| 822 | +networking/device_drivers/neterion/s2io networking/device_drivers/ethernet/neterion/s2io | |
| 823 | +networking/device_drivers/netronome/nfp networking/device_drivers/ethernet/netronome/nfp | |
| 824 | +networking/device_drivers/pensando/ionic networking/device_drivers/ethernet/pensando/ionic | |
| 825 | +networking/device_drivers/qualcomm/rmnet networking/device_drivers/cellular/qualcomm/rmnet | |
| 826 | +networking/device_drivers/smsc/smc9 networking/device_drivers/ethernet/smsc/smc9 | |
| 827 | +networking/device_drivers/stmicro/stmmac networking/device_drivers/ethernet/stmicro/stmmac | |
| 828 | +networking/device_drivers/ti/cpsw networking/device_drivers/ethernet/ti/cpsw | |
| 829 | +networking/device_drivers/ti/cpsw_switchdev networking/device_drivers/ethernet/ti/cpsw_switchdev | |
| 830 | +networking/device_drivers/ti/tlan networking/device_drivers/ethernet/ti/tlan | |
| 831 | +networking/devlink-trap networking/devlink/devlink-trap | |
| 832 | +networking/dpaa2/dpio-driver networking/device_drivers/ethernet/freescale/dpaa2/dpio-driver | |
| 833 | +networking/dpaa2/ethernet-driver networking/device_drivers/ethernet/freescale/dpaa2/ethernet-driver | |
| 834 | +networking/dpaa2/index networking/device_drivers/ethernet/freescale/dpaa2/index | |
| 835 | +networking/dpaa2/overview networking/device_drivers/ethernet/freescale/dpaa2/overview | |
| 836 | +networking/e100 networking/device_drivers/ethernet/intel/e100 | |
| 837 | +networking/e1000 networking/device_drivers/ethernet/intel/e1000 | |
| 838 | +networking/e1000e networking/device_drivers/ethernet/intel/e1000e | |
| 839 | +networking/fm10k networking/device_drivers/ethernet/intel/fm10k | |
| 840 | +networking/fore200e networking/device_drivers/atm/fore200e | |
| 841 | +networking/hinic networking/device_drivers/ethernet/huawei/hinic | |
| 842 | +networking/i40e networking/device_drivers/ethernet/intel/i40e | |
| 843 | +networking/iavf networking/device_drivers/ethernet/intel/iavf | |
| 844 | +networking/ice networking/device_drivers/ethernet/intel/ice | |
| 845 | +networking/igb networking/device_drivers/ethernet/intel/igb | |
| 846 | +networking/igbvf networking/device_drivers/ethernet/intel/igbvf | |
| 847 | +networking/iphase networking/device_drivers/atm/iphase | |
| 848 | +networking/ixgbe networking/device_drivers/ethernet/intel/ixgbe | |
| 849 | +networking/ixgbevf networking/device_drivers/ethernet/intel/ixgbevf | |
| 850 | +networking/netdev-FAQ process/maintainer-netdev | |
| 851 | +networking/skfp networking/device_drivers/fddi/skfp | |
| 852 | +networking/z8530drv networking/device_drivers/hamradio/z8530drv | |
| 853 | +nfc/index driver-api/nfc/index | |
| 854 | +nfc/nfc-hci driver-api/nfc/nfc-hci | |
| 855 | +nfc/nfc-pn544 driver-api/nfc/nfc-pn544 | |
| 856 | +nios2/features arch/nios2/features | |
| 857 | +nios2/index arch/nios2/index | |
| 858 | +nios2/nios2 arch/nios2/nios2 | |
| 859 | +nvdimm/btt driver-api/nvdimm/btt | |
| 860 | +nvdimm/index driver-api/nvdimm/index | |
| 861 | +nvdimm/nvdimm driver-api/nvdimm/nvdimm | |
| 862 | +nvdimm/security driver-api/nvdimm/security | |
| 863 | +nvmem/nvmem driver-api/nvmem | |
| 864 | +openrisc/features arch/openrisc/features | |
| 865 | +openrisc/index arch/openrisc/index | |
| 866 | +openrisc/openrisc_port arch/openrisc/openrisc_port | |
| 867 | +openrisc/todo arch/openrisc/todo | |
| 868 | +parisc/debugging arch/parisc/debugging | |
| 869 | +parisc/features arch/parisc/features | |
| 870 | +parisc/index arch/parisc/index | |
| 871 | +parisc/registers arch/parisc/registers | |
| 872 | +perf/arm-ccn admin-guide/perf/arm-ccn | |
| 873 | +perf/arm_dsu_pmu admin-guide/perf/arm_dsu_pmu | |
| 874 | +perf/hisi-pmu admin-guide/perf/hisi-pmu | |
| 875 | +perf/index admin-guide/perf/index | |
| 876 | +perf/qcom_l2_pmu admin-guide/perf/qcom_l2_pmu | |
| 877 | +perf/qcom_l3_pmu admin-guide/perf/qcom_l3_pmu | |
| 878 | +perf/thunderx2-pmu admin-guide/perf/thunderx2-pmu | |
| 879 | +perf/xgene-pmu admin-guide/perf/xgene-pmu | |
| 880 | +phy/samsung-usb2 driver-api/phy/samsung-usb2 | |
| 881 | +powerpc/associativity arch/powerpc/associativity | |
| 882 | +powerpc/booting arch/powerpc/booting | |
| 883 | +powerpc/bootwrapper arch/powerpc/bootwrapper | |
| 884 | +powerpc/cpu_families arch/powerpc/cpu_families | |
| 885 | +powerpc/cpu_features arch/powerpc/cpu_features | |
| 886 | +powerpc/dawr-power9 arch/powerpc/dawr-power9 | |
| 887 | +powerpc/dexcr arch/powerpc/dexcr | |
| 888 | +powerpc/dscr arch/powerpc/dscr | |
| 889 | +powerpc/eeh-pci-error-recovery arch/powerpc/eeh-pci-error-recovery | |
| 890 | +powerpc/elf_hwcaps arch/powerpc/elf_hwcaps | |
| 891 | +powerpc/elfnote arch/powerpc/elfnote | |
| 892 | +powerpc/features arch/powerpc/features | |
| 893 | +powerpc/firmware-assisted-dump arch/powerpc/firmware-assisted-dump | |
| 894 | +powerpc/hvcs arch/powerpc/hvcs | |
| 895 | +powerpc/imc arch/powerpc/imc | |
| 896 | +powerpc/index arch/powerpc/index | |
| 897 | +powerpc/isa-versions arch/powerpc/isa-versions | |
| 898 | +powerpc/kaslr-booke32 arch/powerpc/kaslr-booke32 | |
| 899 | +powerpc/mpc52xx arch/powerpc/mpc52xx | |
| 900 | +powerpc/papr_hcalls arch/powerpc/papr_hcalls | |
| 901 | +powerpc/pci_iov_resource_on_powernv arch/powerpc/pci_iov_resource_on_powernv | |
| 902 | +powerpc/pmu-ebb arch/powerpc/pmu-ebb | |
| 903 | +powerpc/ptrace arch/powerpc/ptrace | |
| 904 | +powerpc/qe_firmware arch/powerpc/qe_firmware | |
| 905 | +powerpc/syscall64-abi arch/powerpc/syscall64-abi | |
| 906 | +powerpc/transactional_memory arch/powerpc/transactional_memory | |
| 907 | +powerpc/ultravisor arch/powerpc/ultravisor | |
| 908 | +powerpc/vas-api arch/powerpc/vas-api | |
| 909 | +powerpc/vcpudispatch_stats arch/powerpc/vcpudispatch_stats | |
| 910 | +powerpc/vmemmap_dedup arch/powerpc/vmemmap_dedup | |
| 911 | +process/clang-format dev-tools/clang-format | |
| 912 | +process/magic-number staging/magic-number | |
| 913 | +process/unaligned-memory-access core-api/unaligned-memory-access | |
| 914 | +rapidio/index driver-api/rapidio/index | |
| 915 | +rapidio/mport_cdev driver-api/rapidio/mport_cdev | |
| 916 | +rapidio/rapidio driver-api/rapidio/rapidio | |
| 917 | +rapidio/rio_cm driver-api/rapidio/rio_cm | |
| 918 | +rapidio/sysfs driver-api/rapidio/sysfs | |
| 919 | +rapidio/tsi721 driver-api/rapidio/tsi721 | |
| 920 | +riscv/acpi arch/riscv/acpi | |
| 921 | +riscv/boot arch/riscv/boot | |
| 922 | +riscv/boot-image-header arch/riscv/boot-image-header | |
| 923 | +riscv/features arch/riscv/features | |
| 924 | +riscv/hwprobe arch/riscv/hwprobe | |
| 925 | +riscv/index arch/riscv/index | |
| 926 | +riscv/patch-acceptance arch/riscv/patch-acceptance | |
| 927 | +riscv/uabi arch/riscv/uabi | |
| 928 | +riscv/vector arch/riscv/vector | |
| 929 | +riscv/vm-layout arch/riscv/vm-layout | |
| 930 | +s390/3270 arch/s390/3270 | |
| 931 | +s390/cds arch/s390/cds | |
| 932 | +s390/common_io arch/s390/common_io | |
| 933 | +s390/driver-model arch/s390/driver-model | |
| 934 | +s390/features arch/s390/features | |
| 935 | +s390/index arch/s390/index | |
| 936 | +s390/monreader arch/s390/monreader | |
| 937 | +s390/pci arch/s390/pci | |
| 938 | +s390/qeth arch/s390/qeth | |
| 939 | +s390/s390dbf arch/s390/s390dbf | |
| 940 | +s390/text_files arch/s390/text_files | |
| 941 | +s390/vfio-ap arch/s390/vfio-ap | |
| 942 | +s390/vfio-ap-locking arch/s390/vfio-ap-locking | |
| 943 | +s390/vfio-ccw arch/s390/vfio-ccw | |
| 944 | +s390/zfcpdump arch/s390/zfcpdump | |
| 945 | +security/LSM security/lsm-development | |
| 946 | +security/LSM-sctp security/SCTP | |
| 947 | +serial/driver driver-api/serial/driver | |
| 948 | +serial/index driver-api/serial/index | |
| 949 | +serial/moxa-smartio driver-api/tty/moxa-smartio | |
| 950 | +serial/n_gsm driver-api/tty/n_gsm | |
| 951 | +serial/serial-iso7816 driver-api/serial/serial-iso7816 | |
| 952 | +serial/serial-rs485 driver-api/serial/serial-rs485 | |
| 953 | +serial/tty driver-api/tty/tty_ldisc | |
| 954 | +sh/booting arch/sh/booting | |
| 955 | +sh/features arch/sh/features | |
| 956 | +sh/index arch/sh/index | |
| 957 | +sh/new-machine arch/sh/new-machine | |
| 958 | +sh/register-banks arch/sh/register-banks | |
| 959 | +sparc/adi arch/sparc/adi | |
| 960 | +sparc/console arch/sparc/console | |
| 961 | +sparc/features arch/sparc/features | |
| 962 | +sparc/index arch/sparc/index | |
| 963 | +sparc/oradax/oracle-dax arch/sparc/oradax/oracle-dax | |
| 964 | +staging/kprobes trace/kprobes | |
| 965 | +sysctl/abi admin-guide/sysctl/abi | |
| 966 | +sysctl/fs admin-guide/sysctl/fs | |
| 967 | +sysctl/index admin-guide/sysctl/index | |
| 968 | +sysctl/kernel admin-guide/sysctl/kernel | |
| 969 | +sysctl/net admin-guide/sysctl/net | |
| 970 | +sysctl/sunrpc admin-guide/sysctl/sunrpc | |
| 971 | +sysctl/user admin-guide/sysctl/user | |
| 972 | +sysctl/vm admin-guide/sysctl/vm | |
| 973 | +thermal/cpu-cooling-api driver-api/thermal/cpu-cooling-api | |
| 974 | +thermal/exynos_thermal driver-api/thermal/exynos_thermal | |
| 975 | +thermal/exynos_thermal_emulation driver-api/thermal/exynos_thermal_emulation | |
| 976 | +thermal/index driver-api/thermal/index | |
| 977 | +thermal/intel_powerclamp admin-guide/thermal/intel_powerclamp | |
| 978 | +thermal/nouveau_thermal driver-api/thermal/nouveau_thermal | |
| 979 | +thermal/power_allocator driver-api/thermal/power_allocator | |
| 980 | +thermal/sysfs-api driver-api/thermal/sysfs-api | |
| 981 | +thermal/x86_pkg_temperature_thermal driver-api/thermal/x86_pkg_temperature_thermal | |
| 982 | +tpm/index security/tpm/index | |
| 983 | +tpm/tpm_vtpm_proxy security/tpm/tpm_vtpm_proxy | |
| 984 | +trace/coresight trace/coresight/coresight | |
| 985 | +trace/coresight-cpu-debug trace/coresight/coresight-cpu-debug | |
| 986 | +trace/rv/da_monitor_synthesis trace/rv/monitor_synthesis | |
| 987 | +translations/it_IT/admin-guide/security-bugs translations/it_IT/process/security-bugs | |
| 988 | +translations/it_IT/process/clang-format translations/it_IT/dev-tools/clang-format | |
| 989 | +translations/it_IT/process/magic-number translations/it_IT/staging/magic-number | |
| 990 | +translations/it_IT/riscv/patch-acceptance translations/it_IT/arch/riscv/patch-acceptance | |
| 991 | +translations/ja_JP/howto translations/ja_JP/process/howto | |
| 992 | +translations/ko_KR/howto translations/ko_KR/process/howto | |
| 993 | +translations/sp_SP/howto translations/sp_SP/process/howto | |
| 994 | +translations/sp_SP/submitting-patches translations/sp_SP/process/submitting-patches | |
| 995 | +translations/zh_CN/admin-guide/security-bugs translations/zh_CN/process/security-bugs | |
| 996 | +translations/zh_CN/arch translations/zh_CN/arch/index | |
| 997 | +translations/zh_CN/arm64/amu translations/zh_CN/arch/arm64/amu | |
| 998 | +translations/zh_CN/arm64/elf_hwcaps translations/zh_CN/arch/arm64/elf_hwcaps | |
| 999 | +translations/zh_CN/arm64/hugetlbpage translations/zh_CN/arch/arm64/hugetlbpage | |
| 1000 | +translations/zh_CN/arm64/index translations/zh_CN/arch/arm64/index | |
| 1001 | +translations/zh_CN/arm64/perf translations/zh_CN/arch/arm64/perf | |
| 1002 | +translations/zh_CN/coding-style translations/zh_CN/process/coding-style | |
| 1003 | +translations/zh_CN/loongarch/booting translations/zh_CN/arch/loongarch/booting | |
| 1004 | +translations/zh_CN/loongarch/features translations/zh_CN/arch/loongarch/features | |
| 1005 | +translations/zh_CN/loongarch/index translations/zh_CN/arch/loongarch/index | |
| 1006 | +translations/zh_CN/loongarch/introduction translations/zh_CN/arch/loongarch/introduction | |
| 1007 | +translations/zh_CN/loongarch/irq-chip-model translations/zh_CN/arch/loongarch/irq-chip-model | |
| 1008 | +translations/zh_CN/mips/booting translations/zh_CN/arch/mips/booting | |
| 1009 | +translations/zh_CN/mips/features translations/zh_CN/arch/mips/features | |
| 1010 | +translations/zh_CN/mips/index translations/zh_CN/arch/mips/index | |
| 1011 | +translations/zh_CN/mips/ingenic-tcu translations/zh_CN/arch/mips/ingenic-tcu | |
| 1012 | +translations/zh_CN/openrisc/index translations/zh_CN/arch/openrisc/index | |
| 1013 | +translations/zh_CN/openrisc/openrisc_port translations/zh_CN/arch/openrisc/openrisc_port | |
| 1014 | +translations/zh_CN/openrisc/todo translations/zh_CN/arch/openrisc/todo | |
| 1015 | +translations/zh_CN/parisc/debugging translations/zh_CN/arch/parisc/debugging | |
| 1016 | +translations/zh_CN/parisc/index translations/zh_CN/arch/parisc/index | |
| 1017 | +translations/zh_CN/parisc/registers translations/zh_CN/arch/parisc/registers | |
| 1018 | +translations/zh_CN/riscv/boot-image-header translations/zh_CN/arch/riscv/boot-image-header | |
| 1019 | +translations/zh_CN/riscv/index translations/zh_CN/arch/riscv/index | |
| 1020 | +translations/zh_CN/riscv/patch-acceptance translations/zh_CN/arch/riscv/patch-acceptance | |
| 1021 | +translations/zh_CN/riscv/vm-layout translations/zh_CN/arch/riscv/vm-layout | |
| 1022 | +translations/zh_CN/vm/active_mm translations/zh_CN/mm/active_mm | |
| 1023 | +translations/zh_CN/vm/balance translations/zh_CN/mm/balance | |
| 1024 | +translations/zh_CN/vm/damon/api translations/zh_CN/mm/damon/api | |
| 1025 | +translations/zh_CN/vm/damon/design translations/zh_CN/mm/damon/design | |
| 1026 | +translations/zh_CN/vm/damon/faq translations/zh_CN/mm/damon/faq | |
| 1027 | +translations/zh_CN/vm/damon/index translations/zh_CN/mm/damon/index | |
| 1028 | +translations/zh_CN/vm/free_page_reporting translations/zh_CN/mm/free_page_reporting | |
| 1029 | +translations/zh_CN/vm/highmem translations/zh_CN/mm/highmem | |
| 1030 | +translations/zh_CN/vm/hmm translations/zh_CN/mm/hmm | |
| 1031 | +translations/zh_CN/vm/hugetlbfs_reserv translations/zh_CN/mm/hugetlbfs_reserv | |
| 1032 | +translations/zh_CN/vm/hwpoison translations/zh_CN/mm/hwpoison | |
| 1033 | +translations/zh_CN/vm/index translations/zh_CN/mm/index | |
| 1034 | +translations/zh_CN/vm/ksm translations/zh_CN/mm/ksm | |
| 1035 | +translations/zh_CN/vm/memory-model translations/zh_CN/mm/memory-model | |
| 1036 | +translations/zh_CN/vm/mmu_notifier translations/zh_CN/mm/mmu_notifier | |
| 1037 | +translations/zh_CN/vm/numa translations/zh_CN/mm/numa | |
| 1038 | +translations/zh_CN/vm/overcommit-accounting translations/zh_CN/mm/overcommit-accounting | |
| 1039 | +translations/zh_CN/vm/page_frags translations/zh_CN/mm/page_frags | |
| 1040 | +translations/zh_CN/vm/page_owner translations/zh_CN/mm/page_owner | |
| 1041 | +translations/zh_CN/vm/page_table_check translations/zh_CN/mm/page_table_check | |
| 1042 | +translations/zh_CN/vm/remap_file_pages translations/zh_CN/mm/remap_file_pages | |
| 1043 | +translations/zh_CN/vm/split_page_table_lock translations/zh_CN/mm/split_page_table_lock | |
| 1044 | +translations/zh_CN/vm/zsmalloc translations/zh_CN/mm/zsmalloc | |
| 1045 | +translations/zh_TW/arm64/amu translations/zh_TW/arch/arm64/amu | |
| 1046 | +translations/zh_TW/arm64/elf_hwcaps translations/zh_TW/arch/arm64/elf_hwcaps | |
| 1047 | +translations/zh_TW/arm64/hugetlbpage translations/zh_TW/arch/arm64/hugetlbpage | |
| 1048 | +translations/zh_TW/arm64/index translations/zh_TW/arch/arm64/index | |
| 1049 | +translations/zh_TW/arm64/perf translations/zh_TW/arch/arm64/perf | |
| 1050 | +tty/device_drivers/oxsemi-tornado misc-devices/oxsemi-tornado | |
| 1051 | +tty/index driver-api/tty/index | |
| 1052 | +tty/n_tty driver-api/tty/n_tty | |
| 1053 | +tty/tty_buffer driver-api/tty/tty_buffer | |
| 1054 | +tty/tty_driver driver-api/tty/tty_driver | |
| 1055 | +tty/tty_internals driver-api/tty/tty_internals | |
| 1056 | +tty/tty_ldisc driver-api/tty/tty_ldisc | |
| 1057 | +tty/tty_port driver-api/tty/tty_port | |
| 1058 | +tty/tty_struct driver-api/tty/tty_struct | |
| 1059 | +usb/typec driver-api/usb/typec | |
| 1060 | +usb/usb3-debug-port driver-api/usb/usb3-debug-port | |
| 1061 | +userspace-api/media/drivers/st-vgxy61 userspace-api/media/drivers/vgxy61 | |
| 1062 | +userspace-api/media/v4l/pixfmt-meta-d4xx userspace-api/media/v4l/metafmt-d4xx | |
| 1063 | +userspace-api/media/v4l/pixfmt-meta-intel-ipu3 userspace-api/media/v4l/metafmt-intel-ipu3 | |
| 1064 | +userspace-api/media/v4l/pixfmt-meta-rkisp1 userspace-api/media/v4l/metafmt-rkisp1 | |
| 1065 | +userspace-api/media/v4l/pixfmt-meta-uvc userspace-api/media/v4l/metafmt-uvc | |
| 1066 | +userspace-api/media/v4l/pixfmt-meta-vivid userspace-api/media/v4l/metafmt-vivid | |
| 1067 | +userspace-api/media/v4l/pixfmt-meta-vsp1-hgo userspace-api/media/v4l/metafmt-vsp1-hgo | |
| 1068 | +userspace-api/media/v4l/pixfmt-meta-vsp1-hgt userspace-api/media/v4l/metafmt-vsp1-hgt | |
| 1069 | +virt/coco/sevguest virt/coco/sev-guest | |
| 1070 | +virt/kvm/amd-memory-encryption virt/kvm/x86/amd-memory-encryption | |
| 1071 | +virt/kvm/arm/psci virt/kvm/arm/fw-pseudo-registers | |
| 1072 | +virt/kvm/cpuid virt/kvm/x86/cpuid | |
| 1073 | +virt/kvm/hypercalls virt/kvm/x86/hypercalls | |
| 1074 | +virt/kvm/mmu virt/kvm/x86/mmu | |
| 1075 | +virt/kvm/msr virt/kvm/x86/msr | |
| 1076 | +virt/kvm/nested-vmx virt/kvm/x86/nested-vmx | |
| 1077 | +virt/kvm/running-nested-guests virt/kvm/x86/running-nested-guests | |
| 1078 | +virt/kvm/s390-diag virt/kvm/s390/s390-diag | |
| 1079 | +virt/kvm/s390-pv virt/kvm/s390/s390-pv | |
| 1080 | +virt/kvm/s390-pv-boot virt/kvm/s390/s390-pv-boot | |
| 1081 | +virt/kvm/timekeeping virt/kvm/x86/timekeeping | |
| 1082 | +virt/kvm/x86/halt-polling virt/kvm/halt-polling | |
| 1083 | +virtual/index virt/index | |
| 1084 | +virtual/kvm/amd-memory-encryption virt/kvm/x86/amd-memory-encryption | |
| 1085 | +virtual/kvm/cpuid virt/kvm/x86/cpuid | |
| 1086 | +virtual/kvm/index virt/kvm/index | |
| 1087 | +virtual/kvm/vcpu-requests virt/kvm/vcpu-requests | |
| 1088 | +virtual/paravirt_ops virt/paravirt_ops | |
| 1089 | +vm/active_mm mm/active_mm | |
| 1090 | +vm/arch_pgtable_helpers mm/arch_pgtable_helpers | |
| 1091 | +vm/balance mm/balance | |
| 1092 | +vm/bootmem mm/bootmem | |
| 1093 | +vm/damon/api mm/damon/api | |
| 1094 | +vm/damon/design mm/damon/design | |
| 1095 | +vm/damon/faq mm/damon/faq | |
| 1096 | +vm/damon/index mm/damon/index | |
| 1097 | +vm/free_page_reporting mm/free_page_reporting | |
| 1098 | +vm/highmem mm/highmem | |
| 1099 | +vm/hmm mm/hmm | |
| 1100 | +vm/hugetlbfs_reserv mm/hugetlbfs_reserv | |
| 1101 | +vm/hugetlbpage admin-guide/mm/hugetlbpage | |
| 1102 | +vm/hwpoison mm/hwpoison | |
| 1103 | +vm/idle_page_tracking admin-guide/mm/idle_page_tracking | |
| 1104 | +vm/index mm/index | |
| 1105 | +vm/ksm mm/ksm | |
| 1106 | +vm/memory-model mm/memory-model | |
| 1107 | +vm/mmu_notifier mm/mmu_notifier | |
| 1108 | +vm/numa mm/numa | |
| 1109 | +vm/numa_memory_policy admin-guide/mm/numa_memory_policy | |
| 1110 | +vm/oom mm/oom | |
| 1111 | +vm/overcommit-accounting mm/overcommit-accounting | |
| 1112 | +vm/page_allocation mm/page_allocation | |
| 1113 | +vm/page_cache mm/page_cache | |
| 1114 | +vm/page_frags mm/page_frags | |
| 1115 | +vm/page_migration mm/page_migration | |
| 1116 | +vm/page_owner mm/page_owner | |
| 1117 | +vm/page_reclaim mm/page_reclaim | |
| 1118 | +vm/page_table_check mm/page_table_check | |
| 1119 | +vm/page_tables mm/page_tables | |
| 1120 | +vm/pagemap admin-guide/mm/pagemap | |
| 1121 | +vm/physical_memory mm/physical_memory | |
| 1122 | +vm/process_addrs mm/process_addrs | |
| 1123 | +vm/remap_file_pages mm/remap_file_pages | |
| 1124 | +vm/shmfs mm/shmfs | |
| 1125 | +vm/slab mm/slab | |
| 1126 | +vm/slub admin-guide/mm/slab | |
| 1127 | +vm/soft-dirty admin-guide/mm/soft-dirty | |
| 1128 | +vm/split_page_table_lock mm/split_page_table_lock | |
| 1129 | +vm/swap mm/swap | |
| 1130 | +vm/swap_numa admin-guide/mm/swap_numa | |
| 1131 | +vm/transhuge mm/transhuge | |
| 1132 | +vm/unevictable-lru mm/unevictable-lru | |
| 1133 | +vm/userfaultfd admin-guide/mm/userfaultfd | |
| 1134 | +vm/vmalloc mm/vmalloc | |
| 1135 | +vm/vmalloced-kernel-stacks mm/vmalloced-kernel-stacks | |
| 1136 | +vm/vmemmap_dedup mm/vmemmap_dedup | |
| 1137 | +vm/zsmalloc mm/zsmalloc | |
| 1138 | +vm/zswap admin-guide/mm/zswap | |
| 1139 | +watch_queue core-api/watch_queue | |
| 1140 | +x86/amd-memory-encryption arch/x86/amd-memory-encryption | |
| 1141 | +x86/amd_hsmp arch/x86/amd_hsmp | |
| 1142 | +x86/boot arch/x86/boot | |
| 1143 | +x86/booting-dt arch/x86/booting-dt | |
| 1144 | +x86/buslock arch/x86/buslock | |
| 1145 | +x86/cpuinfo arch/x86/cpuinfo | |
| 1146 | +x86/earlyprintk arch/x86/earlyprintk | |
| 1147 | +x86/elf_auxvec arch/x86/elf_auxvec | |
| 1148 | +x86/entry_64 arch/x86/entry_64 | |
| 1149 | +x86/exception-tables arch/x86/exception-tables | |
| 1150 | +x86/features arch/x86/features | |
| 1151 | +x86/i386/IO-APIC arch/x86/i386/IO-APIC | |
| 1152 | +x86/i386/index arch/x86/i386/index | |
| 1153 | +x86/ifs arch/x86/ifs | |
| 1154 | +x86/index arch/x86/index | |
| 1155 | +x86/intel-hfi arch/x86/intel-hfi | |
| 1156 | +x86/intel_txt arch/x86/intel_txt | |
| 1157 | +x86/iommu arch/x86/iommu | |
| 1158 | +x86/kernel-stacks arch/x86/kernel-stacks | |
| 1159 | +x86/mds arch/x86/mds | |
| 1160 | +x86/microcode arch/x86/microcode | |
| 1161 | +x86/mtrr arch/x86/mtrr | |
| 1162 | +x86/orc-unwinder arch/x86/orc-unwinder | |
| 1163 | +x86/pat arch/x86/pat | |
| 1164 | +x86/protection-keys core-api/protection-keys | |
| 1165 | +x86/pti arch/x86/pti | |
| 1166 | +x86/resctrl filesystems/resctrl | |
| 1167 | +x86/resctrl_ui filesystems/resctrl | |
| 1168 | +x86/sgx arch/x86/sgx | |
| 1169 | +x86/sva arch/x86/sva | |
| 1170 | +x86/tdx arch/x86/tdx | |
| 1171 | +x86/tlb arch/x86/tlb | |
| 1172 | +x86/topology arch/x86/topology | |
| 1173 | +x86/tsx_async_abort arch/x86/tsx_async_abort | |
| 1174 | +x86/usb-legacy-support arch/x86/usb-legacy-support | |
| 1175 | +x86/x86_64/5level-paging arch/x86/x86_64/5level-paging | |
| 1176 | +x86/x86_64/cpu-hotplug-spec arch/x86/x86_64/cpu-hotplug-spec | |
| 1177 | +x86/x86_64/fake-numa-for-cpusets arch/x86/x86_64/fake-numa-for-cpusets | |
| 1178 | +x86/x86_64/fsgs arch/x86/x86_64/fsgs | |
| 1179 | +x86/x86_64/index arch/x86/x86_64/index | |
| 1180 | +x86/x86_64/machinecheck arch/x86/x86_64/machinecheck | |
| 1181 | +x86/x86_64/mm arch/x86/x86_64/mm | |
| 1182 | +x86/x86_64/uefi arch/x86/x86_64/uefi | |
| 1183 | +x86/xstate arch/x86/xstate | |
| 1184 | +x86/zero-page arch/x86/zero-page | |
| 1185 | +xilinx/eemi driver-api/xilinx/eemi | |
| 1186 | +xilinx/index driver-api/xilinx/index | |
| 1187 | +xtensa/atomctl arch/xtensa/atomctl | |
| 1188 | +xtensa/booting arch/xtensa/booting | |
| 1189 | +xtensa/features arch/xtensa/features | |
| 1190 | +xtensa/index arch/xtensa/index | |
| 1191 | +xtensa/mmu arch/xtensa/mmu | |
| @@ -60,8 +60,8 @@ ifeq ($(HAVE_LATEXMK),1) · 59 unmodified lines | ||
| 60 | 60 | endif #HAVE_LATEXMK |
| 61 | 61 | |
| 62 | 62 | # Internal variables. |
| 63 | −PAPEROPT_a4 = -D latex_paper_size=a4 | |
| 64 | −PAPEROPT_letter = -D latex_paper_size=letter | |
| 63 | +PAPEROPT_a4 = -D latex_elements.papersize=a4paper | |
| 64 | +PAPEROPT_letter = -D latex_elements.papersize=letterpaper | |
| 65 | 65 | ALLSPHINXOPTS = -D kerneldoc_srctree=$(srctree) -D kerneldoc_bin=$(KERNELDOC) |
| 66 | 66 | ALLSPHINXOPTS += $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) |
| 67 | 67 | ifneq ($(wildcard $(srctree)/.config),) |
| @@ -87,7 +87,7 @@ loop_cmd = $(echo-cmd) $(cmd_$(1)) || exit; · 19 unmodified lines | ||
| 87 | 87 | PYTHONPYCACHEPREFIX ?= $(abspath $(BUILDDIR)/__pycache__) |
| 88 | 88 | |
| 89 | 89 | quiet_cmd_sphinx = SPHINX $@ --> file://$(abspath $(BUILDDIR)/$3/$4) |
| 90 | − cmd_sphinx = $(MAKE) BUILDDIR=$(abspath $(BUILDDIR)) $(build)=Documentation/userspace-api/media $2 && \ | |
| 90 | + cmd_sphinx = \ | |
| 91 | 91 | PYTHONPYCACHEPREFIX="$(PYTHONPYCACHEPREFIX)" \ |
| 92 | 92 | BUILDDIR=$(abspath $(BUILDDIR)) SPHINX_CONF=$(abspath $(src)/$5/$(SPHINX_CONF)) \ |
| 93 | 93 | $(PYTHON3) $(srctree)/scripts/jobserver-exec \ |
| @@ -108,6 +108,9 @@ htmldocs: · 14 unmodified lines | ||
| 108 | 108 | @$(srctree)/scripts/sphinx-pre-install --version-check |
| 109 | 109 | @+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,html,$(var),,$(var))) |
| 110 | 110 | |
| 111 | +htmldocs-redirects: $(srctree)/Documentation/.renames.txt | |
| 112 | + @tools/docs/gen-redirects.py --output $(BUILDDIR) < $< | |
| 113 | + | |
| 111 | 114 | # If Rust support is available and .config exists, add rustdoc generated contents. |
| 112 | 115 | # If there are any, the errors from this make rustdoc will be displayed but |
| 113 | 116 | # won't stop the execution of htmldocs |
| @@ -171,11 +174,11 @@ refcheckdocs: · 57 unmodified lines | ||
| 171 | 174 | |
| 172 | 175 | cleandocs: |
| 173 | 176 | $(Q)rm -rf $(BUILDDIR) |
| 174 | − $(Q)$(MAKE) BUILDDIR=$(abspath $(BUILDDIR)) $(build)=Documentation/userspace-api/media clean | |
| 175 | 177 | |
| 176 | 178 | dochelp: |
| 177 | 179 | @echo ' Linux kernel internal documentation in different formats from ReST:' |
| 178 | 180 | @echo ' htmldocs - HTML' |
| 181 | + @echo ' htmldocs-redirects - generate HTML redirects for moved pages' | |
| 179 | 182 | @echo ' texinfodocs - Texinfo' |
| 180 | 183 | @echo ' infodocs - Info' |
| 181 | 184 | @echo ' latexdocs - LaTeX' |
| @@ -86,7 +86,7 @@ The <EPF Device> directory can have a list of symbolic links · 85 unmodified lines | ||
| 86 | 86 | be created by the user to represent the virtual functions that are bound to |
| 87 | 87 | the physical function. In the above directory structure <EPF Device 11> is a |
| 88 | 88 | physical function and <EPF Device 31> is a virtual function. An EPF device once |
| 89 | −it's linked to another EPF device, cannot be linked to a EPC device. | |
| 89 | +it's linked to another EPF device, cannot be linked to an EPC device. | |
| 90 | 90 | |
| 91 | 91 | EPC Device |
| 92 | 92 | ========== |
| @@ -108,7 +108,7 @@ entries corresponding to EPC device will be created by the EPC core. · 15 unmodified lines | ||
| 108 | 108 | The <EPC Device> directory will have a list of symbolic links to |
| 109 | 109 | <EPF Device>. These symbolic links should be created by the user to |
| 110 | 110 | represent the functions present in the endpoint device. Only <EPF Device> |
| 111 | −that represents a physical function can be linked to a EPC device. | |
| 111 | +that represents a physical function can be linked to an EPC device. | |
| 112 | 112 | |
| 113 | 113 | The <EPC Device> directory will also have a *start* field. Once |
| 114 | 114 | "1" is written to this field, the endpoint device will be ready to |
| @@ -197,8 +197,8 @@ by the PCI endpoint function driver. · 196 unmodified lines | ||
| 197 | 197 | * pci_epf_register_driver() |
| 198 | 198 | |
| 199 | 199 | The PCI Endpoint Function driver should implement the following ops: |
| 200 | − * bind: ops to perform when a EPC device has been bound to EPF device | |
| 201 | − * unbind: ops to perform when a binding has been lost between a EPC | |
| 200 | + * bind: ops to perform when an EPC device has been bound to EPF device | |
| 201 | + * unbind: ops to perform when a binding has been lost between an EPC | |
| 202 | 202 | device and EPF device |
| 203 | 203 | * add_cfs: optional ops to create function specific configfs |
| 204 | 204 | attributes |
| @@ -251,7 +251,7 @@ pci-ep-cfs.c can be used as reference for using these APIs. · 46 unmodified lines | ||
| 251 | 251 | * pci_epf_bind() |
| 252 | 252 | |
| 253 | 253 | pci_epf_bind() should be invoked when the EPF device has been bound to |
| 254 | − a EPC device. | |
| 254 | + an EPC device. | |
| 255 | 255 | |
| 256 | 256 | * pci_epf_unbind() |
| 257 | 257 | |
| @@ -106,7 +106,7 @@ or the RCU-protected data that it points to can change concurrently. · 105 unmodified lines | ||
| 106 | 106 | Like rcu_dereference(), when lockdep is enabled, RCU list and hlist |
| 107 | 107 | traversal primitives check for being called from within an RCU read-side |
| 108 | 108 | critical section. However, a lockdep expression can be passed to them |
| 109 | −as a additional optional argument. With this lockdep expression, these | |
| 109 | +as an additional optional argument. With this lockdep expression, these | |
| 110 | 110 | traversal primitives will complain only if the lockdep expression is |
| 111 | 111 | false and they are called from outside any RCU read-side critical section. |
| 112 | 112 | |
| @@ -119,7 +119,7 @@ warnings: · 118 unmodified lines | ||
| 119 | 119 | uncommon in large datacenter. In one memorable case some decades |
| 120 | 120 | back, a CPU failed in a running system, becoming unresponsive, |
| 121 | 121 | but not causing an immediate crash. This resulted in a series |
| 122 | − of RCU CPU stall warnings, eventually leading the realization | |
| 122 | + of RCU CPU stall warnings, eventually leading to the realization | |
| 123 | 123 | that the CPU had failed. |
| 124 | 124 | |
| 125 | 125 | The RCU, RCU-sched, RCU-tasks, and RCU-tasks-trace implementations have |
| @@ -41,7 +41,7 @@ namespace). The higher level goal is to allow for uid-based sandboxing of system · 40 unmodified lines | ||
| 41 | 41 | services without having to give out CAP_SETUID all over the place just so that |
| 42 | 42 | non-root programs can drop to even-lesser-privileged uids. This is especially |
| 43 | 43 | relevant when one non-root daemon on the system should be allowed to spawn other |
| 44 | −processes as different uids, but its undesirable to give the daemon a | |
| 44 | +processes as different uids, but it's undesirable to give the daemon a | |
| 45 | 45 | basically-root-equivalent CAP_SETUID. |
| 46 | 46 | |
| 47 | 47 | |
| @@ -253,7 +253,7 @@ interface. · 252 unmodified lines | ||
| 253 | 253 | Some architectures have ECC detectors for L1, L2 and L3 caches, |
| 254 | 254 | along with DMA engines, fabric switches, main data path switches, |
| 255 | 255 | interconnections, and various other hardware data paths. If the hardware |
| 256 | −reports it, then a edac_device device probably can be constructed to | |
| 256 | +reports it, then an edac_device device probably can be constructed to | |
| 257 | 257 | harvest and present that to userspace. |
| 258 | 258 | |
| 259 | 259 | |
| @@ -2,7 +2,7 @@ · 1 unmodified line | ||
| 2 | 2 | # They may be installed along the following lines. Check the section |
| 3 | 3 | # 8 udev manpage to see whether your udev supports SUBSYSTEM, and |
| 4 | 4 | # whether it uses one or two equal signs for SUBSYSTEM and KERNEL. |
| 5 | −# | |
| 5 | +# | |
| 6 | 6 | # ecashin@makki ~$ su |
| 7 | 7 | # Password: |
| 8 | 8 | # bash# find /etc -type f -name udev.conf |
| @@ -13,7 +13,7 @@ · 4 unmodified lines | ||
| 13 | 13 | # 10-wacom.rules 50-udev.rules |
| 14 | 14 | # bash# cp /path/to/linux/Documentation/admin-guide/aoe/udev.txt \ |
| 15 | 15 | # /etc/udev/rules.d/60-aoe.rules |
| 16 | −# | |
| 16 | +# | |
| 17 | 17 | |
| 18 | 18 | # aoe char devices |
| 19 | 19 | SUBSYSTEM=="aoe", KERNEL=="discover", NAME="etherd/%k", GROUP="disk", MODE="0220" |
| @@ -22,5 +22,5 @@ SUBSYSTEM=="aoe", KERNEL=="interfaces", NAME="etherd/%k", GROUP="disk", MODE="02 · 2 unmodified lines | ||
| 22 | 22 | SUBSYSTEM=="aoe", KERNEL=="revalidate", NAME="etherd/%k", GROUP="disk", MODE="0220" |
| 23 | 23 | SUBSYSTEM=="aoe", KERNEL=="flush", NAME="etherd/%k", GROUP="disk", MODE="0220" |
| 24 | 24 | |
| 25 | −# aoe block devices | |
| 25 | +# aoe block devices | |
| 26 | 26 | KERNEL=="etherd*", GROUP="disk" |
| @@ -118,7 +118,7 @@ and high-level drivers that you would use: · 117 unmodified lines | ||
| 118 | 118 | ================ ============ ======== |
| 119 | 119 | |
| 120 | 120 | All parports and all protocol drivers are probed automatically unless probe=0 |
| 121 | −parameter is used. So just "modprobe epat" is enough for a Imation SuperDisk | |
| 121 | +parameter is used. So just "modprobe epat" is enough for an Imation SuperDisk | |
| 122 | 122 | drive to work. |
| 123 | 123 | |
| 124 | 124 | Manual device creation:: |
| @@ -600,7 +600,7 @@ lock and return itself to the pool. · 599 unmodified lines | ||
| 600 | 600 | All storage within vdo is managed as 4KB blocks, but it can accept writes |
| 601 | 601 | as small as 512 bytes. Processing a write that is smaller than 4K requires |
| 602 | 602 | a read-modify-write operation that reads the relevant 4K block, copies the |
| 603 | −new data over the approriate sectors of the block, and then launches a | |
| 603 | +new data over the appropriate sectors of the block, and then launches a | |
| 604 | 604 | write operation for the modified data block. The read and write stages of |
| 605 | 605 | this operation are nearly identical to the normal read and write |
| 606 | 606 | operations, and a single data_vio is used throughout this operation. |
| @@ -398,7 +398,7 @@ There are 3 different data modes: · 397 unmodified lines | ||
| 398 | 398 | * writeback mode |
| 399 | 399 | |
| 400 | 400 | In data=writeback mode, ext4 does not journal data at all. This mode provides |
| 401 | − a similar level of journaling as that of XFS, JFS, and ReiserFS in its default | |
| 401 | + a similar level of journaling as that of XFS and JFS in its default | |
| 402 | 402 | mode - metadata journaling. A crash+recovery can cause incorrect data to |
| 403 | 403 | appear in files which were written shortly before the crash. This mode will |
| 404 | 404 | typically provide the best ext4 performance. |
| @@ -214,7 +214,7 @@ XEON PHI specific considerations · 213 unmodified lines | ||
| 214 | 214 | command line with the 'ring3mwait=disable' command line option. |
| 215 | 215 | |
| 216 | 216 | XEON PHI is not affected by the other MDS variants and MSBDS is mitigated |
| 217 | − before the CPU enters a idle state. As XEON PHI is not affected by L1TF | |
| 217 | + before the CPU enters an idle state. As XEON PHI is not affected by L1TF | |
| 218 | 218 | either disabling SMT is not required for full protection. |
| 219 | 219 | |
| 220 | 220 | .. _mds_smt_control: |
| @@ -664,7 +664,7 @@ Intel white papers: · 663 unmodified lines | ||
| 664 | 664 | |
| 665 | 665 | .. _spec_ref1: |
| 666 | 666 | |
| 667 | −[1] `Intel analysis of speculative execution side channels <https://newsroom.intel.com/wp-content/uploads/sites/11/2018/01/Intel-Analysis-of-Speculative-Execution-Side-Channels.pdf>`_. | |
| 667 | +[1] `Intel analysis of speculative execution side channels <https://www.intel.com/content/dam/www/public/us/en/documents/white-papers/analysis-of-speculative-execution-side-channels-white-paper.pdf>`_. | |
| 668 | 668 | |
| 669 | 669 | .. _spec_ref2: |
| 670 | 670 | |
| @@ -682,7 +682,7 @@ AMD white papers: · 11 unmodified lines | ||
| 682 | 682 | |
| 683 | 683 | .. _spec_ref5: |
| 684 | 684 | |
| 685 | −[5] `AMD64 technology indirect branch control extension <https://developer.amd.com/wp-content/resources/Architecture_Guidelines_Update_Indirect_Branch_Control.pdf>`_. | |
| 685 | +[5] `AMD64 technology indirect branch control extension <https://www.amd.com/content/dam/amd/en/documents/processor-tech-docs/white-papers/111006-architecture-guidelines-update-amd64-technology-indirect-branch-control-extension.pdf>`_. | |
| 686 | 686 | |
| 687 | 687 | .. _spec_ref6: |
| 688 | 688 | |
| @@ -708,7 +708,7 @@ MIPS white paper: · 19 unmodified lines | ||
| 708 | 708 | |
| 709 | 709 | .. _spec_ref10: |
| 710 | 710 | |
| 711 | −[10] `MIPS: response on speculative execution and side channel vulnerabilities <https://www.mips.com/blog/mips-response-on-speculative-execution-and-side-channel-vulnerabilities/>`_. | |
| 711 | +[10] `MIPS: response on speculative execution and side channel vulnerabilities <https://web.archive.org/web/20220512003005if_/https://www.mips.com/blog/mips-response-on-speculative-execution-and-side-channel-vulnerabilities/>`_. | |
| 712 | 712 | |
| 713 | 713 | Academic papers: |
| 714 | 714 | |
| @@ -471,7 +471,7 @@ Notes on loading the dump-capture kernel: · 470 unmodified lines | ||
| 471 | 471 | performance degradation. To enable multi-cpu support, you should bring up an |
| 472 | 472 | SMP dump-capture kernel and specify maxcpus/nr_cpus options while loading it. |
| 473 | 473 | |
| 474 | −* For s390x there are two kdump modes: If a ELF header is specified with | |
| 474 | +* For s390x there are two kdump modes: If an ELF header is specified with | |
| 475 | 475 | the elfcorehdr= kernel parameter, it is used by the kdump kernel as it |
| 476 | 476 | is done on all other architectures. If no elfcorehdr= kernel parameter is |
| 477 | 477 | specified, the s390x kdump kernel dynamically creates the header. The |
| @@ -1,3 +1,5 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 1 | 3 | .. _kernelparameters: |
| 2 | 4 | |
| 3 | 5 | The kernel's command-line parameters |
| @@ -213,7 +215,7 @@ need or coordination with <Documentation/arch/x86/boot.rst>. · 209 unmodified lines | ||
| 213 | 215 | There are also arch-specific kernel-parameters not documented here. |
| 214 | 216 | |
| 215 | 217 | Note that ALL kernel parameters listed below are CASE SENSITIVE, and that |
| 216 | −a trailing = on the name of any parameter states that that parameter will | |
| 218 | +a trailing = on the name of any parameter states that the parameter will | |
| 217 | 219 | be entered as an environment variable, whereas its absence indicates that |
| 218 | 220 | it will appear as a kernel argument readable via /proc/cmdline by programs |
| 219 | 221 | running once the system is up. |
| @@ -3705,7 +3705,7 @@ · 3704 unmodified lines | ||
| 3705 | 3705 | looking for corruption. Enabling this will |
| 3706 | 3706 | both detect corruption and prevent the kernel |
| 3707 | 3707 | from using the memory being corrupted. |
| 3708 | − However, its intended as a diagnostic tool; if | |
| 3708 | + However, it's intended as a diagnostic tool; if | |
| 3709 | 3709 | repeatable BIOS-originated corruption always |
| 3710 | 3710 | affects the same memory, you can use memmap= |
| 3711 | 3711 | to prevent the kernel from using that memory. |
| @@ -7400,7 +7400,7 @@ · 3688 unmodified lines | ||
| 7400 | 7400 | (converted into nanoseconds). Fast, but |
| 7401 | 7401 | depending on the architecture, may not be |
| 7402 | 7402 | in sync between CPUs. |
| 7403 | − global - Event time stamps are synchronize across | |
| 7403 | + global - Event time stamps are synchronized across | |
| 7404 | 7404 | CPUs. May be slower than the local clock, |
| 7405 | 7405 | but better for some race conditions. |
| 7406 | 7406 | counter - Simple counting of events (1, 2, ..) |
| @@ -7520,12 +7520,12 @@ · 113 unmodified lines | ||
| 7520 | 7520 | section. |
| 7521 | 7521 | |
| 7522 | 7522 | trace_trigger=[trigger-list] |
| 7523 | − [FTRACE] Add a event trigger on specific events. | |
| 7523 | + [FTRACE] Add an event trigger on specific events. | |
| 7524 | 7524 | Set a trigger on top of a specific event, with an optional |
| 7525 | 7525 | filter. |
| 7526 | 7526 | |
| 7527 | − The format is is "trace_trigger=<event>.<trigger>[ if <filter>],..." | |
| 7528 | − Where more than one trigger may be specified that are comma deliminated. | |
| 7527 | + The format is "trace_trigger=<event>.<trigger>[ if <filter>],..." | |
| 7528 | + Where more than one trigger may be specified that are comma delimited. | |
| 7529 | 7529 | |
| 7530 | 7530 | For example: |
| 7531 | 7531 | |
| @@ -7533,7 +7533,7 @@ · 1 unmodified line | ||
| 7533 | 7533 | |
| 7534 | 7534 | The above will enable the "stacktrace" trigger on the "sched_switch" |
| 7535 | 7535 | event but only trigger it if the "prev_state" of the "sched_switch" |
| 7536 | − event is "2" (TASK_UNINTERUPTIBLE). | |
| 7536 | + event is "2" (TASK_UNINTERRUPTIBLE). | |
| 7537 | 7537 | |
| 7538 | 7538 | See also "Event triggers" in Documentation/trace/events.rst |
| 7539 | 7539 | |
| @@ -61,7 +61,7 @@ Caveats · 60 unmodified lines | ||
| 61 | 61 | Check your drive's rating, and don't wear down your drive's lifetime if you |
| 62 | 62 | don't need to. |
| 63 | 63 | |
| 64 | −* If you mount some of your ext3/reiserfs filesystems with the -n option, then | |
| 64 | +* If you mount some of your ext3 filesystems with the -n option, then | |
| 65 | 65 | the control script will not be able to remount them correctly. You must set |
| 66 | 66 | DO_REMOUNTS=0 in the control script, otherwise it will remount them with the |
| 67 | 67 | wrong options -- or it will fail because it cannot write to /etc/mtab. |
| @@ -96,7 +96,7 @@ control script increases dirty_expire_centisecs and dirty_writeback_centisecs in · 28 unmodified lines | ||
| 96 | 96 | dirtied are not forced to be written to disk as often. The control script also |
| 97 | 97 | changes the dirty background ratio, so that background writeback of dirty pages |
| 98 | 98 | is not done anymore. Combined with a higher commit value (also 10 minutes) for |
| 99 | −ext3 or ReiserFS filesystems (also done automatically by the control script), | |
| 99 | +ext3 filesystem (also done automatically by the control script), | |
| 100 | 100 | this results in concentration of disk activity in a small time interval which |
| 101 | 101 | occurs only once every 10 minutes, or whenever the disk is forced to spin up by |
| 102 | 102 | a cache miss. The disk can then be spun down in the periods of inactivity. |
| @@ -587,7 +587,7 @@ Control script:: · 484 unmodified lines | ||
| 587 | 587 | FST=$(deduce_fstype $MP) |
| 588 | 588 | fi |
| 589 | 589 | case "$FST" in |
| 590 | − "ext3"|"reiserfs") | |
| 590 | + "ext3") | |
| 591 | 591 | PARSEDOPTS="$(parse_mount_opts commit "$OPTS")" |
| 592 | 592 | mount $DEV -t $FST $MP -o remount,$PARSEDOPTS,commit=$MAX_AGE$NOATIME_OPT |
| 593 | 593 | ;; |
| @@ -647,7 +647,7 @@ Control script:: · 53 unmodified lines | ||
| 647 | 647 | FST=$(deduce_fstype $MP) |
| 648 | 648 | fi |
| 649 | 649 | case "$FST" in |
| 650 | − "ext3"|"reiserfs") | |
| 650 | + "ext3") | |
| 651 | 651 | PARSEDOPTS="$(parse_mount_opts_wfstab $DEV commit $OPTS)" |
| 652 | 652 | PARSEDOPTS="$(parse_yesno_opts_wfstab $DEV atime atime $PARSEDOPTS)" |
| 653 | 653 | mount $DEV -t $FST $MP -o remount,$PARSEDOPTS |
| @@ -25,7 +25,7 @@ generate, like: · 24 unmodified lines | ||
| 25 | 25 | (when available) |
| 26 | 26 | |
| 27 | 27 | Those events (see linux/sonypi.h) can be polled using the character device node |
| 28 | −/dev/sonypi (major 10, minor auto allocated or specified as a option). | |
| 28 | +/dev/sonypi (major 10, minor auto allocated or specified as an option). | |
| 29 | 29 | A simple daemon which translates the jogdial movements into mouse wheel events |
| 30 | 30 | can be downloaded at: <http://popies.net/sonypi/> |
| 31 | 31 | |
| @@ -794,7 +794,7 @@ These currently include: · 793 unmodified lines | ||
| 794 | 794 | |
| 795 | 795 | journal_mode (currently raid5 only) |
| 796 | 796 | The cache mode for raid5. raid5 could include an extra disk for |
| 797 | − caching. The mode can be "write-throuth" and "write-back". The | |
| 797 | + caching. The mode can be "write-through" or "write-back". The | |
| 798 | 798 | default is "write-through". |
| 799 | 799 | |
| 800 | 800 | ppl_write_hint |
| @@ -96,7 +96,7 @@ Some of the features of this driver include: · 95 unmodified lines | ||
| 96 | 96 | motion compensation modes: low, medium, and high motion. Pipelines are |
| 97 | 97 | defined that allow sending frames to the VDIC subdev directly from the |
| 98 | 98 | CSI. There is also support in the future for sending frames to the |
| 99 | − VDIC from memory buffers via a output/mem2mem devices. | |
| 99 | + VDIC from memory buffers via output/mem2mem devices. | |
| 100 | 100 | |
| 101 | 101 | - Includes a Frame Interval Monitor (FIM) that can correct vertical sync |
| 102 | 102 | problems with the ADV718x video decoders. |
| @@ -13,7 +13,7 @@ Contact: Eduardo Valentin <eduardo.valentin@nokia.com> · 12 unmodified lines | ||
| 13 | 13 | Information about the Device |
| 14 | 14 | ---------------------------- |
| 15 | 15 | |
| 16 | −This chip is a Silicon Labs product. It is a I2C device, currently on 0x63 address. | |
| 16 | +This chip is a Silicon Labs product. It is an I2C device, currently on 0x63 address. | |
| 17 | 17 | Basically, it has transmission and signal noise level measurement features. |
| 18 | 18 | |
| 19 | 19 | The Si4713 integrates transmit functions for FM broadcast stereo transmission. |
| @@ -28,7 +28,7 @@ Users must comply with local regulations on radio frequency (RF) transmission. · 8 unmodified lines | ||
| 28 | 28 | Device driver description |
| 29 | 29 | ------------------------- |
| 30 | 30 | |
| 31 | −There are two modules to handle this device. One is a I2C device driver | |
| 31 | +There are two modules to handle this device. One is an I2C device driver | |
| 32 | 32 | and the other is a platform driver. |
| 33 | 33 | |
| 34 | 34 | The I2C device driver exports a v4l2-subdev interface to the kernel. |
| @@ -113,7 +113,7 @@ Here is a summary of them: · 78 unmodified lines | ||
| 113 | 113 | - acomp_attack_time - Sets the attack time for audio dynamic range control. |
| 114 | 114 | - acomp_release_time - Sets the release time for audio dynamic range control. |
| 115 | 115 | |
| 116 | −* Limiter setups audio deviation limiter feature. Once a over deviation occurs, | |
| 116 | +* Limiter sets up the audio deviation limiter feature. Once an over deviation occurs, | |
| 117 | 117 | it is possible to adjust the front-end gain of the audio input and always |
| 118 | 118 | prevent over deviation. |
| 119 | 119 | |
| @@ -360,7 +360,7 @@ The directory for the :ref:`quotas <damon_design_damos_quotas>` of the given · 359 unmodified lines | ||
| 360 | 360 | DAMON-based operation scheme. |
| 361 | 361 | |
| 362 | 362 | Under ``quotas`` directory, four files (``ms``, ``bytes``, |
| 363 | −``reset_interval_ms``, ``effective_bytes``) and two directores (``weights`` and | |
| 363 | +``reset_interval_ms``, ``effective_bytes``) and two directories (``weights`` and | |
| 364 | 364 | ``goals``) exist. |
| 365 | 365 | |
| 366 | 366 | You can set the ``time quota`` in milliseconds, ``size quota`` in bytes, and |
| @@ -342,7 +342,7 @@ They depend on various facilities being available: · 341 unmodified lines | ||
| 342 | 342 | When using pxelinux, the kernel image is specified using |
| 343 | 343 | "kernel <relative-path-below /tftpboot>". The nfsroot parameters |
| 344 | 344 | are passed to the kernel by adding them to the "append" line. |
| 345 | − It is common to use serial console in conjunction with pxeliunx, | |
| 345 | + It is common to use serial console in conjunction with pxelinux, | |
| 346 | 346 | see Documentation/admin-guide/serial-console.rst for more information. |
| 347 | 347 | |
| 348 | 348 | For more information on isolinux, including how to create bootdisks |
| @@ -110,8 +110,8 @@ uring channel. It is 2 bits. Some important codes are as follows: · 109 unmodified lines | ||
| 110 | 110 | - 2'b11: count the events which sent to the uring_ext (MATA) channel; |
| 111 | 111 | - 2'b01: is the same as 2'b11; |
| 112 | 112 | - 2'b10: count the events which sent to the uring (non-MATA) channel; |
| 113 | −- 2'b00: default value, count the events which sent to the both uring and | |
| 114 | − uring_ext channel; | |
| 113 | +- 2'b00: default value, count the events which sent to both uring and | |
| 114 | + uring_ext channels; | |
| 115 | 115 | |
| 116 | 116 | 6. ch: NoC PMU supports filtering the event counts of certain transaction |
| 117 | 117 | channel with this option. The current supported channels are as follows: |
| @@ -273,7 +273,7 @@ again. · 272 unmodified lines | ||
| 273 | 273 | does nothing at all; in that case you have to manually install your kernel, |
| 274 | 274 | as outlined in the reference section. |
| 275 | 275 | |
| 276 | − If you are running a immutable Linux distribution, check its documentation | |
| 276 | + If you are running an immutable Linux distribution, check its documentation | |
| 277 | 277 | and the web to find out how to install your own kernel there. |
| 278 | 278 | |
| 279 | 279 | [:ref:`details<install>`] |
| @@ -884,7 +884,7 @@ When a build error occurs, it might be caused by some aspect of your machine's · 604 unmodified lines | ||
| 884 | 884 | setup that often can be fixed quickly; other times though the problem lies in |
| 885 | 885 | the code and can only be fixed by a developer. A close examination of the |
| 886 | 886 | failure messages coupled with some research on the internet will often tell you |
| 887 | −which of the two it is. To perform such a investigation, restart the build | |
| 887 | +which of the two it is. To perform such an investigation, restart the build | |
| 888 | 888 | process like this:: |
| 889 | 889 | |
| 890 | 890 | make V=1 |
| @@ -611,7 +611,7 @@ better place. · 610 unmodified lines | ||
| 611 | 611 | |
| 612 | 612 | How to read the MAINTAINERS file |
| 613 | 613 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 614 | −To illustrate how to use the :ref:`MAINTAINERS <maintainers>` file, lets assume | |
| 614 | +To illustrate how to use the :ref:`MAINTAINERS <maintainers>` file, let's assume | |
| 615 | 615 | the WiFi in your Laptop suddenly misbehaves after updating the kernel. In that |
| 616 | 616 | case it's likely an issue in the WiFi driver. Obviously it could also be some |
| 617 | 617 | code it builds upon, but unless you suspect something like that stick to the |
| @@ -1543,7 +1543,7 @@ as well, because that will speed things up. · 925 unmodified lines | ||
| 1543 | 1543 | |
| 1544 | 1544 | And note, it helps developers a great deal if you can specify the exact version |
| 1545 | 1545 | that introduced the problem. Hence if possible within a reasonable time frame, |
| 1546 | −try to find that version using vanilla kernels. Lets assume something broke when | |
| 1546 | +try to find that version using vanilla kernels. Let's assume something broke when | |
| 1547 | 1547 | your distributor released a update from Linux kernel 5.10.5 to 5.10.8. Then as |
| 1548 | 1548 | instructed above go and check the latest kernel from that version line, say |
| 1549 | 1549 | 5.10.9. If it shows the problem, try a vanilla 5.10.5 to ensure that no patches |
| @@ -164,8 +164,8 @@ pipe-user-pages-soft · 163 unmodified lines | ||
| 164 | 164 | -------------------- |
| 165 | 165 | |
| 166 | 166 | Maximum total number of pages a non-privileged user may allocate for pipes |
| 167 | −before the pipe size gets limited to a single page. Once this limit is reached, | |
| 168 | −new pipes will be limited to a single page in size for this user in order to | |
| 167 | +before the pipe size gets limited to two pages. Once this limit is reached, | |
| 168 | +new pipes will be limited to two pages in size for this user in order to | |
| 169 | 169 | limit total memory usage, and trying to increase them using ``fcntl()`` will be |
| 170 | 170 | denied until usage goes below the limit again. The default value allows to |
| 171 | 171 | allocate up to 1024 pipes at their default size. When set to 0, no limit is |
| @@ -66,25 +66,31 @@ This documentation is about: · 65 unmodified lines | ||
| 66 | 66 | |
| 67 | 67 | =============== =============================================================== |
| 68 | 68 | abi/ execution domains & personalities |
| 69 | −debug/ <empty> | |
| 70 | −dev/ device specific information (eg dev/cdrom/info) | |
| 69 | +<$ARCH> tuning controls for various CPU architecture (e.g. csky, s390) | |
| 70 | +crypto/ <undocumented> | |
| 71 | +debug/ <undocumented> | |
| 72 | +dev/ device specific information (e.g. dev/cdrom/info) | |
| 71 | 73 | fs/ specific filesystems |
| 72 | 74 | filehandle, inode, dentry and quota tuning |
| 73 | 75 | binfmt_misc <Documentation/admin-guide/binfmt-misc.rst> |
| 74 | 76 | kernel/ global kernel info / tuning |
| 75 | 77 | miscellaneous stuff |
| 78 | + some architecture-specific controls | |
| 79 | + security (LSM) stuff | |
| 76 | 80 | net/ networking stuff, for documentation look in: |
| 77 | 81 | <Documentation/networking/> |
| 78 | 82 | proc/ <empty> |
| 79 | 83 | sunrpc/ SUN Remote Procedure Call (NFS) |
| 84 | +user/ Per user namespace limits | |
| 80 | 85 | vm/ memory management tuning |
| 81 | 86 | buffer and cache management |
| 82 | −user/ Per user per user namespace limits | |
| 87 | +xen/ <undocumented> | |
| 83 | 88 | =============== =============================================================== |
| 84 | 89 | |
| 85 | −These are the subdirs I have on my system. There might be more | |
| 86 | −or other subdirs in another setup. If you see another dir, I'd | |
| 87 | −really like to hear about it :-) | |
| 90 | +These are the subdirs I have on my system or have been discovered by | |
| 91 | +searching through the source code. There might be more or other subdirs | |
| 92 | +in another setup. If you see another dir, I'd really like to hear about | |
| 93 | +it :-) | |
| 88 | 94 | |
| 89 | 95 | .. toctree:: |
| 90 | 96 | :maxdepth: 1 |
| @@ -1757,7 +1757,7 @@ or all of these tasks: · 1756 unmodified lines | ||
| 1757 | 1757 | to your bootloader's configuration. |
| 1758 | 1758 | |
| 1759 | 1759 | You have to take care of some or all of the tasks yourself, if your |
| 1760 | −distribution lacks a installkernel script or does only handle part of them. | |
| 1760 | +distribution lacks an installkernel script or does only handle part of them. | |
| 1761 | 1761 | Consult the distribution's documentation for details. If in doubt, install the |
| 1762 | 1762 | kernel manually:: |
| 1763 | 1763 | |
| @@ -15,7 +15,7 @@ It features: · 14 unmodified lines | ||
| 15 | 15 | - SD/MMC/SDIO support |
| 16 | 16 | - Ethernet controller |
| 17 | 17 | - USB OTFG FS & HS controllers |
| 18 | −- I2C, SPI, CAN busses support | |
| 18 | +- I2C, SPI, CAN buses support | |
| 19 | 19 | - Several 16 & 32 bits general purpose timers |
| 20 | 20 | - Serial Audio interface |
| 21 | 21 | - LCD controller |
| @@ -15,7 +15,7 @@ It features: · 14 unmodified lines | ||
| 15 | 15 | - SD/MMC/SDIO support*2 |
| 16 | 16 | - Ethernet controller |
| 17 | 17 | - USB OTFG FS & HS controllers |
| 18 | −- I2C*4, SPI*6, CAN*3 busses support | |
| 18 | +- I2C*4, SPI*6, CAN*3 buses support | |
| 19 | 19 | - Several 16 & 32 bits general purpose timers |
| 20 | 20 | - Serial Audio interface*2 |
| 21 | 21 | - LCD controller |
| @@ -15,7 +15,7 @@ It features: · 14 unmodified lines | ||
| 15 | 15 | - SD/MMC/SDIO support |
| 16 | 16 | - Ethernet controller |
| 17 | 17 | - USB OTFG FS & HS controllers |
| 18 | −- I2C, SPI, CAN busses support | |
| 18 | +- I2C, SPI, CAN buses support | |
| 19 | 19 | - Several 16 & 32 bits general purpose timers |
| 20 | 20 | - Serial Audio interface |
| 21 | 21 | - LCD controller |
| @@ -15,7 +15,7 @@ It features: · 14 unmodified lines | ||
| 15 | 15 | - SD/MMC/SDIO support |
| 16 | 16 | - Ethernet controller |
| 17 | 17 | - USB OTFG FS & HS controllers |
| 18 | −- I2C, SPI, CAN busses support | |
| 18 | +- I2C, SPI, CAN buses support | |
| 19 | 19 | - Several 16 & 32 bits general purpose timers |
| 20 | 20 | - Serial Audio interface |
| 21 | 21 | - LCD controller |
| @@ -24,7 +24,7 @@ More details: · 23 unmodified lines | ||
| 24 | 24 | - ADC/DAC |
| 25 | 25 | - USB EHCI/OHCI controllers |
| 26 | 26 | - USB OTG |
| 27 | −- I2C, SPI, CAN busses support | |
| 27 | +- I2C, SPI, CAN buses support | |
| 28 | 28 | - Several general purpose timers |
| 29 | 29 | - Serial Audio interface |
| 30 | 30 | - LCD controller |
| @@ -23,7 +23,7 @@ More details: · 22 unmodified lines | ||
| 23 | 23 | - ADC/DAC |
| 24 | 24 | - USB EHCI/OHCI controllers |
| 25 | 25 | - USB OTG |
| 26 | −- I2C, SPI busses support | |
| 26 | +- I2C, SPI buses support | |
| 27 | 27 | - Several general purpose timers |
| 28 | 28 | - Serial Audio interface |
| 29 | 29 | - LCD-TFT controller |
| @@ -139,13 +139,13 @@ Feature EXTIOI_HAS_INT_ENCODE is part of standard EIOINTC. If it is 1, it · 138 unmodified lines | ||
| 139 | 139 | indicates that CPU Interrupt Pin selection can be normal method rather than |
| 140 | 140 | bitmap method, so interrupt can be routed to IP0 - IP15. |
| 141 | 141 | |
| 142 | −Feature EXTIOI_HAS_CPU_ENCODE is entension of V-EIOINTC. If it is 1, it | |
| 142 | +Feature EXTIOI_HAS_CPU_ENCODE is extension of V-EIOINTC. If it is 1, it | |
| 143 | 143 | indicates that CPU selection can be normal method rather than bitmap method, |
| 144 | 144 | so interrupt can be routed to CPU0 - CPU255. |
| 145 | 145 | |
| 146 | 146 | EXTIOI_VIRT_CONFIG |
| 147 | 147 | ------------------ |
| 148 | −This register is read-write register, for compatibility intterupt routed uses | |
| 148 | +This register is read-write register, for compatibility interrupt routed uses | |
| 149 | 149 | the default method which is the same with standard EIOINTC. If the bit is set |
| 150 | 150 | with 1, it indicated HW to use normal method rather than bitmap method. |
| 151 | 151 | |
| @@ -315,7 +315,6 @@ network daemons and file systems that didn't need to be disturbed. · 314 unmodified lines | ||
| 315 | 315 | ideally, the reset should happen at or below the block layer, |
| 316 | 316 | so that the file systems are not disturbed. |
| 317 | 317 | |
| 318 | − Reiserfs does not tolerate errors returned from the block device. | |
| 319 | 318 | Ext3fs seems to be tolerant, retrying reads/writes until it does |
| 320 | 319 | succeed. Both have been only lightly tested in this scenario. |
| 321 | 320 | |
| @@ -11,7 +11,7 @@ The list of feature flags in /proc/cpuinfo is not complete and · 10 unmodified lines | ||
| 11 | 11 | represents an ill-fated attempt from long time ago to put feature flags |
| 12 | 12 | in an easy to find place for userspace. |
| 13 | 13 | |
| 14 | −However, the amount of feature flags is growing by the CPU generation, | |
| 14 | +However, the number of feature flags is growing with each CPU generation, | |
| 15 | 15 | leading to unparseable and unwieldy /proc/cpuinfo. |
| 16 | 16 | |
| 17 | 17 | What is more, those feature flags do not even need to be in that file |
| @@ -9,6 +9,8 @@ import os · 8 unmodified lines | ||
| 9 | 9 | import shutil |
| 10 | 10 | import sys |
| 11 | 11 | |
| 12 | +from textwrap import dedent | |
| 13 | + | |
| 12 | 14 | import sphinx |
| 13 | 15 | |
| 14 | 16 | # If extensions (or modules to document with autodoc) are in another directory, |
| @@ -51,11 +53,13 @@ else: · 36 unmodified lines | ||
| 51 | 53 | dyn_exclude_patterns.append("devicetree/bindings/**.yaml") |
| 52 | 54 | dyn_exclude_patterns.append("core-api/kho/bindings/**.yaml") |
| 53 | 55 | |
| 54 | −# Properly handle include/exclude patterns | |
| 55 | −# ---------------------------------------- | |
| 56 | +# Properly handle directory patterns and LaTeX docs | |
| 57 | +# ------------------------------------------------- | |
| 56 | 58 | |
| 57 | −def update_patterns(app, config): | |
| 59 | +def config_init(app, config): | |
| 58 | 60 | """ |
| 61 | + Initialize path-dependent variabled | |
| 62 | + | |
| 59 | 63 | On Sphinx, all directories are relative to what it is passed as |
| 60 | 64 | SOURCEDIR parameter for sphinx-build. Due to that, all patterns |
| 61 | 65 | that have directory names on it need to be dynamically set, after |
| @@ -86,6 +90,38 @@ def update_patterns(app, config): · 24 unmodified lines | ||
| 86 | 90 | |
| 87 | 91 | config.exclude_patterns.append(rel_path) |
| 88 | 92 | |
| 93 | + # LaTeX and PDF output require a list of documents with are dependent | |
| 94 | + # of the app.srcdir. Add them here | |
| 95 | + | |
| 96 | + # When SPHINXDIRS is used, we just need to get index.rst, if it exists | |
| 97 | + if not os.path.samefile(doctree, app.srcdir): | |
| 98 | + doc = os.path.basename(app.srcdir) | |
| 99 | + fname = "index" | |
| 100 | + if os.path.exists(os.path.join(app.srcdir, fname + ".rst")): | |
| 101 | + latex_documents.append((fname, doc + ".tex", | |
| 102 | + "Linux %s Documentation" % doc.capitalize(), | |
| 103 | + "The kernel development community", | |
| 104 | + "manual")) | |
| 105 | + return | |
| 106 | + | |
| 107 | + # When building all docs, or when a main index.rst doesn't exist, seek | |
| 108 | + # for it on subdirectories | |
| 109 | + for doc in os.listdir(app.srcdir): | |
| 110 | + fname = os.path.join(doc, "index") | |
| 111 | + if not os.path.exists(os.path.join(app.srcdir, fname + ".rst")): | |
| 112 | + continue | |
| 113 | + | |
| 114 | + has = False | |
| 115 | + for l in latex_documents: | |
| 116 | + if l[0] == fname: | |
| 117 | + has = True | |
| 118 | + break | |
| 119 | + | |
| 120 | + if not has: | |
| 121 | + latex_documents.append((fname, doc + ".tex", | |
| 122 | + "Linux %s Documentation" % doc.capitalize(), | |
| 123 | + "The kernel development community", | |
| 124 | + "manual")) | |
| 89 | 125 | |
| 90 | 126 | # helper |
| 91 | 127 | # ------ |
| @@ -234,7 +270,7 @@ author = "The kernel development community" · 142 unmodified lines | ||
| 234 | 270 | # |version| and |release|, also used in various other places throughout the |
| 235 | 271 | # built documents. |
| 236 | 272 | # |
| 237 | −# In a normal build, version and release are are set to KERNELVERSION and | |
| 273 | +# In a normal build, version and release are set to KERNELVERSION and | |
| 238 | 274 | # KERNELRELEASE, respectively, from the Makefile via Sphinx command line |
| 239 | 275 | # arguments. |
| 240 | 276 | # |
| @@ -420,19 +456,25 @@ htmlhelp_basename = "TheLinuxKerneldoc" · 179 unmodified lines | ||
| 420 | 456 | latex_elements = { |
| 421 | 457 | # The paper size ('letterpaper' or 'a4paper'). |
| 422 | 458 | "papersize": "a4paper", |
| 459 | + "passoptionstopackages": dedent(r""" | |
| 460 | + \PassOptionsToPackage{svgnames}{xcolor} | |
| 461 | + """), | |
| 423 | 462 | # The font size ('10pt', '11pt' or '12pt'). |
| 424 | 463 | "pointsize": "11pt", |
| 464 | + # Needed to generate a .ind file | |
| 465 | + "printindex": r"\footnotesize\raggedright\printindex", | |
| 425 | 466 | # Latex figure (float) alignment |
| 426 | 467 | # 'figure_align': 'htbp', |
| 427 | 468 | # Don't mangle with UTF-8 chars |
| 469 | + "fontenc": "", | |
| 428 | 470 | "inputenc": "", |
| 429 | 471 | "utf8extra": "", |
| 430 | 472 | # Set document margins |
| 431 | − "sphinxsetup": """ | |
| 473 | + "sphinxsetup": dedent(r""" | |
| 432 | 474 | hmargin=0.5in, vmargin=1in, |
| 433 | 475 | parsedliteralwraps=true, |
| 434 | 476 | verbatimhintsturnover=false, |
| 435 | − """, | |
| 477 | + """), | |
| 436 | 478 | # |
| 437 | 479 | # Some of our authors are fond of deep nesting; tell latex to |
| 438 | 480 | # cope. |
| @@ -440,48 +482,22 @@ latex_elements = { · 1 unmodified line | ||
| 440 | 482 | "maxlistdepth": "10", |
| 441 | 483 | # For CJK One-half spacing, need to be in front of hyperref |
| 442 | 484 | "extrapackages": r"\usepackage{setspace}", |
| 443 | − # Additional stuff for the LaTeX preamble. | |
| 444 | − "preamble": """ | |
| 445 | − % Use some font with UTF-8 support with XeLaTeX | |
| 446 | − \\usepackage{fontspec} | |
| 447 | − \\setsansfont{DejaVu Sans} | |
| 448 | − \\setromanfont{DejaVu Serif} | |
| 449 | − \\setmonofont{DejaVu Sans Mono} | |
| 450 | − """, | |
| 451 | −} | |
| 452 | − | |
| 453 | −# Load kerneldoc specific LaTeX settings | |
| 454 | −latex_elements["preamble"] += """ | |
| 485 | + "fontpkg": dedent(r""" | |
| 486 | + \usepackage{fontspec} | |
| 487 | + \setmainfont{DejaVu Serif} | |
| 488 | + \setsansfont{DejaVu Sans} | |
| 489 | + \setmonofont{DejaVu Sans Mono} | |
| 490 | + \newfontfamily\headingfont{DejaVu Serif} | |
| 491 | + """), | |
| 492 | + "preamble": dedent(r""" | |
| 455 | 493 | % Load kerneldoc specific LaTeX settings |
| 456 | − \\input{kerneldoc-preamble.sty} | |
| 457 | −""" | |
| 494 | + \input{kerneldoc-preamble.sty} | |
| 495 | + """) | |
| 496 | +} | |
| 458 | 497 | |
| 459 | −# Grouping the document tree into LaTeX files. List of tuples | |
| 460 | −# (source start file, target name, title, | |
| 461 | −# author, documentclass [howto, manual, or own class]). | |
| 462 | −# Sorted in alphabetical order | |
| 498 | +# This will be filled up by config-inited event | |
| 463 | 499 | latex_documents = [] |
| 464 | 500 | |
| 465 | −# Add all other index files from Documentation/ subdirectories | |
| 466 | −for fn in os.listdir("."): | |
| 467 | − doc = os.path.join(fn, "index") | |
| 468 | − if os.path.exists(doc + ".rst"): | |
| 469 | − has = False | |
| 470 | − for l in latex_documents: | |
| 471 | − if l[0] == doc: | |
| 472 | − has = True | |
| 473 | − break | |
| 474 | − if not has: | |
| 475 | − latex_documents.append( | |
| 476 | − ( | |
| 477 | − doc, | |
| 478 | − fn + ".tex", | |
| 479 | − "Linux %s Documentation" % fn.capitalize(), | |
| 480 | − "The kernel development community", | |
| 481 | − "manual", | |
| 482 | − ) | |
| 483 | − ) | |
| 484 | − | |
| 485 | 501 | # The name of an image file (relative to this directory) to place at the top of |
| 486 | 502 | # the title page. |
| 487 | 503 | # latex_logo = None |
| @@ -577,4 +593,4 @@ loadConfig(globals()) · 89 unmodified lines | ||
| 577 | 593 | def setup(app): |
| 578 | 594 | """Patterns need to be updated at init time on older Sphinx versions""" |
| 579 | 595 | |
| 580 | − app.connect('config-inited', update_patterns) | |
| 596 | + app.connect('config-inited', config_init) | |
| @@ -44,7 +44,7 @@ Each segment in the list also stores: · 43 unmodified lines | ||
| 44 | 44 | * the size of each folio and |
| 45 | 45 | * three 1-bit marks per folio, |
| 46 | 46 | |
| 47 | −but hese should not be accessed directly as the underlying data structure may | |
| 47 | +but these should not be accessed directly as the underlying data structure may | |
| 48 | 48 | change, but rather the access functions outlined below should be used. |
| 49 | 49 | |
| 50 | 50 | The facility can be made accessible by:: |
| @@ -24,6 +24,7 @@ it. · 23 unmodified lines | ||
| 24 | 24 | printk-index |
| 25 | 25 | symbol-namespaces |
| 26 | 26 | asm-annotations |
| 27 | + real-time/index | |
| 27 | 28 | |
| 28 | 29 | Data structures and low-level utilities |
| 29 | 30 | ======================================= |
| @@ -9,9 +9,9 @@ ChangeLog: · 8 unmodified lines | ||
| 9 | 9 | |
| 10 | 10 | /proc/irq/IRQ#/smp_affinity and /proc/irq/IRQ#/smp_affinity_list specify |
| 11 | 11 | which target CPUs are permitted for a given IRQ source. It's a bitmask |
| 12 | −(smp_affinity) or cpu list (smp_affinity_list) of allowed CPUs. It's not | |
| 12 | +(smp_affinity) or CPU list (smp_affinity_list) of allowed CPUs. It's not | |
| 13 | 13 | allowed to turn off all CPUs, and if an IRQ controller does not support |
| 14 | −IRQ affinity then the value will not change from the default of all cpus. | |
| 14 | +IRQ affinity then the value will not change from the default of all CPUs. | |
| 15 | 15 | |
| 16 | 16 | /proc/irq/default_smp_affinity specifies default affinity mask that applies |
| 17 | 17 | to all non-active IRQs. Once IRQ is allocated/activated its affinity bitmask |
| @@ -60,7 +60,7 @@ Now lets restrict that IRQ to CPU(4-7). · 42 unmodified lines | ||
| 60 | 60 | This time around IRQ44 was delivered only to the last four processors. |
| 61 | 61 | i.e counters for the CPU0-3 did not change. |
| 62 | 62 | |
| 63 | −Here is an example of limiting that same irq (44) to cpus 1024 to 1031:: | |
| 63 | +Here is an example of limiting that same IRQ (44) to CPUs 1024 to 1031:: | |
| 64 | 64 | |
| 65 | 65 | [root@moon 44]# echo 1024-1031 > smp_affinity_list |
| 66 | 66 | [root@moon 44]# cat smp_affinity_list |
| @@ -18,8 +18,8 @@ handlers as irqchips. I.e. in effect cascading interrupt controllers. · 17 unmodified lines | ||
| 18 | 18 | So in the past, IRQ numbers could be chosen so that they match the |
| 19 | 19 | hardware IRQ line into the root interrupt controller (i.e. the |
| 20 | 20 | component actually firing the interrupt line to the CPU). Nowadays, |
| 21 | −this number is just a number and the number loose all kind of | |
| 22 | −correspondence to hardware interrupt numbers. | |
| 21 | +this number is just a number and the number has no | |
| 22 | +relationship to hardware interrupt numbers. | |
| 23 | 23 | |
| 24 | 24 | For this reason, we need a mechanism to separate controller-local |
| 25 | 25 | interrupt numbers, called hardware IRQs, from Linux IRQ numbers. |
| @@ -77,15 +77,15 @@ Once a mapping has been established, it can be retrieved or used via a · 51 unmodified lines | ||
| 77 | 77 | variety of methods: |
| 78 | 78 | |
| 79 | 79 | - irq_resolve_mapping() returns a pointer to the irq_desc structure |
| 80 | − for a given domain and hwirq number, and NULL if there was no | |
| 80 | + for a given domain and hwirq number, or NULL if there was no | |
| 81 | 81 | mapping. |
| 82 | 82 | - irq_find_mapping() returns a Linux IRQ number for a given domain and |
| 83 | − hwirq number, and 0 if there was no mapping | |
| 83 | + hwirq number, or 0 if there was no mapping | |
| 84 | 84 | - generic_handle_domain_irq() handles an interrupt described by a |
| 85 | 85 | domain and a hwirq number |
| 86 | 86 | |
| 87 | −Note that irq domain lookups must happen in contexts that are | |
| 88 | −compatible with a RCU read-side critical section. | |
| 87 | +Note that irq_domain lookups must happen in contexts that are | |
| 88 | +compatible with an RCU read-side critical section. | |
| 89 | 89 | |
| 90 | 90 | The irq_create_mapping() function must be called *at least once* |
| 91 | 91 | before any call to irq_find_mapping(), lest the descriptor will not |
| @@ -100,7 +100,7 @@ Types of irq_domain Mappings · 8 unmodified lines | ||
| 100 | 100 | ============================ |
| 101 | 101 | |
| 102 | 102 | There are several mechanisms available for reverse mapping from hwirq |
| 103 | −to Linux irq, and each mechanism uses a different allocation function. | |
| 103 | +to Linux IRQ, and each mechanism uses a different allocation function. | |
| 104 | 104 | Which reverse map type should be used depends on the use case. Each |
| 105 | 105 | of the reverse map types are described below: |
| 106 | 106 | |
| @@ -111,13 +111,13 @@ Linear · 4 unmodified lines | ||
| 111 | 111 | |
| 112 | 112 | irq_domain_create_linear() |
| 113 | 113 | |
| 114 | −The linear reverse map maintains a fixed size table indexed by the | |
| 114 | +The linear reverse map maintains a fixed-size table indexed by the | |
| 115 | 115 | hwirq number. When a hwirq is mapped, an irq_desc is allocated for |
| 116 | 116 | the hwirq, and the IRQ number is stored in the table. |
| 117 | 117 | |
| 118 | 118 | The Linear map is a good choice when the maximum number of hwirqs is |
| 119 | 119 | fixed and a relatively small number (~ < 256). The advantages of this |
| 120 | −map are fixed time lookup for IRQ numbers, and irq_descs are only | |
| 120 | +map are fixed-time lookup for IRQ numbers, and irq_descs are only | |
| 121 | 121 | allocated for in-use IRQs. The disadvantage is that the table must be |
| 122 | 122 | as large as the largest possible hwirq number. |
| 123 | 123 | |
| @@ -134,7 +134,7 @@ The irq_domain maintains a radix tree map from hwirq numbers to Linux · 10 unmodified lines | ||
| 134 | 134 | IRQs. When an hwirq is mapped, an irq_desc is allocated and the |
| 135 | 135 | hwirq is used as the lookup key for the radix tree. |
| 136 | 136 | |
| 137 | −The tree map is a good choice if the hwirq number can be very large | |
| 137 | +The Tree map is a good choice if the hwirq number can be very large | |
| 138 | 138 | since it doesn't need to allocate a table as large as the largest |
| 139 | 139 | hwirq number. The disadvantage is that hwirq to IRQ number lookup is |
| 140 | 140 | dependent on how many entries are in the table. |
| @@ -169,10 +169,10 @@ Legacy · 28 unmodified lines | ||
| 169 | 169 | |
| 170 | 170 | The Legacy mapping is a special case for drivers that already have a |
| 171 | 171 | range of irq_descs allocated for the hwirqs. It is used when the |
| 172 | −driver cannot be immediately converted to use the linear mapping. For | |
| 172 | +driver cannot be immediately converted to use the Linear mapping. For | |
| 173 | 173 | example, many embedded system board support files use a set of #defines |
| 174 | 174 | for IRQ numbers that are passed to struct device registrations. In that |
| 175 | −case the Linux IRQ numbers cannot be dynamically assigned and the legacy | |
| 175 | +case the Linux IRQ numbers cannot be dynamically assigned and the Legacy | |
| 176 | 176 | mapping should be used. |
| 177 | 177 | |
| 178 | 178 | As the name implies, the \*_legacy() functions are deprecated and only |
| @@ -180,15 +180,15 @@ exist to ease the support of ancient platforms. No new users should be · 1 unmodified line | ||
| 180 | 180 | added. Same goes for the \*_simple() functions when their use results |
| 181 | 181 | in the legacy behaviour. |
| 182 | 182 | |
| 183 | −The legacy map assumes a contiguous range of IRQ numbers has already | |
| 183 | +The Legacy map assumes a contiguous range of IRQ numbers has already | |
| 184 | 184 | been allocated for the controller and that the IRQ number can be |
| 185 | 185 | calculated by adding a fixed offset to the hwirq number, and |
| 186 | 186 | visa-versa. The disadvantage is that it requires the interrupt |
| 187 | 187 | controller to manage IRQ allocations and it requires an irq_desc to be |
| 188 | 188 | allocated for every hwirq, even if it is unused. |
| 189 | 189 | |
| 190 | −The legacy map should only be used if fixed IRQ mappings must be | |
| 191 | −supported. For example, ISA controllers would use the legacy map for | |
| 190 | +The Legacy map should only be used if fixed IRQ mappings must be | |
| 191 | +supported. For example, ISA controllers would use the Legacy map for | |
| 192 | 192 | mapping Linux IRQs 0-15 so that existing ISA drivers get the correct IRQ |
| 193 | 193 | numbers. |
| 194 | 194 | |
| @@ -197,7 +197,7 @@ which will use a legacy domain only if an IRQ range is supplied by the · 2 unmodified lines | ||
| 197 | 197 | system and will otherwise use a linear domain mapping. The semantics of |
| 198 | 198 | this call are such that if an IRQ range is specified then descriptors |
| 199 | 199 | will be allocated on-the-fly for it, and if no range is specified it |
| 200 | −will fall through to irq_domain_create_linear() which means *no* irq | |
| 200 | +will fall through to irq_domain_create_linear() which means *no* IRQ | |
| 201 | 201 | descriptors will be allocated. |
| 202 | 202 | |
| 203 | 203 | A typical use case for simple domains is where an irqchip provider |
| @@ -214,7 +214,7 @@ Hierarchy IRQ Domain · 10 unmodified lines | ||
| 214 | 214 | |
| 215 | 215 | On some architectures, there may be multiple interrupt controllers |
| 216 | 216 | involved in delivering an interrupt from the device to the target CPU. |
| 217 | −Let's look at a typical interrupt delivering path on x86 platforms:: | |
| 217 | +Let's look at a typical interrupt delivery path on x86 platforms:: | |
| 218 | 218 | |
| 219 | 219 | Device --> IOAPIC -> Interrupt remapping Controller -> Local APIC -> CPU |
| 220 | 220 | |
| @@ -227,8 +227,8 @@ There are three interrupt controllers involved: · 6 unmodified lines | ||
| 227 | 227 | To support such a hardware topology and make software architecture match |
| 228 | 228 | hardware architecture, an irq_domain data structure is built for each |
| 229 | 229 | interrupt controller and those irq_domains are organized into hierarchy. |
| 230 | −When building irq_domain hierarchy, the irq_domain near to the device is | |
| 231 | −child and the irq_domain near to CPU is parent. So a hierarchy structure | |
| 230 | +When building irq_domain hierarchy, the irq_domain nearest the device is | |
| 231 | +child and the irq_domain nearest the CPU is parent. So a hierarchy structure | |
| 232 | 232 | as below will be built for the example above:: |
| 233 | 233 | |
| 234 | 234 | CPU Vector irq_domain (root irq_domain to manage CPU vectors) |
| @@ -521,7 +521,7 @@ Fwnode handles · 520 unmodified lines | ||
| 521 | 521 | |
| 522 | 522 | %pfw[fP] |
| 523 | 523 | |
| 524 | −For printing information on fwnode handles. The default is to print the full | |
| 524 | +For printing information on an fwnode_handle. The default is to print the full | |
| 525 | 525 | node name, including the path. The modifiers are functionally equivalent to |
| 526 | 526 | %pOF above. |
| 527 | 527 | |
| @@ -0,0 +1,109 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +============================================= | |
| 4 | +Porting an architecture to support PREEMPT_RT | |
| 5 | +============================================= | |
| 6 | + | |
| 7 | +:Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de> | |
| 8 | + | |
| 9 | +This list outlines the architecture specific requirements that must be | |
| 10 | +implemented in order to enable PREEMPT_RT. Once all required features are | |
| 11 | +implemented, ARCH_SUPPORTS_RT can be selected in architecture’s Kconfig to make | |
| 12 | +PREEMPT_RT selectable. | |
| 13 | +Many prerequisites (genirq support for example) are enforced by the common code | |
| 14 | +and are omitted here. | |
| 15 | + | |
| 16 | +The optional features are not strictly required but it is worth to consider | |
| 17 | +them. | |
| 18 | + | |
| 19 | +Requirements | |
| 20 | +------------ | |
| 21 | + | |
| 22 | +Forced threaded interrupts | |
| 23 | + CONFIG_IRQ_FORCED_THREADING must be selected. Any interrupts that must | |
| 24 | + remain in hard-IRQ context must be marked with IRQF_NO_THREAD. This | |
| 25 | + requirement applies for instance to clocksource event interrupts, | |
| 26 | + perf interrupts and cascading interrupt-controller handlers. | |
| 27 | + | |
| 28 | +PREEMPTION support | |
| 29 | + Kernel preemption must be supported and requires that | |
| 30 | + CONFIG_ARCH_NO_PREEMPT remain unselected. Scheduling requests, such as those | |
| 31 | + issued from an interrupt or other exception handler, must be processed | |
| 32 | + immediately. | |
| 33 | + | |
| 34 | +POSIX CPU timers and KVM | |
| 35 | + POSIX CPU timers must expire from thread context rather than directly within | |
| 36 | + the timer interrupt. This behavior is enabled by setting the configuration | |
| 37 | + option CONFIG_HAVE_POSIX_CPU_TIMERS_TASK_WORK. | |
| 38 | + When KVM is enabled, CONFIG_KVM_XFER_TO_GUEST_WORK must also be set to ensure | |
| 39 | + that any pending work, such as POSIX timer expiration, is handled before | |
| 40 | + transitioning into guest mode. | |
| 41 | + | |
| 42 | +Hard-IRQ and Soft-IRQ stacks | |
| 43 | + Soft interrupts are handled in the thread context in which they are raised. If | |
| 44 | + a soft interrupt is triggered from hard-IRQ context, its execution is deferred | |
| 45 | + to the ksoftirqd thread. Preemption is never disabled during soft interrupt | |
| 46 | + handling, which makes soft interrupts preemptible. | |
| 47 | + If an architecture provides a custom __do_softirq() implementation that uses a | |
| 48 | + separate stack, it must select CONFIG_HAVE_SOFTIRQ_ON_OWN_STACK. The | |
| 49 | + functionality should only be enabled when CONFIG_SOFTIRQ_ON_OWN_STACK is set. | |
| 50 | + | |
| 51 | +FPU and SIMD access in kernel mode | |
| 52 | + FPU and SIMD registers are typically not used in kernel mode and are therefore | |
| 53 | + not saved during kernel preemption. As a result, any kernel code that uses | |
| 54 | + these registers must be enclosed within a kernel_fpu_begin() and | |
| 55 | + kernel_fpu_end() section. | |
| 56 | + The kernel_fpu_begin() function usually invokes local_bh_disable() to prevent | |
| 57 | + interruptions from softirqs and to disable regular preemption. This allows the | |
| 58 | + protected code to run safely in both thread and softirq contexts. | |
| 59 | + On PREEMPT_RT kernels, however, kernel_fpu_begin() must not call | |
| 60 | + local_bh_disable(). Instead, it should use preempt_disable(), since softirqs | |
| 61 | + are always handled in thread context under PREEMPT_RT. In this case, disabling | |
| 62 | + preemption alone is sufficient. | |
| 63 | + The crypto subsystem operates on memory pages and requires users to "walk and | |
| 64 | + map" these pages while processing a request. This operation must occur outside | |
| 65 | + the kernel_fpu_begin()/ kernel_fpu_end() section because it requires preemption | |
| 66 | + to be enabled. These preemption points are generally sufficient to avoid | |
| 67 | + excessive scheduling latency. | |
| 68 | + | |
| 69 | +Exception handlers | |
| 70 | + Exception handlers, such as the page fault handler, typically enable interrupts | |
| 71 | + early, before invoking any generic code to process the exception. This is | |
| 72 | + necessary because handling a page fault may involve operations that can sleep. | |
| 73 | + Enabling interrupts is especially important on PREEMPT_RT, where certain | |
| 74 | + locks, such as spinlock_t, become sleepable. For example, handling an | |
| 75 | + invalid opcode may result in sending a SIGILL signal to the user task. A | |
| 76 | + debug excpetion will send a SIGTRAP signal. | |
| 77 | + In both cases, if the exception occurred in user space, it is safe to enable | |
| 78 | + interrupts early. Sending a signal requires both interrupts and kernel | |
| 79 | + preemption to be enabled. | |
| 80 | + | |
| 81 | +Optional features | |
| 82 | +----------------- | |
| 83 | + | |
| 84 | +Timer and clocksource | |
| 85 | + A high-resolution clocksource and clockevents device are recommended. The | |
| 86 | + clockevents device should support the CLOCK_EVT_FEAT_ONESHOT feature for | |
| 87 | + optimal timer behavior. In most cases, microsecond-level accuracy is | |
| 88 | + sufficient | |
| 89 | + | |
| 90 | +Lazy preemption | |
| 91 | + This mechanism allows an in-kernel scheduling request for non-real-time tasks | |
| 92 | + to be delayed until the task is about to return to user space. It helps avoid | |
| 93 | + preempting a task that holds a sleeping lock at the time of the scheduling | |
| 94 | + request. | |
| 95 | + With CONFIG_GENERIC_IRQ_ENTRY enabled, supporting this feature requires | |
| 96 | + defining a bit for TIF_NEED_RESCHED_LAZY, preferably near TIF_NEED_RESCHED. | |
| 97 | + | |
| 98 | +Serial console with NBCON | |
| 99 | + With PREEMPT_RT enabled, all console output is handled by a dedicated thread | |
| 100 | + rather than directly from the context in which printk() is invoked. This design | |
| 101 | + allows printk() to be safely used in atomic contexts. | |
| 102 | + However, this also means that if the kernel crashes and cannot switch to the | |
| 103 | + printing thread, no output will be visible preventing the system from printing | |
| 104 | + its final messages. | |
| 105 | + There are exceptions for immediate output, such as during panic() handling. To | |
| 106 | + support this, the console driver must implement new-style lock handling. This | |
| 107 | + involves setting the CON_NBCON flag in console::flags and providing | |
| 108 | + implementations for the write_atomic, write_thread, device_lock, and | |
| 109 | + device_unlock callbacks. | |
| @@ -0,0 +1,242 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +=========================== | |
| 4 | +How realtime kernels differ | |
| 5 | +=========================== | |
| 6 | + | |
| 7 | +:Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de> | |
| 8 | + | |
| 9 | +Preface | |
| 10 | +======= | |
| 11 | + | |
| 12 | +With forced-threaded interrupts and sleeping spin locks, code paths that | |
| 13 | +previously caused long scheduling latencies have been made preemptible and | |
| 14 | +moved into process context. This allows the scheduler to manage them more | |
| 15 | +effectively and respond to higher-priority tasks with reduced latency. | |
| 16 | + | |
| 17 | +The following chapters provide an overview of key differences between a | |
| 18 | +PREEMPT_RT kernel and a standard, non-PREEMPT_RT kernel. | |
| 19 | + | |
| 20 | +Locking | |
| 21 | +======= | |
| 22 | + | |
| 23 | +Spinning locks such as spinlock_t are used to provide synchronization for data | |
| 24 | +structures accessed from both interrupt context and process context. For this | |
| 25 | +reason, locking functions are also available with the _irq() or _irqsave() | |
| 26 | +suffixes, which disable interrupts before acquiring the lock. This ensures that | |
| 27 | +the lock can be safely acquired in process context when interrupts are enabled. | |
| 28 | + | |
| 29 | +However, on a PREEMPT_RT system, interrupts are forced-threaded and no longer | |
| 30 | +run in hard IRQ context. As a result, there is no need to disable interrupts as | |
| 31 | +part of the locking procedure when using spinlock_t. | |
| 32 | + | |
| 33 | +For low-level core components such as interrupt handling, the scheduler, or the | |
| 34 | +timer subsystem the kernel uses raw_spinlock_t. This lock type preserves | |
| 35 | +traditional semantics: it disables preemption and, when used with _irq() or | |
| 36 | +_irqsave(), also disables interrupts. This ensures proper synchronization in | |
| 37 | +critical sections that must remain non-preemptible or with interrupts disabled. | |
| 38 | + | |
| 39 | +Execution context | |
| 40 | +================= | |
| 41 | + | |
| 42 | +Interrupt handling in a PREEMPT_RT system is invoked in process context through | |
| 43 | +the use of threaded interrupts. Other parts of the kernel also shift their | |
| 44 | +execution into threaded context by different mechanisms. The goal is to keep | |
| 45 | +execution paths preemptible, allowing the scheduler to interrupt them when a | |
| 46 | +higher-priority task needs to run. | |
| 47 | + | |
| 48 | +Below is an overview of the kernel subsystems involved in this transition to | |
| 49 | +threaded, preemptible execution. | |
| 50 | + | |
| 51 | +Interrupt handling | |
| 52 | +------------------ | |
| 53 | + | |
| 54 | +All interrupts are forced-threaded in a PREEMPT_RT system. The exceptions are | |
| 55 | +interrupts that are requested with the IRQF_NO_THREAD, IRQF_PERCPU, or | |
| 56 | +IRQF_ONESHOT flags. | |
| 57 | + | |
| 58 | +The IRQF_ONESHOT flag is used together with threaded interrupts, meaning those | |
| 59 | +registered using request_threaded_irq() and providing only a threaded handler. | |
| 60 | +Its purpose is to keep the interrupt line masked until the threaded handler has | |
| 61 | +completed. | |
| 62 | + | |
| 63 | +If a primary handler is also provided in this case, it is essential that the | |
| 64 | +handler does not acquire any sleeping locks, as it will not be threaded. The | |
| 65 | +handler should be minimal and must avoid introducing delays, such as | |
| 66 | +busy-waiting on hardware registers. | |
| 67 | + | |
| 68 | + | |
| 69 | +Soft interrupts, bottom half handling | |
| 70 | +------------------------------------- | |
| 71 | + | |
| 72 | +Soft interrupts are raised by the interrupt handler and are executed after the | |
| 73 | +handler returns. Since they run in thread context, they can be preempted by | |
| 74 | +other threads. Do not assume that softirq context runs with preemption | |
| 75 | +disabled. This means you must not rely on mechanisms like local_bh_disable() in | |
| 76 | +process context to protect per-CPU variables. Because softirq handlers are | |
| 77 | +preemptible under PREEMPT_RT, this approach does not provide reliable | |
| 78 | +synchronization. | |
| 79 | + | |
| 80 | +If this kind of protection is required for performance reasons, consider using | |
| 81 | +local_lock_nested_bh(). On non-PREEMPT_RT kernels, this allows lockdep to | |
| 82 | +verify that bottom halves are disabled. On PREEMPT_RT systems, it adds the | |
| 83 | +necessary locking to ensure proper protection. | |
| 84 | + | |
| 85 | +Using local_lock_nested_bh() also makes the locking scope explicit and easier | |
| 86 | +for readers and maintainers to understand. | |
| 87 | + | |
| 88 | + | |
| 89 | +per-CPU variables | |
| 90 | +----------------- | |
| 91 | + | |
| 92 | +Protecting access to per-CPU variables solely by using preempt_disable() should | |
| 93 | +be avoided, especially if the critical section has unbounded runtime or may | |
| 94 | +call APIs that can sleep. | |
| 95 | + | |
| 96 | +If using a spinlock_t is considered too costly for performance reasons, | |
| 97 | +consider using local_lock_t. On non-PREEMPT_RT configurations, this introduces | |
| 98 | +no runtime overhead when lockdep is disabled. With lockdep enabled, it verifies | |
| 99 | +that the lock is only acquired in process context and never from softirq or | |
| 100 | +hard IRQ context. | |
| 101 | + | |
| 102 | +On a PREEMPT_RT kernel, local_lock_t is implemented using a per-CPU spinlock_t, | |
| 103 | +which provides safe local protection for per-CPU data while keeping the system | |
| 104 | +preemptible. | |
| 105 | + | |
| 106 | +Because spinlock_t on PREEMPT_RT does not disable preemption, it cannot be used | |
| 107 | +to protect per-CPU data by relying on implicit preemption disabling. If this | |
| 108 | +inherited preemption disabling is essential and if local_lock_t cannot be used | |
| 109 | +due to performance constraints, brevity of the code, or abstraction boundaries | |
| 110 | +within an API then preempt_disable_nested() may be a suitable alternative. On | |
| 111 | +non-PREEMPT_RT kernels, it verifies with lockdep that preemption is already | |
| 112 | +disabled. On PREEMPT_RT, it explicitly disables preemption. | |
| 113 | + | |
| 114 | +Timers | |
| 115 | +------ | |
| 116 | + | |
| 117 | +By default, an hrtimer is executed in hard interrupt context. The exception is | |
| 118 | +timers initialized with the HRTIMER_MODE_SOFT flag, which are executed in | |
| 119 | +softirq context. | |
| 120 | + | |
| 121 | +On a PREEMPT_RT kernel, this behavior is reversed: hrtimers are executed in | |
| 122 | +softirq context by default, typically within the ktimersd thread. This thread | |
| 123 | +runs at the lowest real-time priority, ensuring it executes before any | |
| 124 | +SCHED_OTHER tasks but does not interfere with higher-priority real-time | |
| 125 | +threads. To explicitly request execution in hard interrupt context on | |
| 126 | +PREEMPT_RT, the timer must be marked with the HRTIMER_MODE_HARD flag. | |
| 127 | + | |
| 128 | +Memory allocation | |
| 129 | +----------------- | |
| 130 | + | |
| 131 | +The memory allocation APIs, such as kmalloc() and alloc_pages(), require a | |
| 132 | +gfp_t flag to indicate the allocation context. On non-PREEMPT_RT kernels, it is | |
| 133 | +necessary to use GFP_ATOMIC when allocating memory from interrupt context or | |
| 134 | +from sections where preemption is disabled. This is because the allocator must | |
| 135 | +not sleep in these contexts waiting for memory to become available. | |
| 136 | + | |
| 137 | +However, this approach does not work on PREEMPT_RT kernels. The memory | |
| 138 | +allocator in PREEMPT_RT uses sleeping locks internally, which cannot be | |
| 139 | +acquired when preemption is disabled. Fortunately, this is generally not a | |
| 140 | +problem, because PREEMPT_RT moves most contexts that would traditionally run | |
| 141 | +with preemption or interrupts disabled into threaded context, where sleeping is | |
| 142 | +allowed. | |
| 143 | + | |
| 144 | +What remains problematic is code that explicitly disables preemption or | |
| 145 | +interrupts. In such cases, memory allocation must be performed outside the | |
| 146 | +critical section. | |
| 147 | + | |
| 148 | +This restriction also applies to memory deallocation routines such as kfree() | |
| 149 | +and free_pages(), which may also involve internal locking and must not be | |
| 150 | +called from non-preemptible contexts. | |
| 151 | + | |
| 152 | +IRQ work | |
| 153 | +-------- | |
| 154 | + | |
| 155 | +The irq_work API provides a mechanism to schedule a callback in interrupt | |
| 156 | +context. It is designed for use in contexts where traditional scheduling is not | |
| 157 | +possible, such as from within NMI handlers or from inside the scheduler, where | |
| 158 | +using a workqueue would be unsafe. | |
| 159 | + | |
| 160 | +On non-PREEMPT_RT systems, all irq_work items are executed immediately in | |
| 161 | +interrupt context. Items marked with IRQ_WORK_LAZY are deferred until the next | |
| 162 | +timer tick but are still executed in interrupt context. | |
| 163 | + | |
| 164 | +On PREEMPT_RT systems, the execution model changes. Because irq_work callbacks | |
| 165 | +may acquire sleeping locks or have unbounded execution time, they are handled | |
| 166 | +in thread context by a per-CPU irq_work kernel thread. This thread runs at the | |
| 167 | +lowest real-time priority, ensuring it executes before any SCHED_OTHER tasks | |
| 168 | +but does not interfere with higher-priority real-time threads. | |
| 169 | + | |
| 170 | +The exception are work items marked with IRQ_WORK_HARD_IRQ, which are still | |
| 171 | +executed in hard interrupt context. Lazy items (IRQ_WORK_LAZY) continue to be | |
| 172 | +deferred until the next timer tick and are also executed by the irq_work/ | |
| 173 | +thread. | |
| 174 | + | |
| 175 | +RCU callbacks | |
| 176 | +------------- | |
| 177 | + | |
| 178 | +RCU callbacks are invoked by default in softirq context. Their execution is | |
| 179 | +important because, depending on the use case, they either free memory or ensure | |
| 180 | +progress in state transitions. Running these callbacks as part of the softirq | |
| 181 | +chain can lead to undesired situations, such as contention for CPU resources | |
| 182 | +with other SCHED_OTHER tasks when executed within ksoftirqd. | |
| 183 | + | |
| 184 | +To avoid running callbacks in softirq context, the RCU subsystem provides a | |
| 185 | +mechanism to execute them in process context instead. This behavior can be | |
| 186 | +enabled by setting the boot command-line parameter rcutree.use_softirq=0. This | |
| 187 | +setting is enforced in kernels configured with PREEMPT_RT. | |
| 188 | + | |
| 189 | +Spin until ready | |
| 190 | +================ | |
| 191 | + | |
| 192 | +The "spin until ready" pattern involves repeatedly checking (spinning on) the | |
| 193 | +state of a data structure until it becomes available. This pattern assumes that | |
| 194 | +preemption, soft interrupts, or interrupts are disabled. If the data structure | |
| 195 | +is marked busy, it is presumed to be in use by another CPU, and spinning should | |
| 196 | +eventually succeed as that CPU makes progress. | |
| 197 | + | |
| 198 | +Some examples are hrtimer_cancel() or timer_delete_sync(). These functions | |
| 199 | +cancel timers that execute with interrupts or soft interrupts disabled. If a | |
| 200 | +thread attempts to cancel a timer and finds it active, spinning until the | |
| 201 | +callback completes is safe because the callback can only run on another CPU and | |
| 202 | +will eventually finish. | |
| 203 | + | |
| 204 | +On PREEMPT_RT kernels, however, timer callbacks run in thread context. This | |
| 205 | +introduces a challenge: a higher-priority thread attempting to cancel the timer | |
| 206 | +may preempt the timer callback thread. Since the scheduler cannot migrate the | |
| 207 | +callback thread to another CPU due to affinity constraints, spinning can result | |
| 208 | +in livelock even on multiprocessor systems. | |
| 209 | + | |
| 210 | +To avoid this, both the canceling and callback sides must use a handshake | |
| 211 | +mechanism that supports priority inheritance. This allows the canceling thread | |
| 212 | +to suspend until the callback completes, ensuring forward progress without | |
| 213 | +risking livelock. | |
| 214 | + | |
| 215 | +In order to solve the problem at the API level, the sequence locks were extended | |
| 216 | +to allow a proper handover between the the spinning reader and the maybe | |
| 217 | +blocked writer. | |
| 218 | + | |
| 219 | +Sequence locks | |
| 220 | +-------------- | |
| 221 | + | |
| 222 | +Sequence counters and sequential locks are documented in | |
| 223 | +Documentation/locking/seqlock.rst. | |
| 224 | + | |
| 225 | +The interface has been extended to ensure proper preemption states for the | |
| 226 | +writer and spinning reader contexts. This is achieved by embedding the writer | |
| 227 | +serialization lock directly into the sequence counter type, resulting in | |
| 228 | +composite types such as seqcount_spinlock_t or seqcount_mutex_t. | |
| 229 | + | |
| 230 | +These composite types allow readers to detect an ongoing write and actively | |
| 231 | +boost the writer’s priority to help it complete its update instead of spinning | |
| 232 | +and waiting for its completion. | |
| 233 | + | |
| 234 | +If the plain seqcount_t is used, extra care must be taken to synchronize the | |
| 235 | +reader with the writer during updates. The writer must ensure its update is | |
| 236 | +serialized and non-preemptible relative to the reader. This cannot be achieved | |
| 237 | +using a regular spinlock_t because spinlock_t on PREEMPT_RT does not disable | |
| 238 | +preemption. In such cases, using seqcount_spinlock_t is the preferred solution. | |
| 239 | + | |
| 240 | +However, if there is no spinning involved i.e., if the reader only needs to | |
| 241 | +detect whether a write has started and not serialize against it then using | |
| 242 | +seqcount_t is reasonable. | |
| @@ -0,0 +1,16 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +===================== | |
| 4 | +Real-time preemption | |
| 5 | +===================== | |
| 6 | + | |
| 7 | +This documentation is intended for Linux kernel developers and contributors | |
| 8 | +interested in the inner workings of PREEMPT_RT. It explains key concepts and | |
| 9 | +the required changes compared to a non-PREEMPT_RT configuration. | |
| 10 | + | |
| 11 | +.. toctree:: | |
| 12 | + :maxdepth: 2 | |
| 13 | + | |
| 14 | + theory | |
| 15 | + differences | |
| 16 | + architecture-porting | |
| @@ -0,0 +1,116 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +===================== | |
| 4 | +Theory of operation | |
| 5 | +===================== | |
| 6 | + | |
| 7 | +:Author: Sebastian Andrzej Siewior <bigeasy@linutronix.de> | |
| 8 | + | |
| 9 | +Preface | |
| 10 | +======= | |
| 11 | + | |
| 12 | +PREEMPT_RT transforms the Linux kernel into a real-time kernel. It achieves | |
| 13 | +this by replacing locking primitives, such as spinlock_t, with a preemptible | |
| 14 | +and priority-inheritance aware implementation known as rtmutex, and by enforcing | |
| 15 | +the use of threaded interrupts. As a result, the kernel becomes fully | |
| 16 | +preemptible, with the exception of a few critical code paths, including entry | |
| 17 | +code, the scheduler, and low-level interrupt handling routines. | |
| 18 | + | |
| 19 | +This transformation places the majority of kernel execution contexts under the | |
| 20 | +control of the scheduler and significantly increasing the number of preemption | |
| 21 | +points. Consequently, it reduces the latency between a high-priority task | |
| 22 | +becoming runnable and its actual execution on the CPU. | |
| 23 | + | |
| 24 | +Scheduling | |
| 25 | +========== | |
| 26 | + | |
| 27 | +The core principles of Linux scheduling and the associated user-space API are | |
| 28 | +documented in the man page sched(7) | |
| 29 | +`sched(7) <https://man7.org/linux/man-pages/man7/sched.7.html>`_. | |
| 30 | +By default, the Linux kernel uses the SCHED_OTHER scheduling policy. Under | |
| 31 | +this policy, a task is preempted when the scheduler determines that it has | |
| 32 | +consumed a fair share of CPU time relative to other runnable tasks. However, | |
| 33 | +the policy does not guarantee immediate preemption when a new SCHED_OTHER task | |
| 34 | +becomes runnable. The currently running task may continue executing. | |
| 35 | + | |
| 36 | +This behavior differs from that of real-time scheduling policies such as | |
| 37 | +SCHED_FIFO. When a task with a real-time policy becomes runnable, the | |
| 38 | +scheduler immediately selects it for execution if it has a higher priority than | |
| 39 | +the currently running task. The task continues to run until it voluntarily | |
| 40 | +yields the CPU, typically by blocking on an event. | |
| 41 | + | |
| 42 | +Sleeping spin locks | |
| 43 | +=================== | |
| 44 | + | |
| 45 | +The various lock types and their behavior under real-time configurations are | |
| 46 | +described in detail in Documentation/locking/locktypes.rst. | |
| 47 | +In a non-PREEMPT_RT configuration, a spinlock_t is acquired by first disabling | |
| 48 | +preemption and then actively spinning until the lock becomes available. Once | |
| 49 | +the lock is released, preemption is enabled. From a real-time perspective, | |
| 50 | +this approach is undesirable because disabling preemption prevents the | |
| 51 | +scheduler from switching to a higher-priority task, potentially increasing | |
| 52 | +latency. | |
| 53 | + | |
| 54 | +To address this, PREEMPT_RT replaces spinning locks with sleeping spin locks | |
| 55 | +that do not disable preemption. On PREEMPT_RT, spinlock_t is implemented using | |
| 56 | +rtmutex. Instead of spinning, a task attempting to acquire a contended lock | |
| 57 | +disables CPU migration, donates its priority to the lock owner (priority | |
| 58 | +inheritance), and voluntarily schedules out while waiting for the lock to | |
| 59 | +become available. | |
| 60 | + | |
| 61 | +Disabling CPU migration provides the same effect as disabling preemption, while | |
| 62 | +still allowing preemption and ensuring that the task continues to run on the | |
| 63 | +same CPU while holding a sleeping lock. | |
| 64 | + | |
| 65 | +Priority inheritance | |
| 66 | +==================== | |
| 67 | + | |
| 68 | +Lock types such as spinlock_t and mutex_t in a PREEMPT_RT enabled kernel are | |
| 69 | +implemented on top of rtmutex, which provides support for priority inheritance | |
| 70 | +(PI). When a task blocks on such a lock, the PI mechanism temporarily | |
| 71 | +propagates the blocked task’s scheduling parameters to the lock owner. | |
| 72 | + | |
| 73 | +For example, if a SCHED_FIFO task A blocks on a lock currently held by a | |
| 74 | +SCHED_OTHER task B, task A’s scheduling policy and priority are temporarily | |
| 75 | +inherited by task B. After this inheritance, task A is put to sleep while | |
| 76 | +waiting for the lock, and task B effectively becomes the highest-priority task | |
| 77 | +in the system. This allows B to continue executing, make progress, and | |
| 78 | +eventually release the lock. | |
| 79 | + | |
| 80 | +Once B releases the lock, it reverts to its original scheduling parameters, and | |
| 81 | +task A can resume execution. | |
| 82 | + | |
| 83 | +Threaded interrupts | |
| 84 | +=================== | |
| 85 | + | |
| 86 | +Interrupt handlers are another source of code that executes with preemption | |
| 87 | +disabled and outside the control of the scheduler. To bring interrupt handling | |
| 88 | +under scheduler control, PREEMPT_RT enforces threaded interrupt handlers. | |
| 89 | + | |
| 90 | +With forced threading, interrupt handling is split into two stages. The first | |
| 91 | +stage, the primary handler, is executed in IRQ context with interrupts disabled. | |
| 92 | +Its sole responsibility is to wake the associated threaded handler. The second | |
| 93 | +stage, the threaded handler, is the function passed to request_irq() as the | |
| 94 | +interrupt handler. It runs in process context, scheduled by the kernel. | |
| 95 | + | |
| 96 | +From waking the interrupt thread until threaded handling is completed, the | |
| 97 | +interrupt source is masked in the interrupt controller. This ensures that the | |
| 98 | +device interrupt remains pending but does not retrigger the CPU, allowing the | |
| 99 | +system to exit IRQ context and handle the interrupt in a scheduled thread. | |
| 100 | + | |
| 101 | +By default, the threaded handler executes with the SCHED_FIFO scheduling policy | |
| 102 | +and a priority of 50 (MAX_RT_PRIO / 2), which is midway between the minimum and | |
| 103 | +maximum real-time priorities. | |
| 104 | + | |
| 105 | +If the threaded interrupt handler raises any soft interrupts during its | |
| 106 | +execution, those soft interrupt routines are invoked after the threaded handler | |
| 107 | +completes, within the same thread. Preemption remains enabled during the | |
| 108 | +execution of the soft interrupt handler. | |
| 109 | + | |
| 110 | +Summary | |
| 111 | +======= | |
| 112 | + | |
| 113 | +By using sleeping locks and forced-threaded interrupts, PREEMPT_RT | |
| 114 | +significantly reduces sections of code where interrupts or preemption is | |
| 115 | +disabled, allowing the scheduler to preempt the current execution context and | |
| 116 | +switch to a higher-priority task. | |
| @@ -131,11 +131,11 @@ Here is an example workflow for AutoFDO kernel: · 130 unmodified lines | ||
| 131 | 131 | |
| 132 | 132 | For Zen3:: |
| 133 | 133 | |
| 134 | − $ cat proc/cpuinfo | grep " brs" | |
| 134 | + $ cat /proc/cpuinfo | grep " brs" | |
| 135 | 135 | |
| 136 | 136 | For Zen4:: |
| 137 | 137 | |
| 138 | − $ cat proc/cpuinfo | grep amd_lbr_v2 | |
| 138 | + $ cat /proc/cpuinfo | grep amd_lbr_v2 | |
| 139 | 139 | |
| 140 | 140 | The following command generated the perf data file:: |
| 141 | 141 | |
| @@ -29,6 +29,7 @@ Documentation/process/debugging/index.rst · 28 unmodified lines | ||
| 29 | 29 | ubsan |
| 30 | 30 | kmemleak |
| 31 | 31 | kcsan |
| 32 | + lkmm/index | |
| 32 | 33 | kfence |
| 33 | 34 | kselftest |
| 34 | 35 | kunit/index |
| @@ -5,7 +5,7 @@ The Kernel Test Anything Protocol (KTAP), version 1 · 4 unmodified lines | ||
| 5 | 5 | =================================================== |
| 6 | 6 | |
| 7 | 7 | TAP, or the Test Anything Protocol is a format for specifying test results used |
| 8 | −by a number of projects. It's website and specification are found at this `link | |
| 8 | +by a number of projects. Its website and specification are found at this `link | |
| 9 | 9 | <https://testanything.org/>`_. The Linux Kernel largely uses TAP output for test |
| 10 | 10 | results. However, Kernel testing frameworks have special needs for test results |
| 11 | 11 | which don't align with the original TAP specification. Thus, a "Kernel TAP" |
| @@ -20,6 +20,7 @@ machine-readable, whereas the diagnostic data is unstructured and is there to · 8 unmodified lines | ||
| 20 | 20 | aid human debugging. |
| 21 | 21 | |
| 22 | 22 | KTAP output is built from four different types of lines: |
| 23 | + | |
| 23 | 24 | - Version lines |
| 24 | 25 | - Plan lines |
| 25 | 26 | - Test case result lines |
| @@ -38,6 +39,7 @@ All KTAP-formatted results begin with a "version line" which specifies which · 12 unmodified lines | ||
| 38 | 39 | version of the (K)TAP standard the result is compliant with. |
| 39 | 40 | |
| 40 | 41 | For example: |
| 42 | + | |
| 41 | 43 | - "KTAP version 1" |
| 42 | 44 | - "TAP version 13" |
| 43 | 45 | - "TAP version 14" |
| @@ -276,6 +278,7 @@ Example KTAP output · 232 unmodified lines | ||
| 276 | 278 | This output defines the following hierarchy: |
| 277 | 279 | |
| 278 | 280 | A single test called "main_test", which fails, and has three subtests: |
| 281 | + | |
| 279 | 282 | - "example_test_1", which passes, and has one subtest: |
| 280 | 283 | |
| 281 | 284 | - "test_1", which passes, and outputs the diagnostic message "test_1: initializing test_1" |
| @@ -0,0 +1,11 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +Access Marking | |
| 4 | +-------------- | |
| 5 | + | |
| 6 | +Literal include of ``tools/memory-model/Documentation/access-marking.txt``. | |
| 7 | + | |
| 8 | +------------------------------------------------------------------ | |
| 9 | + | |
| 10 | +.. kernel-include:: tools/memory-model/Documentation/access-marking.txt | |
| 11 | + :literal: | |
| @@ -0,0 +1,11 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +Cheatsheet | |
| 4 | +---------- | |
| 5 | + | |
| 6 | +Literal include of ``tools/memory-model/Documentation/cheatsheet.txt``. | |
| 7 | + | |
| 8 | +------------------------------------------------------------------ | |
| 9 | + | |
| 10 | +.. kernel-include:: tools/memory-model/Documentation/cheatsheet.txt | |
| 11 | + :literal: | |
| @@ -0,0 +1,11 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +Control Dependencies | |
| 4 | +-------------------- | |
| 5 | + | |
| 6 | +Literal include of ``tools/memory-model/Documentation/control-dependencies.txt``. | |
| 7 | + | |
| 8 | +------------------------------------------------------------------ | |
| 9 | + | |
| 10 | +.. kernel-include:: tools/memory-model/Documentation/control-dependencies.txt | |
| 11 | + :literal: | |
| @@ -0,0 +1,11 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +Explanation | |
| 4 | +----------- | |
| 5 | + | |
| 6 | +Literal include of ``tools/memory-model/Documentation/explanation.txt``. | |
| 7 | + | |
| 8 | +------------------------------------------------------------------ | |
| 9 | + | |
| 10 | +.. kernel-include:: tools/memory-model/Documentation/explanation.txt | |
| 11 | + :literal: | |
| @@ -0,0 +1,11 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +Glossary | |
| 4 | +-------- | |
| 5 | + | |
| 6 | +Literal include of ``tools/memory-model/Documentation/glossary.txt``. | |
| 7 | + | |
| 8 | +------------------------------------------------------------------ | |
| 9 | + | |
| 10 | +.. kernel-include:: tools/memory-model/Documentation/glossary.txt | |
| 11 | + :literal: | |
| @@ -0,0 +1,11 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +herd-representation | |
| 4 | +------------------- | |
| 5 | + | |
| 6 | +Literal include of ``tools/memory-model/Documentation/herd-representation.txt``. | |
| 7 | + | |
| 8 | +------------------------------------------------------------------ | |
| 9 | + | |
| 10 | +.. kernel-include:: tools/memory-model/Documentation/herd-representation.txt | |
| 11 | + :literal: | |
| @@ -0,0 +1,21 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +Documentation | |
| 4 | +============= | |
| 5 | + | |
| 6 | +.. toctree:: | |
| 7 | + :maxdepth: 1 | |
| 8 | + | |
| 9 | + readme | |
| 10 | + simple | |
| 11 | + ordering | |
| 12 | + litmus-tests | |
| 13 | + locking | |
| 14 | + recipes | |
| 15 | + control-dependencies | |
| 16 | + access-marking | |
| 17 | + cheatsheet | |
| 18 | + explanation | |
| 19 | + herd-representation | |
| 20 | + glossary | |
| 21 | + references | |
| @@ -0,0 +1,11 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +Litmus Tests | |
| 4 | +------------ | |
| 5 | + | |
| 6 | +Literal include of ``tools/memory-model/Documentation/litmus-tests.txt``. | |
| 7 | + | |
| 8 | +------------------------------------------------------------------ | |
| 9 | + | |
| 10 | +.. kernel-include:: tools/memory-model/Documentation/litmus-tests.txt | |
| 11 | + :literal: | |
| @@ -0,0 +1,11 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +Locking | |
| 4 | +------- | |
| 5 | + | |
| 6 | +Literal include of ``tools/memory-model/Documentation/locking.txt``. | |
| 7 | + | |
| 8 | +------------------------------------------------------------------ | |
| 9 | + | |
| 10 | +.. kernel-include:: tools/memory-model/Documentation/locking.txt | |
| 11 | + :literal: | |
| @@ -0,0 +1,11 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +Ordering | |
| 4 | +-------- | |
| 5 | + | |
| 6 | +Literal include of ``tools/memory-model/Documentation/ordering.txt``. | |
| 7 | + | |
| 8 | +------------------------------------------------------------------ | |
| 9 | + | |
| 10 | +.. kernel-include:: tools/memory-model/Documentation/ordering.txt | |
| 11 | + :literal: | |
| @@ -0,0 +1,11 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +README (for LKMM Documentation) | |
| 4 | +------------------------------- | |
| 5 | + | |
| 6 | +Literal include of ``tools/memory-model/Documentation/README``. | |
| 7 | + | |
| 8 | +------------------------------------------------------------------ | |
| 9 | + | |
| 10 | +.. kernel-include:: tools/memory-model/Documentation/README | |
| 11 | + :literal: | |
| @@ -0,0 +1,11 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +Recipes | |
| 4 | +------- | |
| 5 | + | |
| 6 | +Literal include of ``tools/memory-model/Documentation/recipes.txt``. | |
| 7 | + | |
| 8 | +------------------------------------------------------------------ | |
| 9 | + | |
| 10 | +.. kernel-include:: tools/memory-model/Documentation/recipes.txt | |
| 11 | + :literal: | |
| @@ -0,0 +1,11 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +References | |
| 4 | +---------- | |
| 5 | + | |
| 6 | +Literal include of ``tools/memory-model/Documentation/references.txt``. | |
| 7 | + | |
| 8 | +------------------------------------------------------------------ | |
| 9 | + | |
| 10 | +.. kernel-include:: tools/memory-model/Documentation/references.txt | |
| 11 | + :literal: | |
| @@ -0,0 +1,11 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +Simple | |
| 4 | +------ | |
| 5 | + | |
| 6 | +Literal include of ``tools/memory-model/Documentation/simple.txt``. | |
| 7 | + | |
| 8 | +------------------------------------------------------------------ | |
| 9 | + | |
| 10 | +.. kernel-include:: tools/memory-model/Documentation/simple.txt | |
| 11 | + :literal: | |
| @@ -0,0 +1,15 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +============================================ | |
| 4 | +Linux Kernel Memory Consistency Model (LKMM) | |
| 5 | +============================================ | |
| 6 | + | |
| 7 | +This section literally renders documents under ``tools/memory-model/`` | |
| 8 | +and ``tools/memory-model/Documentation/``, which are maintained in | |
| 9 | +the *pure* plain text form. | |
| 10 | + | |
| 11 | +.. toctree:: | |
| 12 | + :maxdepth: 2 | |
| 13 | + | |
| 14 | + readme | |
| 15 | + docs/index | |
| @@ -0,0 +1,11 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 3 | +README (for LKMM) | |
| 4 | +================= | |
| 5 | + | |
| 6 | +Literal include of ``tools/memory-model/README``. | |
| 7 | + | |
| 8 | +------------------------------------------------------------ | |
| 9 | + | |
| 10 | +.. kernel-include:: tools/memory-model/README | |
| 11 | + :literal: | |
| @@ -66,7 +66,7 @@ I. For patch submitters · 65 unmodified lines | ||
| 66 | 66 | any DTS patches, regardless whether using existing or new bindings, should |
| 67 | 67 | be placed at the end of patchset to indicate no dependency of drivers on |
| 68 | 68 | the DTS. DTS will be anyway applied through separate tree or branch, so |
| 69 | − different order would indicate the serie is non-bisectable. | |
| 69 | + different order would indicate the series is non-bisectable. | |
| 70 | 70 | |
| 71 | 71 | If a driver subsystem maintainer prefers to apply entire set, instead of |
| 72 | 72 | their relevant portion of patchset, please split the DTS patches into |
| @@ -22,7 +22,7 @@ The basic interaction protocol, similar to PCIe configuration mechanisms. · 21 unmodified lines | ||
| 22 | 22 | Typically used for initialization, configuration, and I/O access for anything |
| 23 | 23 | other than memory (CXL.mem) or cache (CXL.cache) operations. |
| 24 | 24 | |
| 25 | −The Linux CXL driver exposes access to .io functionalty via the various sysfs | |
| 25 | +The Linux CXL driver exposes access to .io functionality via the various sysfs | |
| 26 | 26 | interfaces and /dev/cxl/ devices (which exposes direct access to device |
| 27 | 27 | mailboxes). |
| 28 | 28 | |
| @@ -10,7 +10,7 @@ has a single CXL memory expander with a 4GB of memory. · 9 unmodified lines | ||
| 10 | 10 | Things to note: |
| 11 | 11 | |
| 12 | 12 | * Cross-Bridge interleave is not being used. |
| 13 | −* The expanders are in two separate but adjascent memory regions. | |
| 13 | +* The expanders are in two separate but adjacent memory regions. | |
| 14 | 14 | * This CEDT/SRAT describes one node per device |
| 15 | 15 | * The expanders have the same performance and will be in the same memory tier. |
| 16 | 16 | |
| @@ -16,7 +16,7 @@ Bus-Independent Device Accesses · 15 unmodified lines | ||
| 16 | 16 | Introduction |
| 17 | 17 | ============ |
| 18 | 18 | |
| 19 | −Linux provides an API which abstracts performing IO across all busses | |
| 19 | +Linux provides an API which abstracts performing IO across all buses | |
| 20 | 20 | and devices, allowing device drivers to be written independently of bus |
| 21 | 21 | type. |
| 22 | 22 | |
| @@ -71,7 +71,7 @@ can be compiler optimised, you can use __readb() and friends to · 48 unmodified lines | ||
| 71 | 71 | indicate the relaxed ordering. Use this with care. |
| 72 | 72 | |
| 73 | 73 | While the basic functions are defined to be synchronous with respect to |
| 74 | −each other and ordered with respect to each other the busses the devices | |
| 74 | +each other and ordered with respect to each other the buses the devices | |
| 75 | 75 | sit on may themselves have asynchronicity. In particular many authors |
| 76 | 76 | are burned by the fact that PCI bus writes are posted asynchronously. A |
| 77 | 77 | driver author must issue a read from the same device to ensure that |
| @@ -22,7 +22,7 @@ uniformity across the different bus types. · 21 unmodified lines | ||
| 22 | 22 | |
| 23 | 23 | The current driver model provides a common, uniform data model for describing |
| 24 | 24 | a bus and the devices that can appear under the bus. The unified bus |
| 25 | −model includes a set of common attributes which all busses carry, and a set | |
| 25 | +model includes a set of common attributes which all buses carry, and a set | |
| 26 | 26 | of common callbacks, such as device discovery during bus probing, bus |
| 27 | 27 | shutdown, bus power management, etc. |
| 28 | 28 | |
| @@ -4,7 +4,7 @@ Platform Devices and Drivers · 3 unmodified lines | ||
| 4 | 4 | |
| 5 | 5 | See <linux/platform_device.h> for the driver model interface to the |
| 6 | 6 | platform bus: platform_device, and platform_driver. This pseudo-bus |
| 7 | −is used to connect devices on busses with minimal infrastructure, | |
| 7 | +is used to connect devices on buses with minimal infrastructure, | |
| 8 | 8 | like those used to integrate peripherals on many system-on-chip |
| 9 | 9 | processors, or some "legacy" PC interconnects; as opposed to large |
| 10 | 10 | formally specified ones like PCI or USB. |
| @@ -8,9 +8,9 @@ This document groups random notes about porting EISA drivers to the · 7 unmodified lines | ||
| 8 | 8 | new EISA/sysfs API. |
| 9 | 9 | |
| 10 | 10 | Starting from version 2.5.59, the EISA bus is almost given the same |
| 11 | −status as other much more mainstream busses such as PCI or USB. This | |
| 11 | +status as other much more mainstream buses such as PCI or USB. This | |
| 12 | 12 | has been possible through sysfs, which defines a nice enough set of |
| 13 | −abstractions to manage busses, devices and drivers. | |
| 13 | +abstractions to manage buses, devices and drivers. | |
| 14 | 14 | |
| 15 | 15 | Although the new API is quite simple to use, converting existing |
| 16 | 16 | drivers to the new infrastructure is not an easy task (mostly because |
| @@ -205,7 +205,7 @@ Random notes · 188 unmodified lines | ||
| 205 | 205 | Converting an EISA driver to the new API mostly involves *deleting* |
| 206 | 206 | code (since probing is now in the core EISA code). Unfortunately, most |
| 207 | 207 | drivers share their probing routine between ISA, and EISA. Special |
| 208 | −care must be taken when ripping out the EISA code, so other busses | |
| 208 | +care must be taken when ripping out the EISA code, so other buses | |
| 209 | 209 | won't suffer from these surgical strikes... |
| 210 | 210 | |
| 211 | 211 | You *must not* expect any EISA device to be detected when returning |
| @@ -165,8 +165,8 @@ The first thing attached to an HDR command is the HDR mode. There are currently · 164 unmodified lines | ||
| 165 | 165 | for more details): |
| 166 | 166 | |
| 167 | 167 | * HDR-DDR: Double Data Rate mode |
| 168 | −* HDR-TSP: Ternary Symbol Pure. Only usable on busses with no I2C devices | |
| 169 | −* HDR-TSL: Ternary Symbol Legacy. Usable on busses with I2C devices | |
| 168 | +* HDR-TSP: Ternary Symbol Pure. Only usable on buses with no I2C devices | |
| 169 | +* HDR-TSL: Ternary Symbol Legacy. Usable on buses with I2C devices | |
| 170 | 170 | |
| 171 | 171 | When sending an HDR command, the whole bus has to enter HDR mode, which is done |
| 172 | 172 | using a broadcast CCC command. |
| @@ -617,12 +617,12 @@ Note that the address you give here is the I2C address, not the IPMI · 616 unmodified lines | ||
| 617 | 617 | address. So if you want your MC address to be 0x60, you put 0x30 |
| 618 | 618 | here. See the I2C driver info for more details. |
| 619 | 619 | |
| 620 | −Command bridging to other IPMB busses through this interface does not | |
| 620 | +Command bridging to other IPMB buses through this interface does not | |
| 621 | 621 | work. The receive message queue is not implemented, by design. There |
| 622 | 622 | is only one receive message queue on a BMC, and that is meant for the |
| 623 | 623 | host drivers, not something on the IPMB bus. |
| 624 | 624 | |
| 625 | −A BMC may have multiple IPMB busses, which bus your device sits on | |
| 625 | +A BMC may have multiple IPMB buses, which bus your device sits on | |
| 626 | 626 | depends on how the system is wired. You can fetch the channels with |
| 627 | 627 | "ipmitool channel info <n>" where <n> is the channel, with the |
| 628 | 628 | channels being 0-7 and try the IPMB channels. |
| @@ -12,7 +12,7 @@ CSI-2 receiver in an SoC. · 11 unmodified lines | ||
| 12 | 12 | Bus types |
| 13 | 13 | --------- |
| 14 | 14 | |
| 15 | −The following busses are the most common. This section discusses these two only. | |
| 15 | +The following buses are the most common. This section discusses these two only. | |
| 16 | 16 | |
| 17 | 17 | MIPI CSI-2 |
| 18 | 18 | ^^^^^^^^^^ |
| @@ -36,7 +36,7 @@ Transmitter drivers · 17 unmodified lines | ||
| 36 | 36 | |
| 37 | 37 | Transmitter drivers generally need to provide the receiver drivers with the |
| 38 | 38 | configuration of the transmitter. What is required depends on the type of the |
| 39 | −bus. These are common for both busses. | |
| 39 | +bus. These are common for both buses. | |
| 40 | 40 | |
| 41 | 41 | Media bus pixel code |
| 42 | 42 | ^^^^^^^^^^^^^^^^^^^^ |
| @@ -230,7 +230,7 @@ LIBNVDIMM/LIBNDCTL: Bus · 229 unmodified lines | ||
| 230 | 230 | A bus has a 1:1 relationship with an NFIT. The current expectation for |
| 231 | 231 | ACPI based systems is that there is only ever one platform-global NFIT. |
| 232 | 232 | That said, it is trivial to register multiple NFITs, the specification |
| 233 | −does not preclude it. The infrastructure supports multiple busses and | |
| 233 | +does not preclude it. The infrastructure supports multiple buses and | |
| 234 | 234 | we use this capability to test multiple NFIT configurations in the unit |
| 235 | 235 | test. |
| 236 | 236 | |
| @@ -1202,22 +1202,24 @@ default state like this: · 1201 unmodified lines | ||
| 1202 | 1202 | { |
| 1203 | 1203 | /* Allocate a state holder named "foo" etc */ |
| 1204 | 1204 | struct foo_state *foo = ...; |
| 1205 | + int ret; | |
| 1205 | 1206 | |
| 1206 | 1207 | foo->p = devm_pinctrl_get(&device); |
| 1207 | 1208 | if (IS_ERR(foo->p)) { |
| 1208 | − /* FIXME: clean up "foo" here */ | |
| 1209 | − return PTR_ERR(foo->p); | |
| 1209 | + ret = PTR_ERR(foo->p); | |
| 1210 | + foo->p = NULL; | |
| 1211 | + return ret; | |
| 1210 | 1212 | } |
| 1211 | 1213 | |
| 1212 | 1214 | foo->s = pinctrl_lookup_state(foo->p, PINCTRL_STATE_DEFAULT); |
| 1213 | 1215 | if (IS_ERR(foo->s)) { |
| 1214 | − /* FIXME: clean up "foo" here */ | |
| 1216 | + devm_pinctrl_put(foo->p); | |
| 1215 | 1217 | return PTR_ERR(foo->s); |
| 1216 | 1218 | } |
| 1217 | 1219 | |
| 1218 | 1220 | ret = pinctrl_select_state(foo->p, foo->s); |
| 1219 | 1221 | if (ret < 0) { |
| 1220 | − /* FIXME: clean up "foo" here */ | |
| 1222 | + devm_pinctrl_put(foo->p); | |
| 1221 | 1223 | return ret; |
| 1222 | 1224 | } |
| 1223 | 1225 | } |
| @@ -255,7 +255,7 @@ get registered: a child can never be registered, probed or resumed before · 254 unmodified lines | ||
| 255 | 255 | its parent; and can't be removed or suspended after that parent. |
| 256 | 256 | |
| 257 | 257 | The policy is that the device hierarchy should match hardware bus topology. |
| 258 | −[Or at least the control bus, for devices which use multiple busses.] | |
| 258 | +[Or at least the control bus, for devices which use multiple buses.] | |
| 259 | 259 | In particular, this means that a device registration may fail if the parent of |
| 260 | 260 | the device is suspending (i.e. has been chosen by the PM core as the next |
| 261 | 261 | device to suspend) or has already suspended, as well as after all of the other |
| @@ -493,7 +493,7 @@ states, like S3). · 231 unmodified lines | ||
| 493 | 493 | |
| 494 | 494 | Drivers must also be prepared to notice that the device has been removed |
| 495 | 495 | while the system was powered down, whenever that's physically possible. |
| 496 | −PCMCIA, MMC, USB, Firewire, SCSI, and even IDE are common examples of busses | |
| 496 | +PCMCIA, MMC, USB, Firewire, SCSI, and even IDE are common examples of buses | |
| 497 | 497 | where common Linux platforms will see such removal. Details of how drivers |
| 498 | 498 | will notice and handle such removals are currently bus-specific, and often |
| 499 | 499 | involve a separate thread. |
| @@ -18,7 +18,7 @@ optical drives, test equipment, and medical devices) to a host computer. · 17 unmodified lines | ||
| 18 | 18 | |
| 19 | 19 | Although the old parallel (fast/wide/ultra) SCSI bus has largely fallen |
| 20 | 20 | out of use, the SCSI command set is more widely used than ever to |
| 21 | −communicate with devices over a number of different busses. | |
| 21 | +communicate with devices over a number of different buses. | |
| 22 | 22 | |
| 23 | 23 | The `SCSI protocol <https://www.t10.org/scsi-3.htm>`__ is a big-endian |
| 24 | 24 | peer-to-peer packet based protocol. SCSI commands are 6, 10, 12, or 16 |
| @@ -286,7 +286,7 @@ Parallel SCSI (SPI) transport class · 261 unmodified lines | ||
| 286 | 286 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 287 | 287 | |
| 288 | 288 | The file drivers/scsi/scsi_transport_spi.c defines transport |
| 289 | −attributes for traditional (fast/wide/ultra) SCSI busses. | |
| 289 | +attributes for traditional (fast/wide/ultra) SCSI buses. | |
| 290 | 290 | |
| 291 | 291 | .. kernel-doc:: drivers/scsi/scsi_transport_spi.c |
| 292 | 292 | :export: |
| @@ -13,7 +13,7 @@ additional chipselect line is usually active-low (nCS); four signals are · 12 unmodified lines | ||
| 13 | 13 | normally used for each peripheral, plus sometimes an interrupt. |
| 14 | 14 | |
| 15 | 15 | The SPI bus facilities listed here provide a generalized interface to |
| 16 | −declare SPI busses and devices, manage them according to the standard | |
| 16 | +declare SPI buses and devices, manage them according to the standard | |
| 17 | 17 | Linux driver model, and perform input/output operations. At this time, |
| 18 | 18 | only "master" side interfaces are supported, where Linux talks to SPI |
| 19 | 19 | peripherals and does not implement such a peripheral itself. (Interfaces |
| @@ -28,13 +28,13 @@ changed into it. · 27 unmodified lines | ||
| 28 | 28 | delay of changing temperature. However, this node only uses same delay |
| 29 | 29 | of real sensing time, 938us.) |
| 30 | 30 | |
| 31 | −Exynos emulation mode requires synchronous of value changing and | |
| 32 | −enabling. It means when you want to update the any value of delay or | |
| 33 | −next temperature, then you have to enable emulation mode at the same | |
| 34 | −time. (Or you have to keep the mode enabling.) If you don't, it fails to | |
| 35 | −change the value to updated one and just use last succeessful value | |
| 36 | −repeatedly. That's why this node gives users the right to change | |
| 37 | −termerpature only. Just one interface makes it more simply to use. | |
| 31 | +Exynos emulation mode requires that value changes and enabling are performed | |
| 32 | +synchronously. This means that when you want to update any value, such as the | |
| 33 | +delay or the next temperature, you must enable emulation mode at the same | |
| 34 | +time (or keep the mode enabled). If you do not, the value will fail to update | |
| 35 | +and the last successful value will continue to be used. For this reason, | |
| 36 | +this node only allows users to change the temperature. Providing a single | |
| 37 | +interface makes it simpler to use. | |
| 38 | 38 | |
| 39 | 39 | Disabling emulation mode only requires writing value 0 to sysfs node. |
| 40 | 40 | |
| @@ -5,7 +5,7 @@ Linux Hotplugging · 4 unmodified lines | ||
| 5 | 5 | ================= |
| 6 | 6 | |
| 7 | 7 | |
| 8 | −In hotpluggable busses like USB (and Cardbus PCI), end-users plug devices | |
| 8 | +In hotpluggable buses like USB (and Cardbus PCI), end-users plug devices | |
| 9 | 9 | into the bus with power on. In most cases, users expect the devices to become |
| 10 | 10 | immediately usable. That means the system must do many things, including: |
| 11 | 11 | |
| @@ -3,6 +3,7 @@ Linux USB API · 2 unmodified lines | ||
| 3 | 3 | ============= |
| 4 | 4 | |
| 5 | 5 | .. toctree:: |
| 6 | + :maxdepth: 1 | |
| 6 | 7 | |
| 7 | 8 | usb |
| 8 | 9 | gadget |
| @@ -13,7 +13,7 @@ structure, with the host as the root (the system's master), hubs as · 12 unmodified lines | ||
| 13 | 13 | interior nodes, and peripherals as leaves (and slaves). Modern PCs |
| 14 | 14 | support several such trees of USB devices, usually |
| 15 | 15 | a few USB 3.0 (5 GBit/s) or USB 3.1 (10 GBit/s) and some legacy |
| 16 | −USB 2.0 (480 MBit/s) busses just in case. | |
| 16 | +USB 2.0 (480 MBit/s) buses just in case. | |
| 17 | 17 | |
| 18 | 18 | That master/slave asymmetry was designed-in for a number of reasons, one |
| 19 | 19 | being ease of use. It is not physically possible to mistake upstream and |
| @@ -42,7 +42,7 @@ two. One is intended for *general-purpose* drivers (exposed through · 22 unmodified lines | ||
| 42 | 42 | driver frameworks), and the other is for drivers that are *part of the |
| 43 | 43 | core*. Such core drivers include the *hub* driver (which manages trees |
| 44 | 44 | of USB devices) and several different kinds of *host controller |
| 45 | −drivers*, which control individual busses. | |
| 45 | +drivers*, which control individual buses. | |
| 46 | 46 | |
| 47 | 47 | The device model seen by USB drivers is relatively complex. |
| 48 | 48 | |
| @@ -39,11 +39,13 @@ Also, you will need to select at least one compiled-in font, but if · 38 unmodified lines | ||
| 39 | 39 | you don't do anything, the kernel configuration tool will select one for you, |
| 40 | 40 | usually an 8x16 font. |
| 41 | 41 | |
| 42 | −GOTCHA: A common bug report is enabling the framebuffer without enabling the | |
| 43 | −framebuffer console. Depending on the driver, you may get a blanked or | |
| 44 | −garbled display, but the system still boots to completion. If you are | |
| 45 | −fortunate to have a driver that does not alter the graphics chip, then you | |
| 46 | −will still get a VGA console. | |
| 42 | +.. admonition:: GOTCHA | |
| 43 | + | |
| 44 | + A common bug report is enabling the framebuffer without enabling the | |
| 45 | + framebuffer console. Depending on the driver, you may get a blanked or | |
| 46 | + garbled display, but the system still boots to completion. If you are | |
| 47 | + fortunate to have a driver that does not alter the graphics chip, then you | |
| 48 | + will still get a VGA console. | |
| 47 | 49 | |
| 48 | 50 | B. Loading |
| 49 | 51 | ========== |
| @@ -74,6 +76,7 @@ Possible scenarios: · 24 unmodified lines | ||
| 74 | 76 | over the console. |
| 75 | 77 | |
| 76 | 78 | C. Boot options |
| 79 | +=============== | |
| 77 | 80 | |
| 78 | 81 | The framebuffer console has several, largely unknown, boot options |
| 79 | 82 | that can change its behavior. |
| @@ -116,9 +119,10 @@ C. Boot options · 36 unmodified lines | ||
| 116 | 119 | outside the given range will still be controlled by the standard |
| 117 | 120 | console driver. |
| 118 | 121 | |
| 119 | − NOTE: For x86 machines, the standard console is the VGA console which | |
| 120 | − is typically located on the same video card. Thus, the consoles that | |
| 121 | − are controlled by the VGA console will be garbled. | |
| 122 | + .. note:: | |
| 123 | + For x86 machines, the standard console is the VGA console which | |
| 124 | + is typically located on the same video card. Thus, the consoles that | |
| 125 | + are controlled by the VGA console will be garbled. | |
| 122 | 126 | |
| 123 | 127 | 4. fbcon=rotate:<n> |
| 124 | 128 | |
| @@ -140,10 +144,11 @@ C. Boot options · 15 unmodified lines | ||
| 140 | 144 | Console rotation will only become available if Framebuffer Console |
| 141 | 145 | Rotation support is compiled in your kernel. |
| 142 | 146 | |
| 143 | − NOTE: This is purely console rotation. Any other applications that | |
| 144 | − use the framebuffer will remain at their 'normal' orientation. | |
| 145 | − Actually, the underlying fb driver is totally ignorant of console | |
| 146 | − rotation. | |
| 147 | + .. note:: | |
| 148 | + This is purely console rotation. Any other applications that | |
| 149 | + use the framebuffer will remain at their 'normal' orientation. | |
| 150 | + Actually, the underlying fb driver is totally ignorant of console | |
| 151 | + rotation. | |
| 147 | 152 | |
| 148 | 153 | 5. fbcon=margin:<color> |
| 149 | 154 | |
| @@ -172,7 +177,8 @@ C. Boot options · 22 unmodified lines | ||
| 172 | 177 | The value 'n' overrides the number of bootup logos. 0 disables the |
| 173 | 178 | logo, and -1 gives the default which is the number of online CPUs. |
| 174 | 179 | |
| 175 | −C. Attaching, Detaching and Unloading | |
| 180 | +D. Attaching, Detaching and Unloading | |
| 181 | +===================================== | |
| 176 | 182 | |
| 177 | 183 | Before going on to how to attach, detach and unload the framebuffer console, an |
| 178 | 184 | illustration of the dependencies may help. |
| @@ -249,11 +255,11 @@ restored properly. The following is one of the several methods that you can do: · 70 unmodified lines | ||
| 249 | 255 | echo 1 > /sys/class/vtconsole/vtcon1/bind |
| 250 | 256 | |
| 251 | 257 | 8. Once fbcon is unbound, all drivers registered to the system will also |
| 252 | −become unbound. This means that fbcon and individual framebuffer drivers | |
| 253 | −can be unloaded or reloaded at will. Reloading the drivers or fbcon will | |
| 254 | −automatically bind the console, fbcon and the drivers together. Unloading | |
| 255 | −all the drivers without unloading fbcon will make it impossible for the | |
| 256 | −console to bind fbcon. | |
| 258 | + become unbound. This means that fbcon and individual framebuffer drivers | |
| 259 | + can be unloaded or reloaded at will. Reloading the drivers or fbcon will | |
| 260 | + automatically bind the console, fbcon and the drivers together. Unloading | |
| 261 | + all the drivers without unloading fbcon will make it impossible for the | |
| 262 | + console to bind fbcon. | |
| 257 | 263 | |
| 258 | 264 | Notes for vesafb users: |
| 259 | 265 | ======================= |
| @@ -7,7 +7,7 @@ · 6 unmodified lines | ||
| 7 | 7 | | arch |status| |
| 8 | 8 | ----------------------- |
| 9 | 9 | | alpha: | TODO | |
| 10 | − | arc: | TODO | | |
| 10 | + | arc: | ok | | |
| 11 | 11 | | arm: | ok | |
| 12 | 12 | | arm64: | ok | |
| 13 | 13 | | csky: | TODO | |
| @@ -18,7 +18,7 @@ · 4 unmodified lines | ||
| 18 | 18 | | mips: | ok | |
| 19 | 19 | | nios2: | TODO | |
| 20 | 20 | | openrisc: | TODO | |
| 21 | − | parisc: | TODO | | |
| 21 | + | parisc: | ok | | |
| 22 | 22 | | powerpc: | ok | |
| 23 | 23 | | riscv: | ok | |
| 24 | 24 | | s390: | ok | |
| @@ -20,7 +20,7 @@ · 19 unmodified lines | ||
| 20 | 20 | | openrisc: | N/A | |
| 21 | 21 | | parisc: | TODO | |
| 22 | 22 | | powerpc: | TODO | |
| 23 | − | riscv: | TODO | | |
| 23 | + | riscv: | ok | | |
| 24 | 24 | | s390: | ok | |
| 25 | 25 | | sh: | N/A | |
| 26 | 26 | | sparc: | TODO | |
| @@ -24,7 +24,7 @@ · 23 unmodified lines | ||
| 24 | 24 | | s390: | ok | |
| 25 | 25 | | sh: | TODO | |
| 26 | 26 | | sparc: | TODO | |
| 27 | − | um: | TODO | | |
| 27 | + | um: | ok | | |
| 28 | 28 | | x86: | ok | |
| 29 | 29 | | xtensa: | TODO | |
| 30 | 30 | ----------------------- |
| @@ -24,7 +24,7 @@ · 23 unmodified lines | ||
| 24 | 24 | | s390: | ok | |
| 25 | 25 | | sh: | ok | |
| 26 | 26 | | sparc: | ok | |
| 27 | − | um: | TODO | | |
| 27 | + | um: | ok | | |
| 28 | 28 | | x86: | ok | |
| 29 | 29 | | xtensa: | ok | |
| 30 | 30 | ----------------------- |
| @@ -17,7 +17,7 @@ · 16 unmodified lines | ||
| 17 | 17 | | microblaze: | TODO | |
| 18 | 18 | | mips: | ok | |
| 19 | 19 | | nios2: | TODO | |
| 20 | − | openrisc: | TODO | | |
| 20 | + | openrisc: | ok | | |
| 21 | 21 | | parisc: | ok | |
| 22 | 22 | | powerpc: | ok | |
| 23 | 23 | | riscv: | ok | |
| @@ -18,7 +18,7 @@ · 17 unmodified lines | ||
| 18 | 18 | | mips: | ok | |
| 19 | 19 | | nios2: | ok | |
| 20 | 20 | | openrisc: | ok | |
| 21 | − | parisc: | TODO | | |
| 21 | + | parisc: | ok | | |
| 22 | 22 | | powerpc: | ok | |
| 23 | 23 | | riscv: | ok | |
| 24 | 24 | | s390: | ok | |
| @@ -116,7 +116,7 @@ cache_strategy=%s Select a strategy for cached decompression from now on: · 115 unmodified lines | ||
| 116 | 116 | cluster for further reading. It still does |
| 117 | 117 | in-place I/O decompression for the rest |
| 118 | 118 | compressed physical clusters; |
| 119 | − readaround Cache the both ends of incomplete compressed | |
| 119 | + readaround Cache both ends of incomplete compressed | |
| 120 | 120 | physical clusters for further reading. |
| 121 | 121 | It still does in-place I/O decompression |
| 122 | 122 | for the rest compressed physical clusters. |
| @@ -14,7 +14,7 @@ I/O) on regular files with extents, provided the underlying storage device · 13 unmodified lines | ||
| 14 | 14 | supports hardware atomic writes. This is supported in the following two ways: |
| 15 | 15 | |
| 16 | 16 | 1. **Single-fsblock Atomic Writes**: |
| 17 | − EXT4's supports atomic write operations with a single filesystem block since | |
| 17 | + EXT4 supports atomic write operations with a single filesystem block since | |
| 18 | 18 | v6.13. In this the atomic write unit minimum and maximum sizes are both set |
| 19 | 19 | to filesystem blocksize. |
| 20 | 20 | e.g. doing atomic write of 16KB with 16KB filesystem blocksize on 64KB |
| @@ -50,7 +50,7 @@ Multi-fsblock Implementation Details · 29 unmodified lines | ||
| 50 | 50 | |
| 51 | 51 | The bigalloc feature changes ext4 to allocate in units of multiple filesystem |
| 52 | 52 | blocks, also known as clusters. With bigalloc each bit within block bitmap |
| 53 | −represents cluster (power of 2 number of blocks) rather than individual | |
| 53 | +represents a cluster (power of 2 number of blocks) rather than individual | |
| 54 | 54 | filesystem blocks. |
| 55 | 55 | EXT4 supports multi-fsblock atomic writes with bigalloc, subject to the |
| 56 | 56 | following constraints. The minimum atomic write size is the larger of the fs |
| @@ -189,7 +189,7 @@ The write must be aligned to the filesystem's block size and not exceed the · 132 unmodified lines | ||
| 189 | 189 | filesystem's maximum atomic write unit size. |
| 190 | 190 | See ``generic_atomic_write_valid()`` for more details. |
| 191 | 191 | |
| 192 | −``statx()`` system call with ``STATX_WRITE_ATOMIC`` flag can provides following | |
| 192 | +``statx()`` system call with ``STATX_WRITE_ATOMIC`` flag can provide following | |
| 193 | 193 | details: |
| 194 | 194 | |
| 195 | 195 | * ``stx_atomic_write_unit_min``: Minimum size of an atomic write request. |
| @@ -105,7 +105,7 @@ go_unlocked Yes No · 104 unmodified lines | ||
| 105 | 105 | Operations must not drop either the bit lock or the spinlock |
| 106 | 106 | if its held on entry. go_dump and do_demote_ok must never block. |
| 107 | 107 | Note that go_dump will only be called if the glock's state |
| 108 | − indicates that it is caching uptodate data. | |
| 108 | + indicates that it is caching up-to-date data. | |
| 109 | 109 | |
| 110 | 110 | Glock locking order within GFS2: |
| 111 | 111 | |
| @@ -65,7 +65,7 @@ are case sensitive, so for example when you create a file FOO, you can use · 64 unmodified lines | ||
| 65 | 65 | 'cat FOO', 'cat Foo', 'cat foo' or 'cat F*' but not 'cat f*'. Note, that you |
| 66 | 66 | also won't be able to compile linux kernel (and maybe other things) on HPFS |
| 67 | 67 | because kernel creates different files with names like bootsect.S and |
| 68 | −bootsect.s. When searching for file thats name has characters >= 128, codepages | |
| 68 | +bootsect.s. When searching for file whose name has characters >= 128, codepages | |
| 69 | 69 | are used - see below. |
| 70 | 70 | OS/2 ignores dots and spaces at the end of file name, so this driver does as |
| 71 | 71 | well. If you create 'a. ...', the file 'a' will be created, but you can still |
| @@ -321,7 +321,7 @@ The fields are as follows: · 320 unmodified lines | ||
| 321 | 321 | - ``writeback_submit``: Submit the previous built writeback context. |
| 322 | 322 | Block based file systems should use the iomap_ioend_writeback_submit |
| 323 | 323 | helper, other file system can implement their own. |
| 324 | − File systems can optionall to hook into writeback bio submission. | |
| 324 | + File systems can optionally hook into writeback bio submission. | |
| 325 | 325 | This might include pre-write space accounting updates, or installing |
| 326 | 326 | a custom ``->bi_end_io`` function for internal purposes, such as |
| 327 | 327 | deferring the ioend completion to a workqueue to run metadata update |
| @@ -58,33 +58,33 @@ inode, fixing inode and setting the size of result record history. · 57 unmodified lines | ||
| 58 | 58 | # echo "<inode>" > /sys/fs/ocfs2/<devname>/filecheck/check |
| 59 | 59 | # cat /sys/fs/ocfs2/<devname>/filecheck/check |
| 60 | 60 | |
| 61 | −The output is like this:: | |
| 61 | + The output is like this:: | |
| 62 | 62 | |
| 63 | 63 | INO DONE ERROR |
| 64 | 64 | 39502 1 GENERATION |
| 65 | 65 | |
| 66 | − <INO> lists the inode numbers. | |
| 67 | − <DONE> indicates whether the operation has been finished. | |
| 68 | − <ERROR> says what kind of errors was found. For the detailed error numbers, | |
| 69 | − please refer to the file linux/fs/ocfs2/filecheck.h. | |
| 66 | + <INO> lists the inode numbers. | |
| 67 | + <DONE> indicates whether the operation has been finished. | |
| 68 | + <ERROR> says what kind of errors was found. For the detailed error numbers, | |
| 69 | + please refer to the file linux/fs/ocfs2/filecheck.h. | |
| 70 | 70 | |
| 71 | 71 | 2. If you determine to fix this inode, do:: |
| 72 | 72 | |
| 73 | 73 | # echo "<inode>" > /sys/fs/ocfs2/<devname>/filecheck/fix |
| 74 | 74 | # cat /sys/fs/ocfs2/<devname>/filecheck/fix |
| 75 | 75 | |
| 76 | −The output is like this::: | |
| 76 | + The output is like this:: | |
| 77 | 77 | |
| 78 | 78 | INO DONE ERROR |
| 79 | 79 | 39502 1 SUCCESS |
| 80 | 80 | |
| 81 | −This time, the <ERROR> column indicates whether this fix is successful or not. | |
| 81 | + This time, the <ERROR> column indicates whether this fix is successful or not. | |
| 82 | 82 | |
| 83 | 83 | 3. The record cache is used to store the history of check/fix results. It's |
| 84 | −default size is 10, and can be adjust between the range of 10 ~ 100. You can | |
| 85 | −adjust the size like this:: | |
| 84 | + default size is 10, and can be adjust between the range of 10 ~ 100. You can | |
| 85 | + adjust the size like this:: | |
| 86 | 86 | |
| 87 | − # echo "<size>" > /sys/fs/ocfs2/<devname>/filecheck/set | |
| 87 | + # echo "<size>" > /sys/fs/ocfs2/<devname>/filecheck/set | |
| 88 | 88 | |
| 89 | 89 | Fixing stuff |
| 90 | 90 | ============ |
| @@ -61,19 +61,6 @@ Preface · 60 unmodified lines | ||
| 61 | 61 | 0.1 Introduction/Credits |
| 62 | 62 | ------------------------ |
| 63 | 63 | |
| 64 | −This documentation is part of a soon (or so we hope) to be released book on | |
| 65 | −the SuSE Linux distribution. As there is no complete documentation for the | |
| 66 | −/proc file system and we've used many freely available sources to write these | |
| 67 | −chapters, it seems only fair to give the work back to the Linux community. | |
| 68 | −This work is based on the 2.2.* kernel version and the upcoming 2.4.*. I'm | |
| 69 | −afraid it's still far from complete, but we hope it will be useful. As far as | |
| 70 | −we know, it is the first 'all-in-one' document about the /proc file system. It | |
| 71 | −is focused on the Intel x86 hardware, so if you are looking for PPC, ARM, | |
| 72 | −SPARC, AXP, etc., features, you probably won't find what you are looking for. | |
| 73 | −It also only covers IPv4 networking, not IPv6 nor other protocols - sorry. But | |
| 74 | −additions and patches are welcome and will be added to this document if you | |
| 75 | −mail them to Bodo. | |
| 76 | − | |
| 77 | 64 | We'd like to thank Alan Cox, Rik van Riel, and Alexey Kuznetsov and a lot of |
| 78 | 65 | other people for help compiling this documentation. We'd also like to extend a |
| 79 | 66 | special thank you to Andi Kleen for documentation, which we relied on heavily |
| @@ -81,17 +68,9 @@ to create this document, as well as the additional information he provided. · 1 unmodified line | ||
| 81 | 68 | Thanks to everybody else who contributed source or docs to the Linux kernel |
| 82 | 69 | and helped create a great piece of software... :) |
| 83 | 70 | |
| 84 | −If you have any comments, corrections or additions, please don't hesitate to | |
| 85 | −contact Bodo Bauer at bb@ricochet.net. We'll be happy to add them to this | |
| 86 | −document. | |
| 87 | − | |
| 88 | 71 | The latest version of this document is available online at |
| 89 | 72 | https://www.kernel.org/doc/html/latest/filesystems/proc.html |
| 90 | 73 | |
| 91 | −If the above direction does not works for you, you could try the kernel | |
| 92 | −mailing list at linux-kernel@vger.kernel.org and/or try to reach me at | |
| 93 | −comandante@zaralinux.com. | |
| 94 | − | |
| 95 | 74 | 0.2 Legal Stuff |
| 96 | 75 | --------------- |
| 97 | 76 | |
| @@ -286,7 +286,7 @@ Trim_one(m) · 285 unmodified lines | ||
| 286 | 286 | strip the "seen by Trim_ancestors" mark from m |
| 287 | 287 | remove m from the Candidates list |
| 288 | 288 | return |
| 289 | − | |
| 289 | + | |
| 290 | 290 | remove_this = false |
| 291 | 291 | found = false |
| 292 | 292 | for each n in children(m) |
| @@ -312,7 +312,7 @@ Trim_ancestors(m) · 19 unmodified lines | ||
| 312 | 312 | } |
| 313 | 313 | |
| 314 | 314 | Terminating condition in the loop in Trim_ancestors() is correct, |
| 315 | −since that that loop will never run into p belonging to U - p is always | |
| 315 | +since that loop will never run into p belonging to U - p is always | |
| 316 | 316 | an ancestor of argument of Trim_one() and since U is closed, the argument |
| 317 | 317 | of Trim_one() would also have to belong to U. But Trim_one() is never |
| 318 | 318 | called for elements of U. In other words, p belongs to S if and only |
| @@ -361,7 +361,7 @@ such removals. · 42 unmodified lines | ||
| 361 | 361 | Proof: suppose S was non-shifting, x is a locked element of S, parent of x |
| 362 | 362 | is not in S and S - {x} is not non-shifting. Then there is an element m |
| 363 | 363 | in S - {x} and a subtree mounted strictly inside m, such that m contains |
| 364 | −an element not in in S - {x}. Since S is non-shifting, everything in | |
| 364 | +an element not in S - {x}. Since S is non-shifting, everything in | |
| 365 | 365 | that subtree must belong to S. But that means that this subtree must |
| 366 | 366 | contain x somewhere *and* that parent of x either belongs that subtree |
| 367 | 367 | or is equal to m. Either way it must belong to S. Contradiction. |
| @@ -769,7 +769,7 @@ this would be dependent on number of cores the benchmark is run on. · 768 unmodified lines | ||
| 769 | 769 | depending on # of threads: |
| 770 | 770 | |
| 771 | 771 | For the same SKU in #1, a 'single thread, with 10% bandwidth' and '4 |
| 772 | −thread, with 10% bandwidth' can consume upto 10GBps and 40GBps although | |
| 772 | +thread, with 10% bandwidth' can consume up to 10GBps and 40GBps although | |
| 773 | 773 | they have same percentage bandwidth of 10%. This is simply because as |
| 774 | 774 | threads start using more cores in an rdtgroup, the actual bandwidth may |
| 775 | 775 | increase or vary although user specified bandwidth percentage is same. |
| @@ -31,965 +31,960 @@ and versioned filesystem. · 30 unmodified lines | ||
| 31 | 31 | ----------- |
| 32 | 32 | |
| 33 | 33 | Shared subtree provides four different flavors of mounts; struct vfsmount to be |
| 34 | −precise | |
| 34 | +precise: | |
| 35 | 35 | |
| 36 | − a. shared mount | |
| 37 | − b. slave mount | |
| 38 | − c. private mount | |
| 39 | − d. unbindable mount | |
| 40 | 36 | |
| 37 | +a) A **shared mount** can be replicated to as many mountpoints and all the | |
| 38 | + replicas continue to be exactly same. | |
| 41 | 39 | |
| 42 | −2a) A shared mount can be replicated to as many mountpoints and all the | |
| 43 | −replicas continue to be exactly same. | |
| 40 | + Here is an example: | |
| 44 | 41 | |
| 45 | − Here is an example: | |
| 42 | + Let's say /mnt has a mount that is shared:: | |
| 46 | 43 | |
| 47 | − Let's say /mnt has a mount that is shared:: | |
| 44 | + # mount --make-shared /mnt | |
| 48 | 45 | |
| 49 | − mount --make-shared /mnt | |
| 46 | + .. note:: | |
| 47 | + mount(8) command now supports the --make-shared flag, | |
| 48 | + so the sample 'smount' program is no longer needed and has been | |
| 49 | + removed. | |
| 50 | 50 | |
| 51 | − Note: mount(8) command now supports the --make-shared flag, | |
| 52 | − so the sample 'smount' program is no longer needed and has been | |
| 53 | − removed. | |
| 51 | + :: | |
| 54 | 52 | |
| 55 | − :: | |
| 53 | + # mount --bind /mnt /tmp | |
| 56 | 54 | |
| 57 | − # mount --bind /mnt /tmp | |
| 55 | + The above command replicates the mount at /mnt to the mountpoint /tmp | |
| 56 | + and the contents of both the mounts remain identical. | |
| 58 | 57 | |
| 59 | − The above command replicates the mount at /mnt to the mountpoint /tmp | |
| 60 | − and the contents of both the mounts remain identical. | |
| 58 | + :: | |
| 61 | 59 | |
| 62 | − :: | |
| 60 | + #ls /mnt | |
| 61 | + a b c | |
| 63 | 62 | |
| 64 | − #ls /mnt | |
| 65 | − a b c | |
| 63 | + #ls /tmp | |
| 64 | + a b c | |
| 66 | 65 | |
| 67 | − #ls /tmp | |
| 68 | − a b c | |
| 66 | + Now let's say we mount a device at /tmp/a:: | |
| 69 | 67 | |
| 70 | − Now let's say we mount a device at /tmp/a:: | |
| 68 | + # mount /dev/sd0 /tmp/a | |
| 71 | 69 | |
| 72 | − # mount /dev/sd0 /tmp/a | |
| 70 | + # ls /tmp/a | |
| 71 | + t1 t2 t3 | |
| 73 | 72 | |
| 74 | − #ls /tmp/a | |
| 75 | − t1 t2 t3 | |
| 73 | + # ls /mnt/a | |
| 74 | + t1 t2 t3 | |
| 76 | 75 | |
| 77 | − #ls /mnt/a | |
| 78 | − t1 t2 t3 | |
| 76 | + Note that the mount has propagated to the mount at /mnt as well. | |
| 79 | 77 | |
| 80 | − Note that the mount has propagated to the mount at /mnt as well. | |
| 78 | + And the same is true even when /dev/sd0 is mounted on /mnt/a. The | |
| 79 | + contents will be visible under /tmp/a too. | |
| 81 | 80 | |
| 82 | − And the same is true even when /dev/sd0 is mounted on /mnt/a. The | |
| 83 | − contents will be visible under /tmp/a too. | |
| 84 | 81 | |
| 82 | +b) A **slave mount** is like a shared mount except that mount and umount events | |
| 83 | + only propagate towards it. | |
| 85 | 84 | |
| 86 | −2b) A slave mount is like a shared mount except that mount and umount events | |
| 87 | − only propagate towards it. | |
| 85 | + All slave mounts have a master mount which is a shared. | |
| 88 | 86 | |
| 89 | − All slave mounts have a master mount which is a shared. | |
| 87 | + Here is an example: | |
| 90 | 88 | |
| 91 | − Here is an example: | |
| 89 | + Let's say /mnt has a mount which is shared:: | |
| 92 | 90 | |
| 93 | − Let's say /mnt has a mount which is shared. | |
| 94 | − # mount --make-shared /mnt | |
| 91 | + # mount --make-shared /mnt | |
| 95 | 92 | |
| 96 | − Let's bind mount /mnt to /tmp | |
| 97 | − # mount --bind /mnt /tmp | |
| 93 | + Let's bind mount /mnt to /tmp:: | |
| 98 | 94 | |
| 99 | − the new mount at /tmp becomes a shared mount and it is a replica of | |
| 100 | − the mount at /mnt. | |
| 95 | + # mount --bind /mnt /tmp | |
| 101 | 96 | |
| 102 | − Now let's make the mount at /tmp; a slave of /mnt | |
| 103 | − # mount --make-slave /tmp | |
| 97 | + the new mount at /tmp becomes a shared mount and it is a replica of | |
| 98 | + the mount at /mnt. | |
| 104 | 99 | |
| 105 | − let's mount /dev/sd0 on /mnt/a | |
| 106 | − # mount /dev/sd0 /mnt/a | |
| 100 | + Now let's make the mount at /tmp; a slave of /mnt:: | |
| 107 | 101 | |
| 108 | − #ls /mnt/a | |
| 109 | − t1 t2 t3 | |
| 102 | + # mount --make-slave /tmp | |
| 110 | 103 | |
| 111 | − #ls /tmp/a | |
| 112 | − t1 t2 t3 | |
| 104 | + let's mount /dev/sd0 on /mnt/a:: | |
| 113 | 105 | |
| 114 | − Note the mount event has propagated to the mount at /tmp | |
| 106 | + # mount /dev/sd0 /mnt/a | |
| 115 | 107 | |
| 116 | − However let's see what happens if we mount something on the mount at /tmp | |
| 108 | + # ls /mnt/a | |
| 109 | + t1 t2 t3 | |
| 117 | 110 | |
| 118 | − # mount /dev/sd1 /tmp/b | |
| 111 | + # ls /tmp/a | |
| 112 | + t1 t2 t3 | |
| 119 | 113 | |
| 120 | − #ls /tmp/b | |
| 121 | − s1 s2 s3 | |
| 114 | + Note the mount event has propagated to the mount at /tmp | |
| 122 | 115 | |
| 123 | − #ls /mnt/b | |
| 116 | + However let's see what happens if we mount something on the mount at | |
| 117 | + /tmp:: | |
| 124 | 118 | |
| 125 | − Note how the mount event has not propagated to the mount at | |
| 126 | − /mnt | |
| 119 | + # mount /dev/sd1 /tmp/b | |
| 127 | 120 | |
| 121 | + # ls /tmp/b | |
| 122 | + s1 s2 s3 | |
| 128 | 123 | |
| 129 | −2c) A private mount does not forward or receive propagation. | |
| 124 | + # ls /mnt/b | |
| 130 | 125 | |
| 131 | − This is the mount we are familiar with. Its the default type. | |
| 126 | + Note how the mount event has not propagated to the mount at | |
| 127 | + /mnt | |
| 132 | 128 | |
| 133 | 129 | |
| 134 | −2d) A unbindable mount is a unbindable private mount | |
| 130 | +c) A **private mount** does not forward or receive propagation. | |
| 135 | 131 | |
| 136 | − let's say we have a mount at /mnt and we make it unbindable:: | |
| 132 | + This is the mount we are familiar with. Its the default type. | |
| 137 | 133 | |
| 138 | − # mount --make-unbindable /mnt | |
| 139 | 134 | |
| 140 | − Let's try to bind mount this mount somewhere else:: | |
| 135 | +d) An **unbindable mount** is, as the name suggests, an unbindable private | |
| 136 | + mount. | |
| 141 | 137 | |
| 142 | − # mount --bind /mnt /tmp | |
| 143 | − mount: wrong fs type, bad option, bad superblock on /mnt, | |
| 144 | − or too many mounted file systems | |
| 138 | + let's say we have a mount at /mnt and we make it unbindable:: | |
| 145 | 139 | |
| 146 | − Binding a unbindable mount is a invalid operation. | |
| 140 | + # mount --make-unbindable /mnt | |
| 141 | + | |
| 142 | + Let's try to bind mount this mount somewhere else:: | |
| 143 | + | |
| 144 | + # mount --bind /mnt /tmp mount: wrong fs type, bad option, bad | |
| 145 | + superblock on /mnt, or too many mounted file systems | |
| 146 | + | |
| 147 | + Binding a unbindable mount is a invalid operation. | |
| 147 | 148 | |
| 148 | 149 | |
| 149 | 150 | 3) Setting mount states |
| 150 | 151 | ----------------------- |
| 151 | 152 | |
| 152 | − The mount command (util-linux package) can be used to set mount | |
| 153 | − states:: | |
| 153 | +The mount command (util-linux package) can be used to set mount | |
| 154 | +states:: | |
| 154 | 155 | |
| 155 | − mount --make-shared mountpoint | |
| 156 | − mount --make-slave mountpoint | |
| 157 | − mount --make-private mountpoint | |
| 158 | − mount --make-unbindable mountpoint | |
| 156 | + mount --make-shared mountpoint | |
| 157 | + mount --make-slave mountpoint | |
| 158 | + mount --make-private mountpoint | |
| 159 | + mount --make-unbindable mountpoint | |
| 159 | 160 | |
| 160 | 161 | |
| 161 | 162 | 4) Use cases |
| 162 | 163 | ------------ |
| 163 | 164 | |
| 164 | − A) A process wants to clone its own namespace, but still wants to | |
| 165 | − access the CD that got mounted recently. | |
| 165 | +A) A process wants to clone its own namespace, but still wants to | |
| 166 | + access the CD that got mounted recently. | |
| 166 | 167 | |
| 167 | − Solution: | |
| 168 | + Solution: | |
| 168 | 169 | |
| 169 | − The system administrator can make the mount at /cdrom shared:: | |
| 170 | + The system administrator can make the mount at /cdrom shared:: | |
| 170 | 171 | |
| 171 | − mount --bind /cdrom /cdrom | |
| 172 | − mount --make-shared /cdrom | |
| 172 | + mount --bind /cdrom /cdrom | |
| 173 | + mount --make-shared /cdrom | |
| 173 | 174 | |
| 174 | − Now any process that clones off a new namespace will have a | |
| 175 | − mount at /cdrom which is a replica of the same mount in the | |
| 176 | − parent namespace. | |
| 175 | + Now any process that clones off a new namespace will have a | |
| 176 | + mount at /cdrom which is a replica of the same mount in the | |
| 177 | + parent namespace. | |
| 177 | 178 | |
| 178 | − So when a CD is inserted and mounted at /cdrom that mount gets | |
| 179 | − propagated to the other mount at /cdrom in all the other clone | |
| 180 | − namespaces. | |
| 179 | + So when a CD is inserted and mounted at /cdrom that mount gets | |
| 180 | + propagated to the other mount at /cdrom in all the other clone | |
| 181 | + namespaces. | |
| 181 | 182 | |
| 182 | − B) A process wants its mounts invisible to any other process, but | |
| 183 | − still be able to see the other system mounts. | |
| 183 | +B) A process wants its mounts invisible to any other process, but | |
| 184 | + still be able to see the other system mounts. | |
| 184 | 185 | |
| 185 | − Solution: | |
| 186 | + Solution: | |
| 186 | 187 | |
| 187 | − To begin with, the administrator can mark the entire mount tree | |
| 188 | − as shareable:: | |
| 188 | + To begin with, the administrator can mark the entire mount tree | |
| 189 | + as shareable:: | |
| 189 | 190 | |
| 190 | − mount --make-rshared / | |
| 191 | + mount --make-rshared / | |
| 191 | 192 | |
| 192 | − A new process can clone off a new namespace. And mark some part | |
| 193 | − of its namespace as slave:: | |
| 193 | + A new process can clone off a new namespace. And mark some part | |
| 194 | + of its namespace as slave:: | |
| 194 | 195 | |
| 195 | − mount --make-rslave /myprivatetree | |
| 196 | + mount --make-rslave /myprivatetree | |
| 196 | 197 | |
| 197 | − Hence forth any mounts within the /myprivatetree done by the | |
| 198 | − process will not show up in any other namespace. However mounts | |
| 199 | − done in the parent namespace under /myprivatetree still shows | |
| 200 | − up in the process's namespace. | |
| 198 | + Hence forth any mounts within the /myprivatetree done by the | |
| 199 | + process will not show up in any other namespace. However mounts | |
| 200 | + done in the parent namespace under /myprivatetree still shows | |
| 201 | + up in the process's namespace. | |
| 201 | 202 | |
| 202 | 203 | |
| 203 | − Apart from the above semantics this feature provides the | |
| 204 | − building blocks to solve the following problems: | |
| 204 | +Apart from the above semantics this feature provides the | |
| 205 | +building blocks to solve the following problems: | |
| 205 | 206 | |
| 206 | − C) Per-user namespace | |
| 207 | +C) Per-user namespace | |
| 207 | 208 | |
| 208 | − The above semantics allows a way to share mounts across | |
| 209 | − namespaces. But namespaces are associated with processes. If | |
| 210 | − namespaces are made first class objects with user API to | |
| 211 | − associate/disassociate a namespace with userid, then each user | |
| 212 | − could have his/her own namespace and tailor it to his/her | |
| 213 | − requirements. This needs to be supported in PAM. | |
| 209 | + The above semantics allows a way to share mounts across | |
| 210 | + namespaces. But namespaces are associated with processes. If | |
| 211 | + namespaces are made first class objects with user API to | |
| 212 | + associate/disassociate a namespace with userid, then each user | |
| 213 | + could have his/her own namespace and tailor it to his/her | |
| 214 | + requirements. This needs to be supported in PAM. | |
| 214 | 215 | |
| 215 | − D) Versioned files | |
| 216 | +D) Versioned files | |
| 216 | 217 | |
| 217 | − If the entire mount tree is visible at multiple locations, then | |
| 218 | − an underlying versioning file system can return different | |
| 219 | − versions of the file depending on the path used to access that | |
| 220 | − file. | |
| 218 | + If the entire mount tree is visible at multiple locations, then | |
| 219 | + an underlying versioning file system can return different | |
| 220 | + versions of the file depending on the path used to access that | |
| 221 | + file. | |
| 221 | 222 | |
| 222 | − An example is:: | |
| 223 | + An example is:: | |
| 223 | 224 | |
| 224 | − mount --make-shared / | |
| 225 | − mount --rbind / /view/v1 | |
| 226 | − mount --rbind / /view/v2 | |
| 227 | − mount --rbind / /view/v3 | |
| 228 | − mount --rbind / /view/v4 | |
| 225 | + mount --make-shared / | |
| 226 | + mount --rbind / /view/v1 | |
| 227 | + mount --rbind / /view/v2 | |
| 228 | + mount --rbind / /view/v3 | |
| 229 | + mount --rbind / /view/v4 | |
| 229 | 230 | |
| 230 | − and if /usr has a versioning filesystem mounted, then that | |
| 231 | − mount appears at /view/v1/usr, /view/v2/usr, /view/v3/usr and | |
| 232 | − /view/v4/usr too | |
| 231 | + and if /usr has a versioning filesystem mounted, then that | |
| 232 | + mount appears at /view/v1/usr, /view/v2/usr, /view/v3/usr and | |
| 233 | + /view/v4/usr too | |
| 233 | 234 | |
| 234 | − A user can request v3 version of the file /usr/fs/namespace.c | |
| 235 | − by accessing /view/v3/usr/fs/namespace.c . The underlying | |
| 236 | − versioning filesystem can then decipher that v3 version of the | |
| 237 | − filesystem is being requested and return the corresponding | |
| 238 | − inode. | |
| 235 | + A user can request v3 version of the file /usr/fs/namespace.c | |
| 236 | + by accessing /view/v3/usr/fs/namespace.c . The underlying | |
| 237 | + versioning filesystem can then decipher that v3 version of the | |
| 238 | + filesystem is being requested and return the corresponding | |
| 239 | + inode. | |
| 239 | 240 | |
| 240 | 241 | 5) Detailed semantics |
| 241 | 242 | --------------------- |
| 242 | − The section below explains the detailed semantics of | |
| 243 | − bind, rbind, move, mount, umount and clone-namespace operations. | |
| 244 | − | |
| 245 | − Note: the word 'vfsmount' and the noun 'mount' have been used | |
| 246 | − to mean the same thing, throughout this document. | |
| 243 | +The section below explains the detailed semantics of | |
| 244 | +bind, rbind, move, mount, umount and clone-namespace operations. | |
| 247 | 245 | |
| 248 | −5a) Mount states | |
| 246 | +.. Note:: | |
| 247 | + the word 'vfsmount' and the noun 'mount' have been used | |
| 248 | + to mean the same thing, throughout this document. | |
| 249 | 249 | |
| 250 | − A given mount can be in one of the following states | |
| 250 | +a) Mount states | |
| 251 | 251 | |
| 252 | − 1) shared | |
| 253 | − 2) slave | |
| 254 | − 3) shared and slave | |
| 255 | − 4) private | |
| 256 | − 5) unbindable | |
| 252 | + A **propagation event** is defined as event generated on a vfsmount | |
| 253 | + that leads to mount or unmount actions in other vfsmounts. | |
| 257 | 254 | |
| 258 | − A 'propagation event' is defined as event generated on a vfsmount | |
| 259 | − that leads to mount or unmount actions in other vfsmounts. | |
| 255 | + A **peer group** is defined as a group of vfsmounts that propagate | |
| 256 | + events to each other. | |
| 260 | 257 | |
| 261 | − A 'peer group' is defined as a group of vfsmounts that propagate | |
| 262 | − events to each other. | |
| 258 | + A given mount can be in one of the following states: | |
| 263 | 259 | |
| 264 | − (1) Shared mounts | |
| 260 | + (1) Shared mounts | |
| 265 | 261 | |
| 266 | − A 'shared mount' is defined as a vfsmount that belongs to a | |
| 267 | − 'peer group'. | |
| 262 | + A **shared mount** is defined as a vfsmount that belongs to a | |
| 263 | + peer group. | |
| 268 | 264 | |
| 269 | − For example:: | |
| 265 | + For example:: | |
| 270 | 266 | |
| 271 | − mount --make-shared /mnt | |
| 272 | − mount --bind /mnt /tmp | |
| 267 | + mount --make-shared /mnt | |
| 268 | + mount --bind /mnt /tmp | |
| 273 | 269 | |
| 274 | − The mount at /mnt and that at /tmp are both shared and belong | |
| 275 | − to the same peer group. Anything mounted or unmounted under | |
| 276 | − /mnt or /tmp reflect in all the other mounts of its peer | |
| 277 | − group. | |
| 270 | + The mount at /mnt and that at /tmp are both shared and belong | |
| 271 | + to the same peer group. Anything mounted or unmounted under | |
| 272 | + /mnt or /tmp reflect in all the other mounts of its peer | |
| 273 | + group. | |
| 278 | 274 | |
| 279 | 275 | |
| 280 | − (2) Slave mounts | |
| 276 | + (2) Slave mounts | |
| 281 | 277 | |
| 282 | − A 'slave mount' is defined as a vfsmount that receives | |
| 283 | − propagation events and does not forward propagation events. | |
| 278 | + A **slave mount** is defined as a vfsmount that receives | |
| 279 | + propagation events and does not forward propagation events. | |
| 284 | 280 | |
| 285 | − A slave mount as the name implies has a master mount from which | |
| 286 | − mount/unmount events are received. Events do not propagate from | |
| 287 | − the slave mount to the master. Only a shared mount can be made | |
| 288 | − a slave by executing the following command:: | |
| 281 | + A slave mount as the name implies has a master mount from which | |
| 282 | + mount/unmount events are received. Events do not propagate from | |
| 283 | + the slave mount to the master. Only a shared mount can be made | |
| 284 | + a slave by executing the following command:: | |
| 289 | 285 | |
| 290 | − mount --make-slave mount | |
| 286 | + mount --make-slave mount | |
| 291 | 287 | |
| 292 | − A shared mount that is made as a slave is no more shared unless | |
| 293 | − modified to become shared. | |
| 288 | + A shared mount that is made as a slave is no more shared unless | |
| 289 | + modified to become shared. | |
| 294 | 290 | |
| 295 | − (3) Shared and Slave | |
| 291 | + (3) Shared and Slave | |
| 296 | 292 | |
| 297 | − A vfsmount can be both shared as well as slave. This state | |
| 298 | − indicates that the mount is a slave of some vfsmount, and | |
| 299 | − has its own peer group too. This vfsmount receives propagation | |
| 300 | − events from its master vfsmount, and also forwards propagation | |
| 301 | − events to its 'peer group' and to its slave vfsmounts. | |
| 293 | + A vfsmount can be both **shared** as well as **slave**. This state | |
| 294 | + indicates that the mount is a slave of some vfsmount, and | |
| 295 | + has its own peer group too. This vfsmount receives propagation | |
| 296 | + events from its master vfsmount, and also forwards propagation | |
| 297 | + events to its 'peer group' and to its slave vfsmounts. | |
| 302 | 298 | |
| 303 | − Strictly speaking, the vfsmount is shared having its own | |
| 304 | − peer group, and this peer-group is a slave of some other | |
| 305 | − peer group. | |
| 299 | + Strictly speaking, the vfsmount is shared having its own | |
| 300 | + peer group, and this peer-group is a slave of some other | |
| 301 | + peer group. | |
| 306 | 302 | |
| 307 | − Only a slave vfsmount can be made as 'shared and slave' by | |
| 308 | − either executing the following command:: | |
| 303 | + Only a slave vfsmount can be made as 'shared and slave' by | |
| 304 | + either executing the following command:: | |
| 309 | 305 | |
| 310 | − mount --make-shared mount | |
| 306 | + mount --make-shared mount | |
| 311 | 307 | |
| 312 | − or by moving the slave vfsmount under a shared vfsmount. | |
| 308 | + or by moving the slave vfsmount under a shared vfsmount. | |
| 313 | 309 | |
| 314 | − (4) Private mount | |
| 310 | + (4) Private mount | |
| 315 | 311 | |
| 316 | − A 'private mount' is defined as vfsmount that does not | |
| 317 | − receive or forward any propagation events. | |
| 312 | + A **private mount** is defined as vfsmount that does not | |
| 313 | + receive or forward any propagation events. | |
| 318 | 314 | |
| 319 | − (5) Unbindable mount | |
| 315 | + (5) Unbindable mount | |
| 320 | 316 | |
| 321 | − A 'unbindable mount' is defined as vfsmount that does not | |
| 322 | − receive or forward any propagation events and cannot | |
| 323 | − be bind mounted. | |
| 317 | + A **unbindable mount** is defined as vfsmount that does not | |
| 318 | + receive or forward any propagation events and cannot | |
| 319 | + be bind mounted. | |
| 324 | 320 | |
| 325 | 321 | |
| 326 | − State diagram: | |
| 322 | + State diagram: | |
| 327 | 323 | |
| 328 | − The state diagram below explains the state transition of a mount, | |
| 329 | − in response to various commands:: | |
| 324 | + The state diagram below explains the state transition of a mount, | |
| 325 | + in response to various commands:: | |
| 330 | 326 | |
| 331 | − ----------------------------------------------------------------------- | |
| 332 | − | |make-shared | make-slave | make-private |make-unbindab| | |
| 333 | − --------------|------------|--------------|--------------|-------------| | |
| 334 | − |shared |shared |*slave/private| private | unbindable | | |
| 335 | − | | | | | | | |
| 336 | − |-------------|------------|--------------|--------------|-------------| | |
| 337 | − |slave |shared | **slave | private | unbindable | | |
| 338 | − | |and slave | | | | | |
| 339 | − |-------------|------------|--------------|--------------|-------------| | |
| 340 | − |shared |shared | slave | private | unbindable | | |
| 341 | − |and slave |and slave | | | | | |
| 342 | − |-------------|------------|--------------|--------------|-------------| | |
| 343 | − |private |shared | **private | private | unbindable | | |
| 344 | − |-------------|------------|--------------|--------------|-------------| | |
| 345 | − |unbindable |shared |**unbindable | private | unbindable | | |
| 346 | − ------------------------------------------------------------------------ | |
| 327 | + ----------------------------------------------------------------------- | |
| 328 | + | |make-shared | make-slave | make-private |make-unbindab| | |
| 329 | + --------------|------------|--------------|--------------|-------------| | |
| 330 | + |shared |shared |*slave/private| private | unbindable | | |
| 331 | + | | | | | | | |
| 332 | + |-------------|------------|--------------|--------------|-------------| | |
| 333 | + |slave |shared | **slave | private | unbindable | | |
| 334 | + | |and slave | | | | | |
| 335 | + |-------------|------------|--------------|--------------|-------------| | |
| 336 | + |shared |shared | slave | private | unbindable | | |
| 337 | + |and slave |and slave | | | | | |
| 338 | + |-------------|------------|--------------|--------------|-------------| | |
| 339 | + |private |shared | **private | private | unbindable | | |
| 340 | + |-------------|------------|--------------|--------------|-------------| | |
| 341 | + |unbindable |shared |**unbindable | private | unbindable | | |
| 342 | + ------------------------------------------------------------------------ | |
| 347 | 343 | |
| 348 | − * if the shared mount is the only mount in its peer group, making it | |
| 349 | − slave, makes it private automatically. Note that there is no master to | |
| 350 | − which it can be slaved to. | |
| 344 | + * if the shared mount is the only mount in its peer group, making it | |
| 345 | + slave, makes it private automatically. Note that there is no master to | |
| 346 | + which it can be slaved to. | |
| 351 | 347 | |
| 352 | − ** slaving a non-shared mount has no effect on the mount. | |
| 348 | + ** slaving a non-shared mount has no effect on the mount. | |
| 353 | 349 | |
| 354 | − Apart from the commands listed below, the 'move' operation also changes | |
| 355 | − the state of a mount depending on type of the destination mount. Its | |
| 356 | − explained in section 5d. | |
| 350 | + Apart from the commands listed below, the 'move' operation also changes | |
| 351 | + the state of a mount depending on type of the destination mount. Its | |
| 352 | + explained in section 5d. | |
| 357 | 353 | |
| 358 | −5b) Bind semantics | |
| 354 | +b) Bind semantics | |
| 359 | 355 | |
| 360 | − Consider the following command:: | |
| 356 | + Consider the following command:: | |
| 361 | 357 | |
| 362 | − mount --bind A/a B/b | |
| 358 | + mount --bind A/a B/b | |
| 363 | 359 | |
| 364 | − where 'A' is the source mount, 'a' is the dentry in the mount 'A', 'B' | |
| 365 | − is the destination mount and 'b' is the dentry in the destination mount. | |
| 360 | + where 'A' is the source mount, 'a' is the dentry in the mount 'A', 'B' | |
| 361 | + is the destination mount and 'b' is the dentry in the destination mount. | |
| 366 | 362 | |
| 367 | − The outcome depends on the type of mount of 'A' and 'B'. The table | |
| 368 | − below contains quick reference:: | |
| 363 | + The outcome depends on the type of mount of 'A' and 'B'. The table | |
| 364 | + below contains quick reference:: | |
| 369 | 365 | |
| 370 | − -------------------------------------------------------------------------- | |
| 371 | − | BIND MOUNT OPERATION | | |
| 372 | − |************************************************************************| | |
| 373 | − |source(A)->| shared | private | slave | unbindable | | |
| 374 | − | dest(B) | | | | | | |
| 375 | − | | | | | | | | |
| 376 | − | v | | | | | | |
| 377 | − |************************************************************************| | |
| 378 | − | shared | shared | shared | shared & slave | invalid | | |
| 379 | − | | | | | | | |
| 380 | − |non-shared| shared | private | slave | invalid | | |
| 381 | − ************************************************************************** | |
| 366 | + -------------------------------------------------------------------------- | |
| 367 | + | BIND MOUNT OPERATION | | |
| 368 | + |************************************************************************| | |
| 369 | + |source(A)->| shared | private | slave | unbindable | | |
| 370 | + | dest(B) | | | | | | |
| 371 | + | | | | | | | | |
| 372 | + | v | | | | | | |
| 373 | + |************************************************************************| | |
| 374 | + | shared | shared | shared | shared & slave | invalid | | |
| 375 | + | | | | | | | |
| 376 | + |non-shared| shared | private | slave | invalid | | |
| 377 | + ************************************************************************** | |
| 382 | 378 | |
| 383 | − Details: | |
| 379 | + Details: | |
| 384 | 380 | |
| 385 | − 1. 'A' is a shared mount and 'B' is a shared mount. A new mount 'C' | |
| 386 | − which is clone of 'A', is created. Its root dentry is 'a' . 'C' is | |
| 387 | − mounted on mount 'B' at dentry 'b'. Also new mount 'C1', 'C2', 'C3' ... | |
| 388 | − are created and mounted at the dentry 'b' on all mounts where 'B' | |
| 389 | − propagates to. A new propagation tree containing 'C1',..,'Cn' is | |
| 390 | − created. This propagation tree is identical to the propagation tree of | |
| 391 | − 'B'. And finally the peer-group of 'C' is merged with the peer group | |
| 392 | − of 'A'. | |
| 381 | + 1. 'A' is a shared mount and 'B' is a shared mount. A new mount 'C' | |
| 382 | + which is clone of 'A', is created. Its root dentry is 'a' . 'C' is | |
| 383 | + mounted on mount 'B' at dentry 'b'. Also new mount 'C1', 'C2', 'C3' ... | |
| 384 | + are created and mounted at the dentry 'b' on all mounts where 'B' | |
| 385 | + propagates to. A new propagation tree containing 'C1',..,'Cn' is | |
| 386 | + created. This propagation tree is identical to the propagation tree of | |
| 387 | + 'B'. And finally the peer-group of 'C' is merged with the peer group | |
| 388 | + of 'A'. | |
| 393 | 389 | |
| 394 | − 2. 'A' is a private mount and 'B' is a shared mount. A new mount 'C' | |
| 395 | − which is clone of 'A', is created. Its root dentry is 'a'. 'C' is | |
| 396 | − mounted on mount 'B' at dentry 'b'. Also new mount 'C1', 'C2', 'C3' ... | |
| 397 | − are created and mounted at the dentry 'b' on all mounts where 'B' | |
| 398 | − propagates to. A new propagation tree is set containing all new mounts | |
| 399 | − 'C', 'C1', .., 'Cn' with exactly the same configuration as the | |
| 400 | − propagation tree for 'B'. | |
| 390 | + 2. 'A' is a private mount and 'B' is a shared mount. A new mount 'C' | |
| 391 | + which is clone of 'A', is created. Its root dentry is 'a'. 'C' is | |
| 392 | + mounted on mount 'B' at dentry 'b'. Also new mount 'C1', 'C2', 'C3' ... | |
| 393 | + are created and mounted at the dentry 'b' on all mounts where 'B' | |
| 394 | + propagates to. A new propagation tree is set containing all new mounts | |
| 395 | + 'C', 'C1', .., 'Cn' with exactly the same configuration as the | |
| 396 | + propagation tree for 'B'. | |
| 401 | 397 | |
| 402 | − 3. 'A' is a slave mount of mount 'Z' and 'B' is a shared mount. A new | |
| 403 | − mount 'C' which is clone of 'A', is created. Its root dentry is 'a' . | |
| 404 | − 'C' is mounted on mount 'B' at dentry 'b'. Also new mounts 'C1', 'C2', | |
| 405 | − 'C3' ... are created and mounted at the dentry 'b' on all mounts where | |
| 406 | − 'B' propagates to. A new propagation tree containing the new mounts | |
| 407 | − 'C','C1',.. 'Cn' is created. This propagation tree is identical to the | |
| 408 | − propagation tree for 'B'. And finally the mount 'C' and its peer group | |
| 409 | − is made the slave of mount 'Z'. In other words, mount 'C' is in the | |
| 410 | − state 'slave and shared'. | |
| 411 | − | |
| 412 | − 4. 'A' is a unbindable mount and 'B' is a shared mount. This is a | |
| 413 | − invalid operation. | |
| 414 | − | |
| 415 | − 5. 'A' is a private mount and 'B' is a non-shared(private or slave or | |
| 416 | − unbindable) mount. A new mount 'C' which is clone of 'A', is created. | |
| 417 | − Its root dentry is 'a'. 'C' is mounted on mount 'B' at dentry 'b'. | |
| 418 | − | |
| 419 | − 6. 'A' is a shared mount and 'B' is a non-shared mount. A new mount 'C' | |
| 420 | − which is a clone of 'A' is created. Its root dentry is 'a'. 'C' is | |
| 421 | − mounted on mount 'B' at dentry 'b'. 'C' is made a member of the | |
| 422 | − peer-group of 'A'. | |
| 423 | − | |
| 424 | − 7. 'A' is a slave mount of mount 'Z' and 'B' is a non-shared mount. A | |
| 425 | − new mount 'C' which is a clone of 'A' is created. Its root dentry is | |
| 426 | − 'a'. 'C' is mounted on mount 'B' at dentry 'b'. Also 'C' is set as a | |
| 427 | − slave mount of 'Z'. In other words 'A' and 'C' are both slave mounts of | |
| 428 | − 'Z'. All mount/unmount events on 'Z' propagates to 'A' and 'C'. But | |
| 429 | − mount/unmount on 'A' do not propagate anywhere else. Similarly | |
| 430 | − mount/unmount on 'C' do not propagate anywhere else. | |
| 431 | − | |
| 432 | − 8. 'A' is a unbindable mount and 'B' is a non-shared mount. This is a | |
| 433 | − invalid operation. A unbindable mount cannot be bind mounted. | |
| 434 | − | |
| 435 | −5c) Rbind semantics | |
| 436 | − | |
| 437 | − rbind is same as bind. Bind replicates the specified mount. Rbind | |
| 438 | − replicates all the mounts in the tree belonging to the specified mount. | |
| 439 | − Rbind mount is bind mount applied to all the mounts in the tree. | |
| 440 | − | |
| 441 | − If the source tree that is rbind has some unbindable mounts, | |
| 442 | − then the subtree under the unbindable mount is pruned in the new | |
| 443 | − location. | |
| 444 | − | |
| 445 | − eg: | |
| 446 | − | |
| 447 | − let's say we have the following mount tree:: | |
| 448 | − | |
| 449 | − A | |
| 450 | − / \ | |
| 451 | − B C | |
| 452 | − / \ / \ | |
| 453 | − D E F G | |
| 454 | − | |
| 455 | − Let's say all the mount except the mount C in the tree are | |
| 456 | − of a type other than unbindable. | |
| 457 | − | |
| 458 | − If this tree is rbound to say Z | |
| 459 | − | |
| 460 | − We will have the following tree at the new location:: | |
| 461 | − | |
| 462 | − Z | |
| 463 | − | | |
| 464 | − A' | |
| 465 | − / | |
| 466 | − B' Note how the tree under C is pruned | |
| 467 | − / \ in the new location. | |
| 468 | − D' E' | |
| 469 | − | |
| 470 | − | |
| 471 | − | |
| 472 | −5d) Move semantics | |
| 473 | − | |
| 474 | − Consider the following command | |
| 475 | − | |
| 476 | − mount --move A B/b | |
| 398 | + 3. 'A' is a slave mount of mount 'Z' and 'B' is a shared mount. A new | |
| 399 | + mount 'C' which is clone of 'A', is created. Its root dentry is 'a' . | |
| 400 | + 'C' is mounted on mount 'B' at dentry 'b'. Also new mounts 'C1', 'C2', | |
| 401 | + 'C3' ... are created and mounted at the dentry 'b' on all mounts where | |
| 402 | + 'B' propagates to. A new propagation tree containing the new mounts | |
| 403 | + 'C','C1',.. 'Cn' is created. This propagation tree is identical to the | |
| 404 | + propagation tree for 'B'. And finally the mount 'C' and its peer group | |
| 405 | + is made the slave of mount 'Z'. In other words, mount 'C' is in the | |
| 406 | + state 'slave and shared'. | |
| 407 | + | |
| 408 | + 4. 'A' is a unbindable mount and 'B' is a shared mount. This is a | |
| 409 | + invalid operation. | |
| 410 | + | |
| 411 | + 5. 'A' is a private mount and 'B' is a non-shared(private or slave or | |
| 412 | + unbindable) mount. A new mount 'C' which is clone of 'A', is created. | |
| 413 | + Its root dentry is 'a'. 'C' is mounted on mount 'B' at dentry 'b'. | |
| 414 | + | |
| 415 | + 6. 'A' is a shared mount and 'B' is a non-shared mount. A new mount 'C' | |
| 416 | + which is a clone of 'A' is created. Its root dentry is 'a'. 'C' is | |
| 417 | + mounted on mount 'B' at dentry 'b'. 'C' is made a member of the | |
| 418 | + peer-group of 'A'. | |
| 419 | + | |
| 420 | + 7. 'A' is a slave mount of mount 'Z' and 'B' is a non-shared mount. A | |
| 421 | + new mount 'C' which is a clone of 'A' is created. Its root dentry is | |
| 422 | + 'a'. 'C' is mounted on mount 'B' at dentry 'b'. Also 'C' is set as a | |
| 423 | + slave mount of 'Z'. In other words 'A' and 'C' are both slave mounts of | |
| 424 | + 'Z'. All mount/unmount events on 'Z' propagates to 'A' and 'C'. But | |
| 425 | + mount/unmount on 'A' do not propagate anywhere else. Similarly | |
| 426 | + mount/unmount on 'C' do not propagate anywhere else. | |
| 427 | + | |
| 428 | + 8. 'A' is a unbindable mount and 'B' is a non-shared mount. This is a | |
| 429 | + invalid operation. A unbindable mount cannot be bind mounted. | |
| 430 | + | |
| 431 | +c) Rbind semantics | |
| 432 | + | |
| 433 | + rbind is same as bind. Bind replicates the specified mount. Rbind | |
| 434 | + replicates all the mounts in the tree belonging to the specified mount. | |
| 435 | + Rbind mount is bind mount applied to all the mounts in the tree. | |
| 436 | + | |
| 437 | + If the source tree that is rbind has some unbindable mounts, | |
| 438 | + then the subtree under the unbindable mount is pruned in the new | |
| 439 | + location. | |
| 440 | + | |
| 441 | + eg: | |
| 442 | + | |
| 443 | + let's say we have the following mount tree:: | |
| 444 | + | |
| 445 | + A | |
| 446 | + / \ | |
| 447 | + B C | |
| 448 | + / \ / \ | |
| 449 | + D E F G | |
| 450 | + | |
| 451 | + Let's say all the mount except the mount C in the tree are | |
| 452 | + of a type other than unbindable. | |
| 453 | + | |
| 454 | + If this tree is rbound to say Z | |
| 455 | + | |
| 456 | + We will have the following tree at the new location:: | |
| 457 | + | |
| 458 | + Z | |
| 459 | + | | |
| 460 | + A' | |
| 461 | + / | |
| 462 | + B' Note how the tree under C is pruned | |
| 463 | + / \ in the new location. | |
| 464 | + D' E' | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
| 468 | +d) Move semantics | |
| 469 | + | |
| 470 | + Consider the following command:: | |
| 471 | + | |
| 472 | + mount --move A B/b | |
| 477 | 473 | |
| 478 | − where 'A' is the source mount, 'B' is the destination mount and 'b' is | |
| 479 | − the dentry in the destination mount. | |
| 474 | + where 'A' is the source mount, 'B' is the destination mount and 'b' is | |
| 475 | + the dentry in the destination mount. | |
| 480 | 476 | |
| 481 | − The outcome depends on the type of the mount of 'A' and 'B'. The table | |
| 482 | − below is a quick reference:: | |
| 477 | + The outcome depends on the type of the mount of 'A' and 'B'. The table | |
| 478 | + below is a quick reference:: | |
| 483 | 479 | |
| 484 | − --------------------------------------------------------------------------- | |
| 485 | − | MOVE MOUNT OPERATION | | |
| 486 | − |************************************************************************** | |
| 487 | − | source(A)->| shared | private | slave | unbindable | | |
| 488 | − | dest(B) | | | | | | |
| 489 | − | | | | | | | | |
| 490 | − | v | | | | | | |
| 491 | − |************************************************************************** | |
| 492 | − | shared | shared | shared |shared and slave| invalid | | |
| 493 | − | | | | | | | |
| 494 | − |non-shared| shared | private | slave | unbindable | | |
| 495 | − *************************************************************************** | |
| 480 | + --------------------------------------------------------------------------- | |
| 481 | + | MOVE MOUNT OPERATION | | |
| 482 | + |************************************************************************** | |
| 483 | + | source(A)->| shared | private | slave | unbindable | | |
| 484 | + | dest(B) | | | | | | |
| 485 | + | | | | | | | | |
| 486 | + | v | | | | | | |
| 487 | + |************************************************************************** | |
| 488 | + | shared | shared | shared |shared and slave| invalid | | |
| 489 | + | | | | | | | |
| 490 | + |non-shared| shared | private | slave | unbindable | | |
| 491 | + *************************************************************************** | |
| 496 | 492 | |
| 497 | − .. Note:: moving a mount residing under a shared mount is invalid. | |
| 493 | + .. Note:: moving a mount residing under a shared mount is invalid. | |
| 498 | 494 | |
| 499 | − Details follow: | |
| 495 | + Details follow: | |
| 500 | 496 | |
| 501 | − 1. 'A' is a shared mount and 'B' is a shared mount. The mount 'A' is | |
| 502 | − mounted on mount 'B' at dentry 'b'. Also new mounts 'A1', 'A2'...'An' | |
| 503 | − are created and mounted at dentry 'b' on all mounts that receive | |
| 504 | − propagation from mount 'B'. A new propagation tree is created in the | |
| 505 | − exact same configuration as that of 'B'. This new propagation tree | |
| 506 | − contains all the new mounts 'A1', 'A2'... 'An'. And this new | |
| 507 | − propagation tree is appended to the already existing propagation tree | |
| 508 | − of 'A'. | |
| 497 | + 1. 'A' is a shared mount and 'B' is a shared mount. The mount 'A' is | |
| 498 | + mounted on mount 'B' at dentry 'b'. Also new mounts 'A1', 'A2'...'An' | |
| 499 | + are created and mounted at dentry 'b' on all mounts that receive | |
| 500 | + propagation from mount 'B'. A new propagation tree is created in the | |
| 501 | + exact same configuration as that of 'B'. This new propagation tree | |
| 502 | + contains all the new mounts 'A1', 'A2'... 'An'. And this new | |
| 503 | + propagation tree is appended to the already existing propagation tree | |
| 504 | + of 'A'. | |
| 509 | 505 | |
| 510 | − 2. 'A' is a private mount and 'B' is a shared mount. The mount 'A' is | |
| 511 | − mounted on mount 'B' at dentry 'b'. Also new mount 'A1', 'A2'... 'An' | |
| 512 | − are created and mounted at dentry 'b' on all mounts that receive | |
| 513 | − propagation from mount 'B'. The mount 'A' becomes a shared mount and a | |
| 514 | − propagation tree is created which is identical to that of | |
| 515 | − 'B'. This new propagation tree contains all the new mounts 'A1', | |
| 516 | − 'A2'... 'An'. | |
| 506 | + 2. 'A' is a private mount and 'B' is a shared mount. The mount 'A' is | |
| 507 | + mounted on mount 'B' at dentry 'b'. Also new mount 'A1', 'A2'... 'An' | |
| 508 | + are created and mounted at dentry 'b' on all mounts that receive | |
| 509 | + propagation from mount 'B'. The mount 'A' becomes a shared mount and a | |
| 510 | + propagation tree is created which is identical to that of | |
| 511 | + 'B'. This new propagation tree contains all the new mounts 'A1', | |
| 512 | + 'A2'... 'An'. | |
| 517 | 513 | |
| 518 | − 3. 'A' is a slave mount of mount 'Z' and 'B' is a shared mount. The | |
| 519 | − mount 'A' is mounted on mount 'B' at dentry 'b'. Also new mounts 'A1', | |
| 520 | − 'A2'... 'An' are created and mounted at dentry 'b' on all mounts that | |
| 521 | − receive propagation from mount 'B'. A new propagation tree is created | |
| 522 | − in the exact same configuration as that of 'B'. This new propagation | |
| 523 | − tree contains all the new mounts 'A1', 'A2'... 'An'. And this new | |
| 524 | − propagation tree is appended to the already existing propagation tree of | |
| 525 | − 'A'. Mount 'A' continues to be the slave mount of 'Z' but it also | |
| 526 | − becomes 'shared'. | |
| 514 | + 3. 'A' is a slave mount of mount 'Z' and 'B' is a shared mount. The | |
| 515 | + mount 'A' is mounted on mount 'B' at dentry 'b'. Also new mounts 'A1', | |
| 516 | + 'A2'... 'An' are created and mounted at dentry 'b' on all mounts that | |
| 517 | + receive propagation from mount 'B'. A new propagation tree is created | |
| 518 | + in the exact same configuration as that of 'B'. This new propagation | |
| 519 | + tree contains all the new mounts 'A1', 'A2'... 'An'. And this new | |
| 520 | + propagation tree is appended to the already existing propagation tree of | |
| 521 | + 'A'. Mount 'A' continues to be the slave mount of 'Z' but it also | |
| 522 | + becomes 'shared'. | |
| 527 | 523 | |
| 528 | − 4. 'A' is a unbindable mount and 'B' is a shared mount. The operation | |
| 529 | − is invalid. Because mounting anything on the shared mount 'B' can | |
| 530 | − create new mounts that get mounted on the mounts that receive | |
| 531 | − propagation from 'B'. And since the mount 'A' is unbindable, cloning | |
| 532 | − it to mount at other mountpoints is not possible. | |
| 524 | + 4. 'A' is a unbindable mount and 'B' is a shared mount. The operation | |
| 525 | + is invalid. Because mounting anything on the shared mount 'B' can | |
| 526 | + create new mounts that get mounted on the mounts that receive | |
| 527 | + propagation from 'B'. And since the mount 'A' is unbindable, cloning | |
| 528 | + it to mount at other mountpoints is not possible. | |
| 533 | 529 | |
| 534 | − 5. 'A' is a private mount and 'B' is a non-shared(private or slave or | |
| 535 | − unbindable) mount. The mount 'A' is mounted on mount 'B' at dentry 'b'. | |
| 530 | + 5. 'A' is a private mount and 'B' is a non-shared(private or slave or | |
| 531 | + unbindable) mount. The mount 'A' is mounted on mount 'B' at dentry 'b'. | |
| 536 | 532 | |
| 537 | − 6. 'A' is a shared mount and 'B' is a non-shared mount. The mount 'A' | |
| 538 | − is mounted on mount 'B' at dentry 'b'. Mount 'A' continues to be a | |
| 539 | − shared mount. | |
| 533 | + 6. 'A' is a shared mount and 'B' is a non-shared mount. The mount 'A' | |
| 534 | + is mounted on mount 'B' at dentry 'b'. Mount 'A' continues to be a | |
| 535 | + shared mount. | |
| 540 | 536 | |
| 541 | − 7. 'A' is a slave mount of mount 'Z' and 'B' is a non-shared mount. | |
| 542 | − The mount 'A' is mounted on mount 'B' at dentry 'b'. Mount 'A' | |
| 543 | − continues to be a slave mount of mount 'Z'. | |
| 537 | + 7. 'A' is a slave mount of mount 'Z' and 'B' is a non-shared mount. | |
| 538 | + The mount 'A' is mounted on mount 'B' at dentry 'b'. Mount 'A' | |
| 539 | + continues to be a slave mount of mount 'Z'. | |
| 544 | 540 | |
| 545 | − 8. 'A' is a unbindable mount and 'B' is a non-shared mount. The mount | |
| 546 | − 'A' is mounted on mount 'B' at dentry 'b'. Mount 'A' continues to be a | |
| 547 | − unbindable mount. | |
| 541 | + 8. 'A' is a unbindable mount and 'B' is a non-shared mount. The mount | |
| 542 | + 'A' is mounted on mount 'B' at dentry 'b'. Mount 'A' continues to be a | |
| 543 | + unbindable mount. | |
| 548 | 544 | |
| 549 | −5e) Mount semantics | |
| 545 | +e) Mount semantics | |
| 550 | 546 | |
| 551 | − Consider the following command:: | |
| 547 | + Consider the following command:: | |
| 552 | 548 | |
| 553 | − mount device B/b | |
| 549 | + mount device B/b | |
| 554 | 550 | |
| 555 | − 'B' is the destination mount and 'b' is the dentry in the destination | |
| 556 | − mount. | |
| 551 | + 'B' is the destination mount and 'b' is the dentry in the destination | |
| 552 | + mount. | |
| 557 | 553 | |
| 558 | − The above operation is the same as bind operation with the exception | |
| 559 | − that the source mount is always a private mount. | |
| 554 | + The above operation is the same as bind operation with the exception | |
| 555 | + that the source mount is always a private mount. | |
| 560 | 556 | |
| 561 | 557 | |
| 562 | −5f) Unmount semantics | |
| 558 | +f) Unmount semantics | |
| 563 | 559 | |
| 564 | − Consider the following command:: | |
| 560 | + Consider the following command:: | |
| 565 | 561 | |
| 566 | − umount A | |
| 562 | + umount A | |
| 567 | 563 | |
| 568 | − where 'A' is a mount mounted on mount 'B' at dentry 'b'. | |
| 564 | + where 'A' is a mount mounted on mount 'B' at dentry 'b'. | |
| 569 | 565 | |
| 570 | − If mount 'B' is shared, then all most-recently-mounted mounts at dentry | |
| 571 | − 'b' on mounts that receive propagation from mount 'B' and does not have | |
| 572 | − sub-mounts within them are unmounted. | |
| 566 | + If mount 'B' is shared, then all most-recently-mounted mounts at dentry | |
| 567 | + 'b' on mounts that receive propagation from mount 'B' and does not have | |
| 568 | + sub-mounts within them are unmounted. | |
| 573 | 569 | |
| 574 | − Example: Let's say 'B1', 'B2', 'B3' are shared mounts that propagate to | |
| 575 | − each other. | |
| 570 | + Example: Let's say 'B1', 'B2', 'B3' are shared mounts that propagate to | |
| 571 | + each other. | |
| 576 | 572 | |
| 577 | − let's say 'A1', 'A2', 'A3' are first mounted at dentry 'b' on mount | |
| 578 | − 'B1', 'B2' and 'B3' respectively. | |
| 573 | + let's say 'A1', 'A2', 'A3' are first mounted at dentry 'b' on mount | |
| 574 | + 'B1', 'B2' and 'B3' respectively. | |
| 579 | 575 | |
| 580 | − let's say 'C1', 'C2', 'C3' are next mounted at the same dentry 'b' on | |
| 581 | − mount 'B1', 'B2' and 'B3' respectively. | |
| 576 | + let's say 'C1', 'C2', 'C3' are next mounted at the same dentry 'b' on | |
| 577 | + mount 'B1', 'B2' and 'B3' respectively. | |
| 582 | 578 | |
| 583 | − if 'C1' is unmounted, all the mounts that are most-recently-mounted on | |
| 584 | − 'B1' and on the mounts that 'B1' propagates-to are unmounted. | |
| 579 | + if 'C1' is unmounted, all the mounts that are most-recently-mounted on | |
| 580 | + 'B1' and on the mounts that 'B1' propagates-to are unmounted. | |
| 585 | 581 | |
| 586 | − 'B1' propagates to 'B2' and 'B3'. And the most recently mounted mount | |
| 587 | − on 'B2' at dentry 'b' is 'C2', and that of mount 'B3' is 'C3'. | |
| 582 | + 'B1' propagates to 'B2' and 'B3'. And the most recently mounted mount | |
| 583 | + on 'B2' at dentry 'b' is 'C2', and that of mount 'B3' is 'C3'. | |
| 588 | 584 | |
| 589 | − So all 'C1', 'C2' and 'C3' should be unmounted. | |
| 585 | + So all 'C1', 'C2' and 'C3' should be unmounted. | |
| 590 | 586 | |
| 591 | − If any of 'C2' or 'C3' has some child mounts, then that mount is not | |
| 592 | − unmounted, but all other mounts are unmounted. However if 'C1' is told | |
| 593 | − to be unmounted and 'C1' has some sub-mounts, the umount operation is | |
| 594 | − failed entirely. | |
| 587 | + If any of 'C2' or 'C3' has some child mounts, then that mount is not | |
| 588 | + unmounted, but all other mounts are unmounted. However if 'C1' is told | |
| 589 | + to be unmounted and 'C1' has some sub-mounts, the umount operation is | |
| 590 | + failed entirely. | |
| 595 | 591 | |
| 596 | −5g) Clone Namespace | |
| 592 | +g) Clone Namespace | |
| 597 | 593 | |
| 598 | − A cloned namespace contains all the mounts as that of the parent | |
| 599 | − namespace. | |
| 594 | + A cloned namespace contains all the mounts as that of the parent | |
| 595 | + namespace. | |
| 600 | 596 | |
| 601 | − Let's say 'A' and 'B' are the corresponding mounts in the parent and the | |
| 602 | − child namespace. | |
| 597 | + Let's say 'A' and 'B' are the corresponding mounts in the parent and the | |
| 598 | + child namespace. | |
| 603 | 599 | |
| 604 | − If 'A' is shared, then 'B' is also shared and 'A' and 'B' propagate to | |
| 605 | − each other. | |
| 600 | + If 'A' is shared, then 'B' is also shared and 'A' and 'B' propagate to | |
| 601 | + each other. | |
| 606 | 602 | |
| 607 | − If 'A' is a slave mount of 'Z', then 'B' is also the slave mount of | |
| 608 | − 'Z'. | |
| 603 | + If 'A' is a slave mount of 'Z', then 'B' is also the slave mount of | |
| 604 | + 'Z'. | |
| 609 | 605 | |
| 610 | − If 'A' is a private mount, then 'B' is a private mount too. | |
| 606 | + If 'A' is a private mount, then 'B' is a private mount too. | |
| 611 | 607 | |
| 612 | − If 'A' is unbindable mount, then 'B' is a unbindable mount too. | |
| 608 | + If 'A' is unbindable mount, then 'B' is a unbindable mount too. | |
| 613 | 609 | |
| 614 | 610 | |
| 615 | 611 | 6) Quiz |
| 616 | 612 | ------- |
| 617 | 613 | |
| 618 | − A. What is the result of the following command sequence? | |
| 614 | +A. What is the result of the following command sequence? | |
| 619 | 615 | |
| 620 | − :: | |
| 616 | + :: | |
| 621 | 617 | |
| 622 | − mount --bind /mnt /mnt | |
| 623 | − mount --make-shared /mnt | |
| 624 | − mount --bind /mnt /tmp | |
| 625 | − mount --move /tmp /mnt/1 | |
| 618 | + mount --bind /mnt /mnt | |
| 619 | + mount --make-shared /mnt | |
| 620 | + mount --bind /mnt /tmp | |
| 621 | + mount --move /tmp /mnt/1 | |
| 626 | 622 | |
| 627 | − what should be the contents of /mnt /mnt/1 /mnt/1/1 should be? | |
| 628 | − Should they all be identical? or should /mnt and /mnt/1 be | |
| 629 | − identical only? | |
| 623 | + what should be the contents of /mnt /mnt/1 /mnt/1/1 should be? | |
| 624 | + Should they all be identical? or should /mnt and /mnt/1 be | |
| 625 | + identical only? | |
| 630 | 626 | |
| 631 | 627 | |
| 632 | − B. What is the result of the following command sequence? | |
| 628 | +B. What is the result of the following command sequence? | |
| 633 | 629 | |
| 634 | − :: | |
| 630 | + :: | |
| 635 | 631 | |
| 636 | − mount --make-rshared / | |
| 637 | − mkdir -p /v/1 | |
| 638 | − mount --rbind / /v/1 | |
| 632 | + mount --make-rshared / | |
| 633 | + mkdir -p /v/1 | |
| 634 | + mount --rbind / /v/1 | |
| 639 | 635 | |
| 640 | − what should be the content of /v/1/v/1 be? | |
| 636 | + what should be the content of /v/1/v/1 be? | |
| 641 | 637 | |
| 642 | 638 | |
| 643 | − C. What is the result of the following command sequence? | |
| 639 | +C. What is the result of the following command sequence? | |
| 644 | 640 | |
| 645 | − :: | |
| 641 | + :: | |
| 646 | 642 | |
| 647 | − mount --bind /mnt /mnt | |
| 648 | − mount --make-shared /mnt | |
| 649 | − mkdir -p /mnt/1/2/3 /mnt/1/test | |
| 650 | − mount --bind /mnt/1 /tmp | |
| 651 | − mount --make-slave /mnt | |
| 652 | − mount --make-shared /mnt | |
| 653 | − mount --bind /mnt/1/2 /tmp1 | |
| 654 | − mount --make-slave /mnt | |
| 643 | + mount --bind /mnt /mnt | |
| 644 | + mount --make-shared /mnt | |
| 645 | + mkdir -p /mnt/1/2/3 /mnt/1/test | |
| 646 | + mount --bind /mnt/1 /tmp | |
| 647 | + mount --make-slave /mnt | |
| 648 | + mount --make-shared /mnt | |
| 649 | + mount --bind /mnt/1/2 /tmp1 | |
| 650 | + mount --make-slave /mnt | |
| 655 | 651 | |
| 656 | − At this point we have the first mount at /tmp and | |
| 657 | − its root dentry is 1. Let's call this mount 'A' | |
| 658 | − And then we have a second mount at /tmp1 with root | |
| 659 | − dentry 2. Let's call this mount 'B' | |
| 660 | − Next we have a third mount at /mnt with root dentry | |
| 661 | − mnt. Let's call this mount 'C' | |
| 652 | + At this point we have the first mount at /tmp and | |
| 653 | + its root dentry is 1. Let's call this mount 'A' | |
| 654 | + And then we have a second mount at /tmp1 with root | |
| 655 | + dentry 2. Let's call this mount 'B' | |
| 656 | + Next we have a third mount at /mnt with root dentry | |
| 657 | + mnt. Let's call this mount 'C' | |
| 662 | 658 | |
| 663 | − 'B' is the slave of 'A' and 'C' is a slave of 'B' | |
| 664 | − A -> B -> C | |
| 659 | + 'B' is the slave of 'A' and 'C' is a slave of 'B' | |
| 660 | + A -> B -> C | |
| 665 | 661 | |
| 666 | − at this point if we execute the following command | |
| 662 | + at this point if we execute the following command:: | |
| 667 | 663 | |
| 668 | − mount --bind /bin /tmp/test | |
| 664 | + mount --bind /bin /tmp/test | |
| 669 | 665 | |
| 670 | − The mount is attempted on 'A' | |
| 666 | + The mount is attempted on 'A' | |
| 671 | 667 | |
| 672 | − will the mount propagate to 'B' and 'C' ? | |
| 668 | + will the mount propagate to 'B' and 'C' ? | |
| 673 | 669 | |
| 674 | − what would be the contents of | |
| 675 | − /mnt/1/test be? | |
| 670 | + what would be the contents of | |
| 671 | + /mnt/1/test be? | |
| 676 | 672 | |
| 677 | 673 | 7) FAQ |
| 678 | 674 | ------ |
| 679 | 675 | |
| 680 | − Q1. Why is bind mount needed? How is it different from symbolic links? | |
| 681 | − symbolic links can get stale if the destination mount gets | |
| 682 | − unmounted or moved. Bind mounts continue to exist even if the | |
| 683 | − other mount is unmounted or moved. | |
| 676 | +1. Why is bind mount needed? How is it different from symbolic links? | |
| 684 | 677 | |
| 685 | − Q2. Why can't the shared subtree be implemented using exportfs? | |
| 678 | + symbolic links can get stale if the destination mount gets | |
| 679 | + unmounted or moved. Bind mounts continue to exist even if the | |
| 680 | + other mount is unmounted or moved. | |
| 686 | 681 | |
| 687 | − exportfs is a heavyweight way of accomplishing part of what | |
| 688 | − shared subtree can do. I cannot imagine a way to implement the | |
| 689 | − semantics of slave mount using exportfs? | |
| 682 | +2. Why can't the shared subtree be implemented using exportfs? | |
| 690 | 683 | |
| 691 | − Q3 Why is unbindable mount needed? | |
| 684 | + exportfs is a heavyweight way of accomplishing part of what | |
| 685 | + shared subtree can do. I cannot imagine a way to implement the | |
| 686 | + semantics of slave mount using exportfs? | |
| 692 | 687 | |
| 693 | − Let's say we want to replicate the mount tree at multiple | |
| 694 | − locations within the same subtree. | |
| 688 | +3. Why is unbindable mount needed? | |
| 695 | 689 | |
| 696 | − if one rbind mounts a tree within the same subtree 'n' times | |
| 697 | − the number of mounts created is an exponential function of 'n'. | |
| 698 | − Having unbindable mount can help prune the unneeded bind | |
| 699 | − mounts. Here is an example. | |
| 690 | + Let's say we want to replicate the mount tree at multiple | |
| 691 | + locations within the same subtree. | |
| 700 | 692 | |
| 701 | − step 1: | |
| 702 | − let's say the root tree has just two directories with | |
| 703 | − one vfsmount:: | |
| 693 | + if one rbind mounts a tree within the same subtree 'n' times | |
| 694 | + the number of mounts created is an exponential function of 'n'. | |
| 695 | + Having unbindable mount can help prune the unneeded bind | |
| 696 | + mounts. Here is an example. | |
| 704 | 697 | |
| 705 | − root | |
| 706 | − / \ | |
| 707 | − tmp usr | |
| 698 | + step 1: | |
| 699 | + let's say the root tree has just two directories with | |
| 700 | + one vfsmount:: | |
| 708 | 701 | |
| 709 | − And we want to replicate the tree at multiple | |
| 710 | − mountpoints under /root/tmp | |
| 702 | + root | |
| 703 | + / \ | |
| 704 | + tmp usr | |
| 711 | 705 | |
| 712 | − step 2: | |
| 713 | − :: | |
| 706 | + And we want to replicate the tree at multiple | |
| 707 | + mountpoints under /root/tmp | |
| 714 | 708 | |
| 709 | + step 2: | |
| 710 | + :: | |
| 715 | 711 | |
| 716 | − mount --make-shared /root | |
| 717 | 712 | |
| 718 | − mkdir -p /tmp/m1 | |
| 713 | + mount --make-shared /root | |
| 719 | 714 | |
| 720 | − mount --rbind /root /tmp/m1 | |
| 715 | + mkdir -p /tmp/m1 | |
| 721 | 716 | |
| 722 | − the new tree now looks like this:: | |
| 717 | + mount --rbind /root /tmp/m1 | |
| 723 | 718 | |
| 724 | − root | |
| 725 | − / \ | |
| 726 | − tmp usr | |
| 727 | − / | |
| 728 | − m1 | |
| 729 | − / \ | |
| 730 | − tmp usr | |
| 731 | − / | |
| 732 | − m1 | |
| 719 | + the new tree now looks like this:: | |
| 733 | 720 | |
| 734 | − it has two vfsmounts | |
| 721 | + root | |
| 722 | + / \ | |
| 723 | + tmp usr | |
| 724 | + / | |
| 725 | + m1 | |
| 726 | + / \ | |
| 727 | + tmp usr | |
| 728 | + / | |
| 729 | + m1 | |
| 735 | 730 | |
| 736 | − step 3: | |
| 737 | − :: | |
| 731 | + it has two vfsmounts | |
| 738 | 732 | |
| 739 | − mkdir -p /tmp/m2 | |
| 740 | − mount --rbind /root /tmp/m2 | |
| 733 | + step 3: | |
| 734 | + :: | |
| 741 | 735 | |
| 742 | − the new tree now looks like this:: | |
| 736 | + mkdir -p /tmp/m2 | |
| 737 | + mount --rbind /root /tmp/m2 | |
| 743 | 738 | |
| 744 | − root | |
| 745 | − / \ | |
| 746 | − tmp usr | |
| 747 | − / \ | |
| 748 | − m1 m2 | |
| 749 | − / \ / \ | |
| 750 | − tmp usr tmp usr | |
| 751 | − / \ / | |
| 752 | − m1 m2 m1 | |
| 753 | − / \ / \ | |
| 754 | − tmp usr tmp usr | |
| 755 | − / / \ | |
| 756 | − m1 m1 m2 | |
| 757 | − / \ | |
| 758 | − tmp usr | |
| 759 | − / \ | |
| 760 | − m1 m2 | |
| 739 | + the new tree now looks like this:: | |
| 761 | 740 | |
| 762 | − it has 6 vfsmounts | |
| 741 | + root | |
| 742 | + / \ | |
| 743 | + tmp usr | |
| 744 | + / \ | |
| 745 | + m1 m2 | |
| 746 | + / \ / \ | |
| 747 | + tmp usr tmp usr | |
| 748 | + / \ / | |
| 749 | + m1 m2 m1 | |
| 750 | + / \ / \ | |
| 751 | + tmp usr tmp usr | |
| 752 | + / / \ | |
| 753 | + m1 m1 m2 | |
| 754 | + / \ | |
| 755 | + tmp usr | |
| 756 | + / \ | |
| 757 | + m1 m2 | |
| 763 | 758 | |
| 764 | − step 4: | |
| 765 | − :: | |
| 766 | − mkdir -p /tmp/m3 | |
| 767 | − mount --rbind /root /tmp/m3 | |
| 759 | + it has 6 vfsmounts | |
| 768 | 760 | |
| 769 | − I won't draw the tree..but it has 24 vfsmounts | |
| 761 | + step 4: | |
| 762 | + :: | |
| 770 | 763 | |
| 764 | + mkdir -p /tmp/m3 | |
| 765 | + mount --rbind /root /tmp/m3 | |
| 771 | 766 | |
| 772 | − at step i the number of vfsmounts is V[i] = i*V[i-1]. | |
| 773 | − This is an exponential function. And this tree has way more | |
| 774 | − mounts than what we really needed in the first place. | |
| 767 | + I won't draw the tree..but it has 24 vfsmounts | |
| 775 | 768 | |
| 776 | − One could use a series of umount at each step to prune | |
| 777 | − out the unneeded mounts. But there is a better solution. | |
| 778 | − Unclonable mounts come in handy here. | |
| 779 | 769 | |
| 780 | − step 1: | |
| 781 | − let's say the root tree has just two directories with | |
| 782 | − one vfsmount:: | |
| 770 | + at step i the number of vfsmounts is V[i] = i*V[i-1]. | |
| 771 | + This is an exponential function. And this tree has way more | |
| 772 | + mounts than what we really needed in the first place. | |
| 783 | 773 | |
| 784 | − root | |
| 785 | − / \ | |
| 786 | − tmp usr | |
| 774 | + One could use a series of umount at each step to prune | |
| 775 | + out the unneeded mounts. But there is a better solution. | |
| 776 | + Unclonable mounts come in handy here. | |
| 787 | 777 | |
| 788 | − How do we set up the same tree at multiple locations under | |
| 789 | − /root/tmp | |
| 778 | + step 1: | |
| 779 | + let's say the root tree has just two directories with | |
| 780 | + one vfsmount:: | |
| 790 | 781 | |
| 791 | − step 2: | |
| 792 | − :: | |
| 782 | + root | |
| 783 | + / \ | |
| 784 | + tmp usr | |
| 793 | 785 | |
| 786 | + How do we set up the same tree at multiple locations under | |
| 787 | + /root/tmp | |
| 794 | 788 | |
| 795 | − mount --bind /root/tmp /root/tmp | |
| 789 | + step 2: | |
| 790 | + :: | |
| 796 | 791 | |
| 797 | − mount --make-rshared /root | |
| 798 | − mount --make-unbindable /root/tmp | |
| 799 | 792 | |
| 800 | − mkdir -p /tmp/m1 | |
| 793 | + mount --bind /root/tmp /root/tmp | |
| 801 | 794 | |
| 802 | − mount --rbind /root /tmp/m1 | |
| 795 | + mount --make-rshared /root | |
| 796 | + mount --make-unbindable /root/tmp | |
| 803 | 797 | |
| 804 | − the new tree now looks like this:: | |
| 798 | + mkdir -p /tmp/m1 | |
| 805 | 799 | |
| 806 | − root | |
| 807 | − / \ | |
| 808 | − tmp usr | |
| 809 | − / | |
| 810 | − m1 | |
| 811 | − / \ | |
| 812 | − tmp usr | |
| 800 | + mount --rbind /root /tmp/m1 | |
| 813 | 801 | |
| 814 | − step 3: | |
| 815 | − :: | |
| 802 | + the new tree now looks like this:: | |
| 816 | 803 | |
| 817 | − mkdir -p /tmp/m2 | |
| 818 | − mount --rbind /root /tmp/m2 | |
| 804 | + root | |
| 805 | + / \ | |
| 806 | + tmp usr | |
| 807 | + / | |
| 808 | + m1 | |
| 809 | + / \ | |
| 810 | + tmp usr | |
| 819 | 811 | |
| 820 | − the new tree now looks like this:: | |
| 812 | + step 3: | |
| 813 | + :: | |
| 821 | 814 | |
| 822 | − root | |
| 823 | − / \ | |
| 824 | − tmp usr | |
| 825 | − / \ | |
| 826 | − m1 m2 | |
| 827 | − / \ / \ | |
| 828 | − tmp usr tmp usr | |
| 815 | + mkdir -p /tmp/m2 | |
| 816 | + mount --rbind /root /tmp/m2 | |
| 829 | 817 | |
| 830 | − step 4: | |
| 831 | − :: | |
| 818 | + the new tree now looks like this:: | |
| 832 | 819 | |
| 833 | − mkdir -p /tmp/m3 | |
| 834 | − mount --rbind /root /tmp/m3 | |
| 820 | + root | |
| 821 | + / \ | |
| 822 | + tmp usr | |
| 823 | + / \ | |
| 824 | + m1 m2 | |
| 825 | + / \ / \ | |
| 826 | + tmp usr tmp usr | |
| 835 | 827 | |
| 836 | − the new tree now looks like this:: | |
| 828 | + step 4: | |
| 829 | + :: | |
| 837 | 830 | |
| 838 | − root | |
| 839 | − / \ | |
| 840 | − tmp usr | |
| 841 | − / \ \ | |
| 842 | − m1 m2 m3 | |
| 843 | − / \ / \ / \ | |
| 844 | − tmp usr tmp usr tmp usr | |
| 831 | + mkdir -p /tmp/m3 | |
| 832 | + mount --rbind /root /tmp/m3 | |
| 833 | + | |
| 834 | + the new tree now looks like this:: | |
| 835 | + | |
| 836 | + root | |
| 837 | + / \ | |
| 838 | + tmp usr | |
| 839 | + / \ \ | |
| 840 | + m1 m2 m3 | |
| 841 | + / \ / \ / \ | |
| 842 | + tmp usr tmp usr tmp usr | |
| 845 | 843 | |
| 846 | 844 | 8) Implementation |
| 847 | 845 | ----------------- |
| 848 | 846 | |
| 849 | −8A) Datastructure | |
| 847 | +A) Datastructure | |
| 848 | + | |
| 849 | + Several new fields are introduced to struct vfsmount: | |
| 850 | + | |
| 851 | + ->mnt_share | |
| 852 | + Links together all the mount to/from which this vfsmount | |
| 853 | + send/receives propagation events. | |
| 850 | 854 | |
| 851 | − 4 new fields are introduced to struct vfsmount: | |
| 855 | + ->mnt_slave_list | |
| 856 | + Links all the mounts to which this vfsmount propagates | |
| 857 | + to. | |
| 852 | 858 | |
| 853 | − * ->mnt_share | |
| 854 | − * ->mnt_slave_list | |
| 855 | − * ->mnt_slave | |
| 856 | − * ->mnt_master | |
| 859 | + ->mnt_slave | |
| 860 | + Links together all the slaves that its master vfsmount | |
| 861 | + propagates to. | |
| 857 | 862 | |
| 858 | − ->mnt_share | |
| 859 | − links together all the mount to/from which this vfsmount | |
| 860 | − send/receives propagation events. | |
| 863 | + ->mnt_master | |
| 864 | + Points to the master vfsmount from which this vfsmount | |
| 865 | + receives propagation. | |
| 861 | 866 | |
| 862 | − ->mnt_slave_list | |
| 863 | − links all the mounts to which this vfsmount propagates | |
| 864 | − to. | |
| 867 | + ->mnt_flags | |
| 868 | + Takes two more flags to indicate the propagation status of | |
| 869 | + the vfsmount. MNT_SHARE indicates that the vfsmount is a shared | |
| 870 | + vfsmount. MNT_UNCLONABLE indicates that the vfsmount cannot be | |
| 871 | + replicated. | |
| 865 | 872 | |
| 866 | − ->mnt_slave | |
| 867 | − links together all the slaves that its master vfsmount | |
| 868 | − propagates to. | |
| 873 | + All the shared vfsmounts in a peer group form a cyclic list through | |
| 874 | + ->mnt_share. | |
| 869 | 875 | |
| 870 | − ->mnt_master | |
| 871 | − points to the master vfsmount from which this vfsmount | |
| 872 | − receives propagation. | |
| 876 | + All vfsmounts with the same ->mnt_master form on a cyclic list anchored | |
| 877 | + in ->mnt_master->mnt_slave_list and going through ->mnt_slave. | |
| 873 | 878 | |
| 874 | − ->mnt_flags | |
| 875 | − takes two more flags to indicate the propagation status of | |
| 876 | − the vfsmount. MNT_SHARE indicates that the vfsmount is a shared | |
| 877 | − vfsmount. MNT_UNCLONABLE indicates that the vfsmount cannot be | |
| 878 | − replicated. | |
| 879 | + ->mnt_master can point to arbitrary (and possibly different) members | |
| 880 | + of master peer group. To find all immediate slaves of a peer group | |
| 881 | + you need to go through _all_ ->mnt_slave_list of its members. | |
| 882 | + Conceptually it's just a single set - distribution among the | |
| 883 | + individual lists does not affect propagation or the way propagation | |
| 884 | + tree is modified by operations. | |
| 879 | 885 | |
| 880 | − All the shared vfsmounts in a peer group form a cyclic list through | |
| 881 | − ->mnt_share. | |
| 886 | + All vfsmounts in a peer group have the same ->mnt_master. If it is | |
| 887 | + non-NULL, they form a contiguous (ordered) segment of slave list. | |
| 882 | 888 | |
| 883 | − All vfsmounts with the same ->mnt_master form on a cyclic list anchored | |
| 884 | − in ->mnt_master->mnt_slave_list and going through ->mnt_slave. | |
| 889 | + A example propagation tree looks as shown in the figure below. | |
| 885 | 890 | |
| 886 | − ->mnt_master can point to arbitrary (and possibly different) members | |
| 887 | − of master peer group. To find all immediate slaves of a peer group | |
| 888 | − you need to go through _all_ ->mnt_slave_list of its members. | |
| 889 | − Conceptually it's just a single set - distribution among the | |
| 890 | − individual lists does not affect propagation or the way propagation | |
| 891 | − tree is modified by operations. | |
| 891 | + .. note:: | |
| 892 | + Though it looks like a forest, if we consider all the shared | |
| 893 | + mounts as a conceptual entity called 'pnode', it becomes a tree. | |
| 892 | 894 | |
| 893 | − All vfsmounts in a peer group have the same ->mnt_master. If it is | |
| 894 | − non-NULL, they form a contiguous (ordered) segment of slave list. | |
| 895 | + :: | |
| 895 | 896 | |
| 896 | − A example propagation tree looks as shown in the figure below. | |
| 897 | − [ NOTE: Though it looks like a forest, if we consider all the shared | |
| 898 | − mounts as a conceptual entity called 'pnode', it becomes a tree]:: | |
| 899 | 897 | |
| 898 | + A <--> B <--> C <---> D | |
| 899 | + /|\ /| |\ | |
| 900 | + / F G J K H I | |
| 901 | + / | |
| 902 | + E<-->K | |
| 903 | + /|\ | |
| 904 | + M L N | |
| 900 | 905 | |
| 901 | − A <--> B <--> C <---> D | |
| 902 | − /|\ /| |\ | |
| 903 | − / F G J K H I | |
| 904 | − / | |
| 905 | − E<-->K | |
| 906 | − /|\ | |
| 907 | − M L N | |
| 906 | + In the above figure A,B,C and D all are shared and propagate to each | |
| 907 | + other. 'A' has got 3 slave mounts 'E' 'F' and 'G' 'C' has got 2 slave | |
| 908 | + mounts 'J' and 'K' and 'D' has got two slave mounts 'H' and 'I'. | |
| 909 | + 'E' is also shared with 'K' and they propagate to each other. And | |
| 910 | + 'K' has 3 slaves 'M', 'L' and 'N' | |
| 908 | 911 | |
| 909 | − In the above figure A,B,C and D all are shared and propagate to each | |
| 910 | − other. 'A' has got 3 slave mounts 'E' 'F' and 'G' 'C' has got 2 slave | |
| 911 | − mounts 'J' and 'K' and 'D' has got two slave mounts 'H' and 'I'. | |
| 912 | − 'E' is also shared with 'K' and they propagate to each other. And | |
| 913 | − 'K' has 3 slaves 'M', 'L' and 'N' | |
| 912 | + A's ->mnt_share links with the ->mnt_share of 'B' 'C' and 'D' | |
| 914 | 913 | |
| 915 | − A's ->mnt_share links with the ->mnt_share of 'B' 'C' and 'D' | |
| 914 | + A's ->mnt_slave_list links with ->mnt_slave of 'E', 'K', 'F' and 'G' | |
| 916 | 915 | |
| 917 | − A's ->mnt_slave_list links with ->mnt_slave of 'E', 'K', 'F' and 'G' | |
| 916 | + E's ->mnt_share links with ->mnt_share of K | |
| 918 | 917 | |
| 919 | − E's ->mnt_share links with ->mnt_share of K | |
| 918 | + 'E', 'K', 'F', 'G' have their ->mnt_master point to struct vfsmount of 'A' | |
| 920 | 919 | |
| 921 | − 'E', 'K', 'F', 'G' have their ->mnt_master point to struct vfsmount of 'A' | |
| 920 | + 'M', 'L', 'N' have their ->mnt_master point to struct vfsmount of 'K' | |
| 922 | 921 | |
| 923 | − 'M', 'L', 'N' have their ->mnt_master point to struct vfsmount of 'K' | |
| 922 | + K's ->mnt_slave_list links with ->mnt_slave of 'M', 'L' and 'N' | |
| 924 | 923 | |
| 925 | − K's ->mnt_slave_list links with ->mnt_slave of 'M', 'L' and 'N' | |
| 924 | + C's ->mnt_slave_list links with ->mnt_slave of 'J' and 'K' | |
| 926 | 925 | |
| 927 | − C's ->mnt_slave_list links with ->mnt_slave of 'J' and 'K' | |
| 926 | + J and K's ->mnt_master points to struct vfsmount of C | |
| 928 | 927 | |
| 929 | − J and K's ->mnt_master points to struct vfsmount of C | |
| 928 | + and finally D's ->mnt_slave_list links with ->mnt_slave of 'H' and 'I' | |
| 930 | 929 | |
| 931 | − and finally D's ->mnt_slave_list links with ->mnt_slave of 'H' and 'I' | |
| 930 | + 'H' and 'I' have their ->mnt_master pointing to struct vfsmount of 'D'. | |
| 932 | 931 | |
| 933 | − 'H' and 'I' have their ->mnt_master pointing to struct vfsmount of 'D'. | |
| 934 | 932 | |
| 933 | + NOTE: The propagation tree is orthogonal to the mount tree. | |
| 935 | 934 | |
| 936 | − NOTE: The propagation tree is orthogonal to the mount tree. | |
| 935 | +B) Locking: | |
| 937 | 936 | |
| 938 | −8B Locking: | |
| 937 | + ->mnt_share, ->mnt_slave, ->mnt_slave_list, ->mnt_master are protected | |
| 938 | + by namespace_sem (exclusive for modifications, shared for reading). | |
| 939 | 939 | |
| 940 | − ->mnt_share, ->mnt_slave, ->mnt_slave_list, ->mnt_master are protected | |
| 941 | − by namespace_sem (exclusive for modifications, shared for reading). | |
| 940 | + Normally we have ->mnt_flags modifications serialized by vfsmount_lock. | |
| 941 | + There are two exceptions: do_add_mount() and clone_mnt(). | |
| 942 | + The former modifies a vfsmount that has not been visible in any shared | |
| 943 | + data structures yet. | |
| 944 | + The latter holds namespace_sem and the only references to vfsmount | |
| 945 | + are in lists that can't be traversed without namespace_sem. | |
| 942 | 946 | |
| 943 | − Normally we have ->mnt_flags modifications serialized by vfsmount_lock. | |
| 944 | − There are two exceptions: do_add_mount() and clone_mnt(). | |
| 945 | − The former modifies a vfsmount that has not been visible in any shared | |
| 946 | − data structures yet. | |
| 947 | − The latter holds namespace_sem and the only references to vfsmount | |
| 948 | − are in lists that can't be traversed without namespace_sem. | |
| 947 | +C) Algorithm: | |
| 949 | 948 | |
| 950 | −8C Algorithm: | |
| 949 | + The crux of the implementation resides in rbind/move operation. | |
| 951 | 950 | |
| 952 | − The crux of the implementation resides in rbind/move operation. | |
| 951 | + The overall algorithm breaks the operation into 3 phases: (look at | |
| 952 | + attach_recursive_mnt() and propagate_mnt()) | |
| 953 | 953 | |
| 954 | − The overall algorithm breaks the operation into 3 phases: (look at | |
| 955 | − attach_recursive_mnt() and propagate_mnt()) | |
| 954 | + 1. Prepare phase. | |
| 956 | 955 | |
| 957 | − 1. prepare phase. | |
| 958 | − 2. commit phases. | |
| 959 | − 3. abort phases. | |
| 956 | + For each mount in the source tree: | |
| 960 | 957 | |
| 961 | − Prepare phase: | |
| 958 | + a) Create the necessary number of mount trees to | |
| 959 | + be attached to each of the mounts that receive | |
| 960 | + propagation from the destination mount. | |
| 961 | + b) Do not attach any of the trees to its destination. | |
| 962 | + However note down its ->mnt_parent and ->mnt_mountpoint | |
| 963 | + c) Link all the new mounts to form a propagation tree that | |
| 964 | + is identical to the propagation tree of the destination | |
| 965 | + mount. | |
| 962 | 966 | |
| 963 | − for each mount in the source tree: | |
| 967 | + If this phase is successful, there should be 'n' new | |
| 968 | + propagation trees; where 'n' is the number of mounts in the | |
| 969 | + source tree. Go to the commit phase | |
| 964 | 970 | |
| 965 | − a) Create the necessary number of mount trees to | |
| 966 | − be attached to each of the mounts that receive | |
| 967 | − propagation from the destination mount. | |
| 968 | − b) Do not attach any of the trees to its destination. | |
| 969 | − However note down its ->mnt_parent and ->mnt_mountpoint | |
| 970 | − c) Link all the new mounts to form a propagation tree that | |
| 971 | − is identical to the propagation tree of the destination | |
| 972 | − mount. | |
| 971 | + Also there should be 'm' new mount trees, where 'm' is | |
| 972 | + the number of mounts to which the destination mount | |
| 973 | + propagates to. | |
| 973 | 974 | |
| 974 | − If this phase is successful, there should be 'n' new | |
| 975 | − propagation trees; where 'n' is the number of mounts in the | |
| 976 | − source tree. Go to the commit phase | |
| 975 | + If any memory allocations fail, go to the abort phase. | |
| 977 | 976 | |
| 978 | − Also there should be 'm' new mount trees, where 'm' is | |
| 979 | − the number of mounts to which the destination mount | |
| 980 | − propagates to. | |
| 977 | + 2. Commit phase. | |
| 981 | 978 | |
| 982 | − if any memory allocations fail, go to the abort phase. | |
| 979 | + Attach each of the mount trees to their corresponding | |
| 980 | + destination mounts. | |
| 983 | 981 | |
| 984 | − Commit phase | |
| 985 | − attach each of the mount trees to their corresponding | |
| 986 | − destination mounts. | |
| 982 | + 3. Abort phase. | |
| 987 | 983 | |
| 988 | − Abort phase | |
| 989 | − delete all the newly created trees. | |
| 984 | + Delete all the newly created trees. | |
| 990 | 985 | |
| 991 | − .. Note:: | |
| 992 | − all the propagation related functionality resides in the file pnode.c | |
| 986 | + .. Note:: | |
| 987 | + all the propagation related functionality resides in the file pnode.c | |
| 993 | 988 | |
| 994 | 989 | |
| 995 | 990 | ------------------------------------------------------------------------ |
| @@ -243,8 +243,8 @@ Other notes: · 242 unmodified lines | ||
| 243 | 243 | - show() methods should return the number of bytes printed into the |
| 244 | 244 | buffer. |
| 245 | 245 | |
| 246 | −- show() should only use sysfs_emit() or sysfs_emit_at() when formatting | |
| 247 | − the value to be returned to user space. | |
| 246 | +- New implementations of show() methods should only use sysfs_emit() or | |
| 247 | + sysfs_emit_at() when formatting the value to be returned to user space. | |
| 248 | 248 | |
| 249 | 249 | - store() should return the number of bytes used from the buffer. If the |
| 250 | 250 | entire buffer has been used, just return the count argument. |
| @@ -299,7 +299,6 @@ The top level sysfs directory looks like:: · 48 unmodified lines | ||
| 299 | 299 | hypervisor/ |
| 300 | 300 | kernel/ |
| 301 | 301 | module/ |
| 302 | − net/ | |
| 303 | 302 | power/ |
| 304 | 303 | |
| 305 | 304 | devices/ contains a filesystem representation of the device tree. It maps |
| @@ -313,7 +312,7 @@ kernel. Each bus's directory contains two subdirectories:: · 7 unmodified lines | ||
| 313 | 312 | drivers/ |
| 314 | 313 | |
| 315 | 314 | devices/ contains symlinks for each device discovered in the system |
| 316 | −that point to the device's directory under root/. | |
| 315 | +that point to the device's directory under /sys/devices. | |
| 317 | 316 | |
| 318 | 317 | drivers/ contains a directory for each device driver that is loaded |
| 319 | 318 | for devices on that particular bus (this assumes that drivers do not |
| @@ -328,15 +327,29 @@ loaded system modules, for both builtin and loadable modules. · 8 unmodified lines | ||
| 328 | 327 | |
| 329 | 328 | dev/ contains two directories: char/ and block/. Inside these two |
| 330 | 329 | directories there are symlinks named <major>:<minor>. These symlinks |
| 331 | −point to the sysfs directory for the given device. /sys/dev provides a | |
| 330 | +point to the directories under /sys/devices for each device. /sys/dev provides a | |
| 332 | 331 | quick way to lookup the sysfs interface for a device from the result of |
| 333 | 332 | a stat(2) operation. |
| 334 | 333 | |
| 335 | 334 | More information on driver-model specific features can be found in |
| 336 | 335 | Documentation/driver-api/driver-model/. |
| 337 | 336 | |
| 337 | +block/ contains symlinks to all the block devices discovered on the system. | |
| 338 | +These symlinks point to directories under /sys/devices. | |
| 338 | 339 | |
| 339 | −TODO: Finish this section. | |
| 340 | +class/ contains a directory for each device class, grouped by functional type. | |
| 341 | +Each directory in class/ contains symlinks to devices in the /sys/devices directory. | |
| 342 | + | |
| 343 | +firmware/ contains system firmware data and configuration such as firmware tables, | |
| 344 | +ACPI information, and device tree data. | |
| 345 | + | |
| 346 | +hypervisor/ contains virtualization platform information and provides an interface to | |
| 347 | +the underlying hypervisor. It is only present when running on a virtual machine. | |
| 348 | + | |
| 349 | +kernel/ contains runtime kernel parameters, configuration settings, and status. | |
| 350 | + | |
| 351 | +power/ contains power management subsystem information including | |
| 352 | +sleep states, suspend/resume capabilities, and policies. | |
| 340 | 353 | |
| 341 | 354 | |
| 342 | 355 | Current Interfaces |
| @@ -454,7 +454,7 @@ filesystem so that it can apply pending filesystem updates to the staging · 453 unmodified lines | ||
| 454 | 454 | information. |
| 455 | 455 | Once the scan is done, the owning object is re-locked, the live data is used to |
| 456 | 456 | write a new ondisk structure, and the repairs are committed atomically. |
| 457 | −The hooks are disabled and the staging staging area is freed. | |
| 457 | +The hooks are disabled and the staging area is freed. | |
| 458 | 458 | Finally, the storage from the old data structure are carefully reaped. |
| 459 | 459 | |
| 460 | 460 | Introducing concurrency helps online repair avoid various locking problems, but |
| @@ -475,7 +475,7 @@ operation, which may cause application failure or an unplanned filesystem · 14 unmodified lines | ||
| 475 | 475 | shutdown. |
| 476 | 476 | |
| 477 | 477 | Inspiration for the secondary metadata repair strategy was drawn from section |
| 478 | −2.4 of Srinivasan above, and sections 2 ("NSF: Inded Build Without Side-File") | |
| 478 | +2.4 of Srinivasan above, and sections 2 ("NSF: Index Build Without Side-File") | |
| 479 | 479 | and 3.1.1 ("Duplicate Key Insert Problem") in C. Mohan, `"Algorithms for |
| 480 | 480 | Creating Indexes for Very Large Tables Without Quiescing Updates" |
| 481 | 481 | <https://dl.acm.org/doi/10.1145/130283.130337>`_, 1992. |
| @@ -2185,7 +2185,7 @@ The chapter about :ref:`secondary metadata<secondary_metadata>` mentioned that · 1703 unmodified lines | ||
| 2185 | 2185 | checking and repairing of secondary metadata commonly requires coordination |
| 2186 | 2186 | between a live metadata scan of the filesystem and writer threads that are |
| 2187 | 2187 | updating that metadata. |
| 2188 | −Keeping the scan data up to date requires requires the ability to propagate | |
| 2188 | +Keeping the scan data up to date requires the ability to propagate | |
| 2189 | 2189 | metadata updates from the filesystem into the data being collected by the scan. |
| 2190 | 2190 | This *can* be done by appending concurrent updates into a separate log file and |
| 2191 | 2191 | applying them before writing the new metadata to disk, but this leads to |
| @@ -4179,7 +4179,7 @@ When the exchange is initiated, the sequence of operations is as follows: · 1987 unmodified lines | ||
| 4179 | 4179 | This will be discussed in more detail in subsequent sections. |
| 4180 | 4180 | |
| 4181 | 4181 | If the filesystem goes down in the middle of an operation, log recovery will |
| 4182 | −find the most recent unfinished maping exchange log intent item and restart | |
| 4182 | +find the most recent unfinished mapping exchange log intent item and restart | |
| 4183 | 4183 | from there. |
| 4184 | 4184 | This is how atomic file mapping exchanges guarantees that an outside observer |
| 4185 | 4185 | will either see the old broken structure or the new one, and never a mismash of |
| @@ -204,6 +204,27 @@ per-CPU data structures on a non PREEMPT_RT kernel. · 203 unmodified lines | ||
| 204 | 204 | local_lock is not suitable to protect against preemption or interrupts on a |
| 205 | 205 | PREEMPT_RT kernel due to the PREEMPT_RT specific spinlock_t semantics. |
| 206 | 206 | |
| 207 | +CPU local scope and bottom-half | |
| 208 | +------------------------------- | |
| 209 | + | |
| 210 | +Per-CPU variables that are accessed only in softirq context should not rely on | |
| 211 | +the assumption that this context is implicitly protected due to being | |
| 212 | +non-preemptible. In a PREEMPT_RT kernel, softirq context is preemptible, and | |
| 213 | +synchronizing every bottom-half-disabled section via implicit context results | |
| 214 | +in an implicit per-CPU "big kernel lock." | |
| 215 | + | |
| 216 | +A local_lock_t together with local_lock_nested_bh() and | |
| 217 | +local_unlock_nested_bh() for locking operations help to identify the locking | |
| 218 | +scope. | |
| 219 | + | |
| 220 | +When lockdep is enabled, these functions verify that data structure access | |
| 221 | +occurs within softirq context. | |
| 222 | +Unlike local_lock(), local_unlock_nested_bh() does not disable preemption and | |
| 223 | +does not add overhead when used without lockdep. | |
| 224 | + | |
| 225 | +On a PREEMPT_RT kernel, local_lock_t behaves as a real lock and | |
| 226 | +local_unlock_nested_bh() serializes access to the data structure, which allows | |
| 227 | +removal of serialization via local_bh_disable(). | |
| 207 | 228 | |
| 208 | 229 | raw_spinlock_t and spinlock_t |
| 209 | 230 | ============================= |
| @@ -1,3 +1,5 @@ | ||
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | + | |
| 1 | 3 | ====================================== |
| 2 | 4 | Sequence counters and sequential locks |
| 3 | 5 | ====================================== |
| @@ -28,31 +28,3 @@ You may also like to tell ``gpg`` which ``tty`` to use (add to your shell · 27 unmodified lines | ||
| 28 | 28 | rc file):: |
| 29 | 29 | |
| 30 | 30 | export GPG_TTY=$(tty) |
| 31 | − | |
| 32 | − | |
| 33 | −Creating commit links to lore.kernel.org | |
| 34 | −---------------------------------------- | |
| 35 | − | |
| 36 | −The web site https://lore.kernel.org is meant as a grand archive of all mail | |
| 37 | −list traffic concerning or influencing the kernel development. Storing archives | |
| 38 | −of patches here is a recommended practice, and when a maintainer applies a | |
| 39 | −patch to a subsystem tree, it is a good idea to provide a Link: tag with a | |
| 40 | −reference back to the lore archive so that people that browse the commit | |
| 41 | −history can find related discussions and rationale behind a certain change. | |
| 42 | −The link tag will look like this:: | |
| 43 | − | |
| 44 | − Link: https://lore.kernel.org/r/<message-id> | |
| 45 | − | |
| 46 | −This can be configured to happen automatically any time you issue ``git am`` | |
| 47 | −by adding the following hook into your git:: | |
| 48 | − | |
| 49 | − $ git config am.messageid true | |
| 50 | − $ cat >.git/hooks/applypatch-msg <<'EOF' | |
| 51 | − #!/bin/sh | |
| 52 | − . git-sh-setup | |
| 53 | − perl -pi -e 's|^Message-I[dD]:\s*<?([^>]+)>?$|Link: https://lore.kernel.org/r/$1|g;' "$1" | |
| 54 | − test -x "$GIT_DIR/hooks/commit-msg" && | |
| 55 | − exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"} | |
| 56 | − : | |
| 57 | − EOF | |
| 58 | − $ chmod a+x .git/hooks/applypatch-msg | |
| @@ -59,6 +59,7 @@ week) that patches might be considered for merging and when patches need to · 58 unmodified lines | ||
| 59 | 59 | wait for the next -rc. At a minimum: |
| 60 | 60 | |
| 61 | 61 | - Last -rc for new feature submissions: |
| 62 | + | |
| 62 | 63 | New feature submissions targeting the next merge window should have |
| 63 | 64 | their first posting for consideration before this point. Patches that |
| 64 | 65 | are submitted after this point should be clear that they are targeting |
| @@ -68,6 +69,7 @@ wait for the next -rc. At a minimum: · 3 unmodified lines | ||
| 68 | 69 | submissions should appear before -rc5. |
| 69 | 70 | |
| 70 | 71 | - Last -rc to merge features: Deadline for merge decisions |
| 72 | + | |
| 71 | 73 | Indicate to contributors the point at which an as yet un-applied patch |
| 72 | 74 | set will need to wait for the NEXT+1 merge window. Of course there is no |
| 73 | 75 | obligation to ever accept any given patchset, but if the review has not |
| @@ -171,6 +171,8 @@ nodes with particular properties as defined by ``enum node_states``: · 170 unmodified lines | ||
| 171 | 171 | The node has memory(regular, high, movable) |
| 172 | 172 | ``N_CPU`` |
| 173 | 173 | The node has one or more CPUs |
| 174 | +``N_GENERIC_INITIATOR`` | |
| 175 | + The node has one or more Generic Initiators | |
| 174 | 176 | |
| 175 | 177 | For each node that has a property described above, the bit corresponding to the |
| 176 | 178 | node ID in the ``node_states[<property>]`` bitmask is set. |
| @@ -539,7 +539,7 @@ CAN Filter Usage Optimisation · 538 unmodified lines | ||
| 539 | 539 | The CAN filters are processed in per-device filter lists at CAN frame |
| 540 | 540 | reception time. To reduce the number of checks that need to be performed |
| 541 | 541 | while walking through the filter lists the CAN core provides an optimized |
| 542 | −filter handling when the filter subscription focusses on a single CAN ID. | |
| 542 | +filter handling when the filter subscription focuses on a single CAN ID. | |
| 543 | 543 | |
| 544 | 544 | For the possible 2048 SFF CAN identifiers the identifier is used as an index |
| 545 | 545 | to access the corresponding subscription list without any further checks. |
| @@ -42,7 +42,7 @@ Port's netdev devices have to be in UP before joining to the bridge to avoid · 41 unmodified lines | ||
| 42 | 42 | overwriting of bridge configuration as CPSW switch driver completely reloads its |
| 43 | 43 | configuration when first port changes its state to UP. |
| 44 | 44 | |
| 45 | −When the both interfaces joined the bridge - CPSW switch driver will enable | |
| 45 | +When both interfaces have joined the bridge - CPSW switch driver will enable | |
| 46 | 46 | marking packets with offload_fwd_mark flag. |
| 47 | 47 | |
| 48 | 48 | All configuration is implemented via switchdev API. |
| @@ -92,7 +92,7 @@ Port's netdev devices have to be in UP before joining to the bridge to avoid · 91 unmodified lines | ||
| 92 | 92 | overwriting of bridge configuration as CPSW switch driver copletly reloads its |
| 93 | 93 | configuration when first Port changes its state to UP. |
| 94 | 94 | |
| 95 | −When the both interfaces joined the bridge - CPSW switch driver will enable | |
| 95 | +When both interfaces have joined the bridge - CPSW switch driver will enable | |
| 96 | 96 | marking packets with offload_fwd_mark flag unless "ale_bypass=0" |
| 97 | 97 | |
| 98 | 98 | All configuration is implemented via switchdev API. |
| @@ -339,7 +339,7 @@ The send path · 338 unmodified lines | ||
| 339 | 339 | rds_sendmsg() |
| 340 | 340 | - struct rds_message built from incoming data |
| 341 | 341 | - CMSGs parsed (e.g. RDMA ops) |
| 342 | − - transport connection alloced and connected if not already | |
| 342 | + - transport connection allocated and connected if not already | |
| 343 | 343 | - rds_message placed on send queue |
| 344 | 344 | - send worker awoken |
| 345 | 345 | |
| @@ -472,7 +472,7 @@ in the device tree from the root bridge to a leaf device contains both of them). · 471 unmodified lines | ||
| 472 | 472 | The pci_pm_suspend_noirq() routine is executed after suspend_device_irqs() has |
| 473 | 473 | been called, which means that the device driver's interrupt handler won't be |
| 474 | 474 | invoked while this routine is running. It first checks if the device's driver |
| 475 | −implements legacy PCI suspends routines (Section 3), in which case the legacy | |
| 475 | +implements legacy PCI suspend routines (Section 3), in which case the legacy | |
| 476 | 476 | late suspend routine is called and its result is returned (the standard |
| 477 | 477 | configuration registers of the device are saved if the driver's callback hasn't |
| 478 | 478 | done that). Second, if the device driver's struct dev_pm_ops object is not |
| @@ -544,7 +544,7 @@ result is then returned). · 65 unmodified lines | ||
| 544 | 544 | The resume phase is carried out asynchronously for PCI devices, like the |
| 545 | 545 | suspend phase described above, which means that if two PCI devices don't depend |
| 546 | 546 | on each other in a known way, the pci_pm_resume() routine may be executed for |
| 547 | −the both of them in parallel. | |
| 547 | +both of them in parallel. | |
| 548 | 548 | |
| 549 | 549 | The pci_pm_complete() routine only executes the device driver's pm->complete() |
| 550 | 550 | callback, if defined. |
| @@ -13,7 +13,7 @@ infrastructure uses it internally? And where do they share common code? · 12 unmodified lines | ||
| 13 | 13 | |
| 14 | 14 | Well, a picture is worth a thousand words... So ASCII art follows :-) |
| 15 | 15 | |
| 16 | −[This depicts the current design in the kernel, and focusses only on the | |
| 16 | +[This depicts the current design in the kernel, and focuses only on the | |
| 17 | 17 | interactions involving the freezer and CPU hotplug and also tries to explain |
| 18 | 18 | the locking involved. It outlines the notifications involved as well. |
| 19 | 19 | But please note that here, only the call paths are illustrated, with the aim |
| @@ -207,10 +207,9 @@ document with a specification implemented by the patch:: · 206 unmodified lines | ||
| 207 | 207 | |
| 208 | 208 | Link: https://example.com/somewhere.html optional-other-stuff |
| 209 | 209 | |
| 210 | −Many maintainers when applying a patch also add this tag to link to the | |
| 211 | −latest public review posting of the patch; often this is automatically done | |
| 212 | −by tools like b4 or a git hook like the one described in | |
| 213 | −'Documentation/maintainer/configure-git.rst'. | |
| 210 | +As per guidance from the Chief Penguin, a Link: tag should only be added to | |
| 211 | +a commit if it leads to useful information that is not found in the commit | |
| 212 | +itself. | |
| 214 | 213 | |
| 215 | 214 | If the URL points to a public bug report being fixed by the patch, use the |
| 216 | 215 | "Closes:" tag instead:: |
| @@ -61,7 +61,7 @@ Sphinx\ [#f1]_ 3.4.3 sphinx-build --version · 60 unmodified lines | ||
| 61 | 61 | GNU tar 1.28 tar --version |
| 62 | 62 | gtags (optional) 6.6.5 gtags --version |
| 63 | 63 | mkimage (optional) 2017.01 mkimage --version |
| 64 | −Python (optional) 3.9.x python3 --version | |
| 64 | +Python 3.9.x python3 --version | |
| 65 | 65 | GNU AWK (optional) 5.1.0 gawk --version |
| 66 | 66 | ====================== =============== ======================================== |
| 67 | 67 | |
| @@ -154,6 +154,13 @@ Perl · 86 unmodified lines | ||
| 154 | 154 | You will need perl 5 and the following modules: ``Getopt::Long``, |
| 155 | 155 | ``Getopt::Std``, ``File::Basename``, and ``File::Find`` to build the kernel. |
| 156 | 156 | |
| 157 | +Python | |
| 158 | +------ | |
| 159 | + | |
| 160 | +Several config options require it: it is required for arm/arm64 | |
| 161 | +default configs, CONFIG_LTO_CLANG, some DRM optional configs, | |
| 162 | +the kernel-doc tool, and docs build (Sphinx), among others. | |
| 163 | + | |
| 157 | 164 | BC |
| 158 | 165 | -- |
| 159 | 166 | |
| @@ -49,7 +49,7 @@ hosting infrastructure, regardless of how good the security practices · 48 unmodified lines | ||
| 49 | 49 | for the latter may be. |
| 50 | 50 | |
| 51 | 51 | The above guiding principle is the reason why this guide is needed. We |
| 52 | −want to make sure that by placing trust into developers we do not simply | |
| 52 | +want to make sure that by placing trust into developers we do not merely | |
| 53 | 53 | shift the blame for potential future security incidents to someone else. |
| 54 | 54 | The goal is to provide a set of guidelines developers can use to create |
| 55 | 55 | a secure working environment and safeguard the PGP keys used to |
| @@ -60,7 +60,7 @@ establish the integrity of the Linux kernel itself. · 4 unmodified lines | ||
| 60 | 60 | PGP tools |
| 61 | 61 | ========= |
| 62 | 62 | |
| 63 | −Use GnuPG 2.2 or later | |
| 63 | +Use GnuPG 2.4 or later | |
| 64 | 64 | ---------------------- |
| 65 | 65 | |
| 66 | 66 | Your distro should already have GnuPG installed by default, you just |
| @@ -69,9 +69,9 @@ To check, run:: · 2 unmodified lines | ||
| 69 | 69 | |
| 70 | 70 | $ gpg --version | head -n1 |
| 71 | 71 | |
| 72 | −If you have version 2.2 or above, then you are good to go. If you have a | |
| 73 | −version that is prior than 2.2, then some commands from this guide may | |
| 74 | −not work. | |
| 72 | +If you have version 2.4 or above, then you are good to go. If you have | |
| 73 | +an earlier version, then you are using a release of GnuPG that is no | |
| 74 | +longer maintained and some commands from this guide may not work. | |
| 75 | 75 | |
| 76 | 76 | Configure gpg-agent options |
| 77 | 77 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| @@ -199,13 +199,6 @@ separate signing subkey:: · 121 unmodified lines | ||
| 199 | 199 | |
| 200 | 200 | $ gpg --quick-addkey [fpr] ed25519 sign |
| 201 | 201 | |
| 202 | −.. note:: ECC support in GnuPG | |
| 203 | − | |
| 204 | − Note, that if you intend to use a hardware token that does not | |
| 205 | − support ED25519 ECC keys, you should choose "nistp256" instead or | |
| 206 | − "ed25519." See the section below on recommended hardware devices. | |
| 207 | − | |
| 208 | − | |
| 209 | 202 | Back up your Certify key for disaster recovery |
| 210 | 203 | ---------------------------------------------- |
| 211 | 204 | |
| @@ -213,7 +206,7 @@ The more signatures you have on your PGP key from other developers, the · 1 unmodified line | ||
| 213 | 206 | more reasons you have to create a backup version that lives on something |
| 214 | 207 | other than digital media, for disaster recovery reasons. |
| 215 | 208 | |
| 216 | −The best way to create a printable hardcopy of your private key is by | |
| 209 | +A good way to create a printable hardcopy of your private key is by | |
| 217 | 210 | using the ``paperkey`` software written for this very purpose. See ``man |
| 218 | 211 | paperkey`` for more details on the output format and its benefits over |
| 219 | 212 | other solutions. Paperkey should already be packaged for most |
| @@ -224,11 +217,11 @@ key:: · 4 unmodified lines | ||
| 224 | 217 | |
| 225 | 218 | $ gpg --export-secret-key [fpr] | paperkey -o /tmp/key-backup.txt |
| 226 | 219 | |
| 227 | −Print out that file (or pipe the output straight to lpr), then take a | |
| 228 | −pen and write your passphrase on the margin of the paper. **This is | |
| 229 | −strongly recommended** because the key printout is still encrypted with | |
| 230 | −that passphrase, and if you ever change it you will not remember what it | |
| 231 | −used to be when you had created the backup -- *guaranteed*. | |
| 220 | +Print out that file, then take a pen and write your passphrase on the | |
| 221 | +margin of the paper. **This is strongly recommended** because the key | |
| 222 | +printout is still encrypted with that passphrase, and if you ever change | |
| 223 | +it you will not remember what it used to be when you had created the | |
| 224 | +backup -- *guaranteed*. | |
| 232 | 225 | |
| 233 | 226 | Put the resulting printout and the hand-written passphrase into an envelope |
| 234 | 227 | and store in a secure and well-protected place, preferably away from your |
| @@ -236,10 +229,9 @@ home, such as your bank vault. · 1 unmodified line | ||
| 236 | 229 | |
| 237 | 230 | .. note:: |
| 238 | 231 | |
| 239 | − Your printer is probably no longer a simple dumb device connected to | |
| 240 | − your parallel port, but since the output is still encrypted with | |
| 241 | − your passphrase, printing out even to "cloud-integrated" modern | |
| 242 | − printers should remain a relatively safe operation. | |
| 232 | + The key is still encrypted with your passphrase, so printing out | |
| 233 | + even to "cloud-integrated" modern printers should remain a | |
| 234 | + relatively safe operation. | |
| 243 | 235 | |
| 244 | 236 | Back up your whole GnuPG directory |
| 245 | 237 | ---------------------------------- |
| @@ -255,16 +247,17 @@ on these external copies whenever you need to use your Certify key -- · 9 unmodified lines | ||
| 255 | 247 | such as when making changes to your own key or signing other people's |
| 256 | 248 | keys after conferences and summits. |
| 257 | 249 | |
| 258 | −Start by getting a small USB "thumb" drive (preferably two!) that you | |
| 259 | −will use for backup purposes. You will need to encrypt them using LUKS | |
| 260 | −-- refer to your distro's documentation on how to accomplish this. | |
| 250 | +Start by getting an external media card (preferably two!) that you will | |
| 251 | +use for backup purposes. You will need to create an encrypted partition | |
| 252 | +on this device using LUKS -- refer to your distro's documentation on how | |
| 253 | +to accomplish this. | |
| 261 | 254 | |
| 262 | 255 | For the encryption passphrase, you can use the same one as on your |
| 263 | 256 | PGP key. |
| 264 | 257 | |
| 265 | −Once the encryption process is over, re-insert the USB drive and make | |
| 266 | −sure it gets properly mounted. Copy your entire ``.gnupg`` directory | |
| 267 | −over to the encrypted storage:: | |
| 258 | +Once the encryption process is over, re-insert your device and make sure | |
| 259 | +it gets properly mounted. Copy your entire ``.gnupg`` directory over to | |
| 260 | +the encrypted storage:: | |
| 268 | 261 | |
| 269 | 262 | $ cp -a ~/.gnupg /media/disk/foo/gnupg-backup |
| 270 | 263 | |
| @@ -273,11 +266,10 @@ You should now test to make sure everything still works:: · 2 unmodified lines | ||
| 273 | 266 | $ gpg --homedir=/media/disk/foo/gnupg-backup --list-key [fpr] |
| 274 | 267 | |
| 275 | 268 | If you don't get any errors, then you should be good to go. Unmount the |
| 276 | −USB drive, distinctly label it so you don't blow it away next time you | |
| 277 | −need to use a random USB drive, and put in a safe place -- but not too | |
| 278 | −far away, because you'll need to use it every now and again for things | |
| 279 | −like editing identities, adding or revoking subkeys, or signing other | |
| 280 | −people's keys. | |
| 269 | +device, distinctly label it so you don't overwrite it by accident, and | |
| 270 | +put in a safe place -- but not too far away, because you'll need to use | |
| 271 | +it every now and again for things like editing identities, adding or | |
| 272 | +revoking subkeys, or signing other people's keys. | |
| 281 | 273 | |
| 282 | 274 | Remove the Certify key from your homedir |
| 283 | 275 | ---------------------------------------- |
| @@ -303,7 +295,7 @@ and store it on offline storage. · 19 unmodified lines | ||
| 303 | 295 | your GnuPG directory in its entirety. What we are about to do will |
| 304 | 296 | render your key useless if you do not have a usable backup! |
| 305 | 297 | |
| 306 | −First, identify the keygrip of your Certify key:: | |
| 298 | +First, identify the "keygrip" of your Certify key:: | |
| 307 | 299 | |
| 308 | 300 | $ gpg --with-keygrip --list-key [fpr] |
| 309 | 301 | |
| @@ -328,8 +320,8 @@ Certify key fingerprint). This will correspond directly to a file in your · 18 unmodified lines | ||
| 328 | 320 | 2222000000000000000000000000000000000000.key |
| 329 | 321 | 3333000000000000000000000000000000000000.key |
| 330 | 322 | |
| 331 | −All you have to do is simply remove the .key file that corresponds to | |
| 332 | −the Certify key keygrip:: | |
| 323 | +It is sufficient to remove the .key file that corresponds to the Certify | |
| 324 | +key keygrip:: | |
| 333 | 325 | |
| 334 | 326 | $ cd ~/.gnupg/private-keys-v1.d |
| 335 | 327 | $ rm 1111000000000000000000000000000000000000.key |
| @@ -372,7 +364,7 @@ GnuPG operation is performed, the keys are loaded into system memory and · 36 unmodified lines | ||
| 372 | 364 | can be stolen from there by sufficiently advanced malware (think |
| 373 | 365 | Meltdown and Spectre). |
| 374 | 366 | |
| 375 | −The best way to completely protect your keys is to move them to a | |
| 367 | +A good way to completely protect your keys is to move them to a | |
| 376 | 368 | specialized hardware device that is capable of smartcard operations. |
| 377 | 369 | |
| 378 | 370 | The benefits of smartcards |
| @@ -383,11 +375,11 @@ private keys and performing crypto operations directly on the card · 4 unmodified lines | ||
| 383 | 375 | itself. Because the key contents never leave the smartcard, the |
| 384 | 376 | operating system of the computer into which you plug in the hardware |
| 385 | 377 | device is not able to retrieve the private keys themselves. This is very |
| 386 | −different from the encrypted USB storage device we used earlier for | |
| 387 | −backup purposes -- while that USB device is plugged in and mounted, the | |
| 378 | +different from the encrypted media storage device we used earlier for | |
| 379 | +backup purposes -- while that device is plugged in and mounted, the | |
| 388 | 380 | operating system is able to access the private key contents. |
| 389 | 381 | |
| 390 | −Using external encrypted USB media is not a substitute to having a | |
| 382 | +Using external encrypted media is not a substitute to having a | |
| 391 | 383 | smartcard-capable device. |
| 392 | 384 | |
| 393 | 385 | Available smartcard devices |
| @@ -398,17 +390,15 @@ easiest is to get a specialized USB device that implements smartcard · 4 unmodified lines | ||
| 398 | 390 | functionality. There are several options available: |
| 399 | 391 | |
| 400 | 392 | - `Nitrokey Start`_: Open hardware and Free Software, based on FSI |
| 401 | − Japan's `Gnuk`_. One of the few available commercial devices that | |
| 402 | − support ED25519 ECC keys, but offer fewest security features (such as | |
| 403 | − resistance to tampering or some side-channel attacks). | |
| 404 | −- `Nitrokey Pro 2`_: Similar to the Nitrokey Start, but more | |
| 405 | − tamper-resistant and offers more security features. Pro 2 supports ECC | |
| 406 | − cryptography (NISTP). | |
| 393 | + Japan's `Gnuk`_. One of the cheapest options, but offers fewest | |
| 394 | + security features (such as resistance to tampering or some | |
| 395 | + side-channel attacks). | |
| 396 | +- `Nitrokey 3`_: Similar to the Nitrokey Start, but more | |
| 397 | + tamper-resistant and offers more security features and USB | |
| 398 | + form-factors. Supports ECC cryptography (ED25519 and NISTP). | |
| 407 | 399 | - `Yubikey 5`_: proprietary hardware and software, but cheaper than |
| 408 | − Nitrokey Pro and comes available in the USB-C form that is more useful | |
| 409 | − with newer laptops. Offers additional security features such as FIDO | |
| 410 | − U2F, among others, and now finally supports NISTP and ED25519 ECC | |
| 411 | − keys. | |
| 400 | + Nitrokey with a similar set of features. Supports ECC cryptography | |
| 401 | + (ED25519 and NISTP). | |
| 412 | 402 | |
| 413 | 403 | Your choice will depend on cost, shipping availability in your |
| 414 | 404 | geographical region, and open/proprietary hardware considerations. |
| @@ -419,8 +409,8 @@ geographical region, and open/proprietary hardware considerations. · 4 unmodified lines | ||
| 419 | 409 | you `qualify for a free Nitrokey Start`_ courtesy of The Linux |
| 420 | 410 | Foundation. |
| 421 | 411 | |
| 422 | −.. _`Nitrokey Start`: https://shop.nitrokey.com/shop/product/nitrokey-start-6 | |
| 423 | −.. _`Nitrokey Pro 2`: https://shop.nitrokey.com/shop/product/nkpr2-nitrokey-pro-2-3 | |
| 412 | +.. _`Nitrokey Start`: https://www.nitrokey.com/products/nitrokeys | |
| 413 | +.. _`Nitrokey 3`: https://www.nitrokey.com/products/nitrokeys | |
| 424 | 414 | .. _`Yubikey 5`: https://www.yubico.com/products/yubikey-5-overview/ |
| 425 | 415 | .. _Gnuk: https://www.fsij.org/doc-gnuk/ |
| 426 | 416 | .. _`qualify for a free Nitrokey Start`: https://www.kernel.org/nitrokey-digital-tokens-for-kernel-developers.html |
| @@ -455,7 +445,7 @@ the smartcard). You so rarely need to use the Admin PIN, that you will · 28 unmodified lines | ||
| 455 | 445 | inevitably forget what it is if you do not record it. |
| 456 | 446 | |
| 457 | 447 | Getting back to the main card menu, you can also set other values (such |
| 458 | −as name, sex, login data, etc), but it's not necessary and will | |
| 448 | +as name, gender, login data, etc), but it's not necessary and will | |
| 459 | 449 | additionally leak information about your smartcard should you lose it. |
| 460 | 450 | |
| 461 | 451 | .. note:: |
| @@ -615,7 +605,7 @@ run:: · 153 unmodified lines | ||
| 615 | 605 | You can also use a specific date if that is easier to remember (e.g. |
| 616 | 606 | your birthday, January 1st, or Canada Day):: |
| 617 | 607 | |
| 618 | − $ gpg --quick-set-expire [fpr] 2025-07-01 | |
| 608 | + $ gpg --quick-set-expire [fpr] 2038-07-01 | |
| 619 | 609 | |
| 620 | 610 | Remember to send the updated key back to keyservers:: |
| 621 | 611 | |
| @@ -656,9 +646,9 @@ hundreds of cloned repositories floating around, how does anyone verify · 34 unmodified lines | ||
| 656 | 646 | that their copy of linux.git has not been tampered with by a malicious |
| 657 | 647 | third party? |
| 658 | 648 | |
| 659 | −Or what happens if a backdoor is discovered in the code and the "Author" | |
| 660 | −line in the commit says it was done by you, while you're pretty sure you | |
| 661 | −had `nothing to do with it`_? | |
| 649 | +Or what happens if malicious code is discovered in the kernel and the | |
| 650 | +"Author" line in the commit says it was done by you, while you're pretty | |
| 651 | +sure you had `nothing to do with it`_? | |
| 662 | 652 | |
| 663 | 653 | To address both of these issues, Git introduced PGP integration. Signed |
| 664 | 654 | tags prove the repository integrity by assuring that its contents are |
| @@ -681,8 +671,7 @@ should be used (``[fpr]`` is the fingerprint of your key):: · 16 unmodified lines | ||
| 681 | 671 | How to work with signed tags |
| 682 | 672 | ---------------------------- |
| 683 | 673 | |
| 684 | −To create a signed tag, simply pass the ``-s`` switch to the tag | |
| 685 | −command:: | |
| 674 | +To create a signed tag, pass the ``-s`` switch to the tag command:: | |
| 686 | 675 | |
| 687 | 676 | $ git tag -s [tagname] |
| 688 | 677 | |
| @@ -693,7 +682,7 @@ not been maliciously altered. · 4 unmodified lines | ||
| 693 | 682 | How to verify signed tags |
| 694 | 683 | ~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 695 | 684 | |
| 696 | −To verify a signed tag, simply use the ``verify-tag`` command:: | |
| 685 | +To verify a signed tag, use the ``verify-tag`` command:: | |
| 697 | 686 | |
| 698 | 687 | $ git verify-tag [tagname] |
| 699 | 688 | |
| @@ -712,9 +701,9 @@ The merge message will contain something like this:: · 12 unmodified lines | ||
| 712 | 701 | # gpg: Signature made [...] |
| 713 | 702 | # gpg: Good signature from [...] |
| 714 | 703 | |
| 715 | −If you are verifying someone else's git tag, then you will need to | |
| 716 | −import their PGP key. Please refer to the | |
| 717 | −":ref:`verify_identities`" section below. | |
| 704 | +If you are verifying someone else's git tag, you will first need to | |
| 705 | +import their PGP key. Please refer to the ":ref:`verify_identities`" | |
| 706 | +section below. | |
| 718 | 707 | |
| 719 | 708 | Configure git to always sign annotated tags |
| 720 | 709 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| @@ -728,16 +717,16 @@ configuration option:: · 7 unmodified lines | ||
| 728 | 717 | How to work with signed commits |
| 729 | 718 | ------------------------------- |
| 730 | 719 | |
| 731 | −It is easy to create signed commits, but it is much more difficult to | |
| 732 | −use them in Linux kernel development, since it relies on patches sent to | |
| 733 | −the mailing list, and this workflow does not preserve PGP commit | |
| 734 | −signatures. Furthermore, when rebasing your repository to match | |
| 735 | −upstream, even your own PGP commit signatures will end up discarded. For | |
| 736 | −this reason, most kernel developers don't bother signing their commits | |
| 737 | −and will ignore signed commits in any external repositories that they | |
| 738 | −rely upon in their work. | |
| 720 | +It is also possible to create signed commits, but they have limited | |
| 721 | +usefulness in Linux kernel development. The kernel contribution workflow | |
| 722 | +relies on sending in patches, and converting commits to patches does not | |
| 723 | +preserve git commit signatures. Furthermore, when rebasing your own | |
| 724 | +repository on a newer upstream, PGP commit signatures will end up | |
| 725 | +discarded. For this reason, most kernel developers don't bother signing | |
| 726 | +their commits and will ignore signed commits in any external | |
| 727 | +repositories that they rely upon in their work. | |
| 739 | 728 | |
| 740 | −However, if you have your working git tree publicly available at some | |
| 729 | +That said, if you have your working git tree publicly available at some | |
| 741 | 730 | git hosting service (kernel.org, infradead.org, ozlabs.org, or others), |
| 742 | 731 | then the recommendation is that you sign all your git commits even if |
| 743 | 732 | upstream developers do not directly benefit from this practice. |
| @@ -748,7 +737,7 @@ We recommend this for the following reasons: · 4 unmodified lines | ||
| 748 | 737 | provenance, even externally maintained trees carrying PGP commit |
| 749 | 738 | signatures will be valuable for such purposes. |
| 750 | 739 | 2. If you ever need to re-clone your local repository (for example, |
| 751 | − after a disk failure), this lets you easily verify the repository | |
| 740 | + after reinstalling your system), this lets you verify the repository | |
| 752 | 741 | integrity before resuming your work. |
| 753 | 742 | 3. If someone needs to cherry-pick your commits, this allows them to |
| 754 | 743 | quickly verify their integrity before applying them. |
| @@ -756,9 +745,8 @@ We recommend this for the following reasons: · 1 unmodified line | ||
| 756 | 745 | Creating signed commits |
| 757 | 746 | ~~~~~~~~~~~~~~~~~~~~~~~ |
| 758 | 747 | |
| 759 | −To create a signed commit, you just need to pass the ``-S`` flag to the | |
| 760 | −``git commit`` command (it's capital ``-S`` due to collision with | |
| 761 | −another flag):: | |
| 748 | +To create a signed commit, pass the ``-S`` flag to the ``git commit`` | |
| 749 | +command (it's capital ``-S`` due to collision with another flag):: | |
| 762 | 750 | |
| 763 | 751 | $ git commit -S |
| 764 | 752 | |
| @@ -775,7 +763,6 @@ You can tell git to always sign commits:: · 10 unmodified lines | ||
| 775 | 763 | |
| 776 | 764 | .. _verify_identities: |
| 777 | 765 | |
| 778 | − | |
| 779 | 766 | How to work with signed patches |
| 780 | 767 | ------------------------------- |
| 781 | 768 | |
| @@ -793,6 +780,11 @@ headers (a-la DKIM): · 11 unmodified lines | ||
| 793 | 780 | Installing and configuring patatt |
| 794 | 781 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 795 | 782 | |
| 783 | +.. note:: | |
| 784 | + | |
| 785 | + If you use B4 to send in your patches, patatt is already installed | |
| 786 | + and integrated into your workflow. | |
| 787 | + | |
| 796 | 788 | Patatt is packaged for many distributions already, so please check there |
| 797 | 789 | first. You can also install it from pypi using "``pip install patatt``". |
| 798 | 790 | |
| @@ -835,9 +827,9 @@ encounters, for example:: · 36 unmodified lines | ||
| 835 | 827 | How to verify kernel developer identities |
| 836 | 828 | ========================================= |
| 837 | 829 | |
| 838 | −Signing tags and commits is easy, but how does one go about verifying | |
| 839 | −that the key used to sign something belongs to the actual kernel | |
| 840 | −developer and not to a malicious imposter? | |
| 830 | +Signing tags and commits is straightforward, but how does one go about | |
| 831 | +verifying that the key used to sign something belongs to the actual | |
| 832 | +kernel developer and not to a malicious imposter? | |
| 841 | 833 | |
| 842 | 834 | Configure auto-key-retrieval using WKD and DANE |
| 843 | 835 | ----------------------------------------------- |
| @@ -884,7 +876,7 @@ various software makers dictating who should be your trusted certifying · 40 unmodified lines | ||
| 884 | 876 | entity, PGP leaves this responsibility to each user. |
| 885 | 877 | |
| 886 | 878 | Unfortunately, very few people understand how the Web of Trust works. |
| 887 | −While it remains an important aspect of the OpenPGP specification, | |
| 879 | +While it is still an important part of the OpenPGP specification, | |
| 888 | 880 | recent versions of GnuPG (2.2 and above) have implemented an alternative |
| 889 | 881 | mechanism called "Trust on First Use" (TOFU). You can think of TOFU as |
| 890 | 882 | "the SSH-like approach to trust." With SSH, the first time you connect |
| @@ -894,8 +886,8 @@ to connect, forcing you to make a decision on whether you choose to · 3 unmodified lines | ||
| 894 | 886 | trust the changed key or not. Similarly, the first time you import |
| 895 | 887 | someone's PGP key, it is assumed to be valid. If at any point in the |
| 896 | 888 | future GnuPG comes across another key with the same identity, both the |
| 897 | −previously imported key and the new key will be marked as invalid and | |
| 898 | −you will need to manually figure out which one to keep. | |
| 889 | +previously imported key and the new key will be marked for verification | |
| 890 | +and you will need to manually figure out which one to keep. | |
| 899 | 891 | |
| 900 | 892 | We recommend that you use the combined TOFU+PGP trust model (which is |
| 901 | 893 | the new default in GnuPG v2). To set it, add (or modify) the |
| @@ -343,7 +343,7 @@ https://en.wikipedia.org/wiki/Posting_style#Interleaved_style · 342 unmodified lines | ||
| 343 | 343 | As is frequently quoted on the mailing list:: |
| 344 | 344 | |
| 345 | 345 | A: http://en.wikipedia.org/wiki/Top_post |
| 346 | − Q: Were do I find info about this thing called top-posting? | |
| 346 | + Q: Where do I find info about this thing called top-posting? | |
| 347 | 347 | A: Because it messes up the order in which people normally read text. |
| 348 | 348 | Q: Why is top-posting such a bad thing? |
| 349 | 349 | A: Top-posting. |
| @@ -602,8 +602,8 @@ future. Note, this is one of only three tags you might be able to use without · 252 unmodified lines | ||
| 602 | 602 | explicit permission of the person named (see 'Tagging people requires |
| 603 | 603 | permission' below for details). |
| 604 | 604 | |
| 605 | −A Fixes: tag indicates that the patch fixes an issue in a previous commit. It | |
| 606 | −is used to make it easy to determine where a bug originated, which can help | |
| 605 | +A Fixes: tag indicates that the patch fixes a bug in a previous commit. It | |
| 606 | +is used to make it easy to determine where an issue originated, which can help | |
| 607 | 607 | review a bug fix. This tag also assists the stable kernel team in determining |
| 608 | 608 | which stable kernel versions should receive your fix. This is the preferred |
| 609 | 609 | method for indicating a bug fixed by the patch. See :ref:`describe_changes` |
| @@ -244,7 +244,7 @@ def add_and_resolve_xref(app, docname, domain, reftype, target, contnode=None): · 243 unmodified lines | ||
| 244 | 244 | return contnode |
| 245 | 245 | |
| 246 | 246 | # |
| 247 | −# Variant of markup_abi_ref() that warns whan a reference is not found | |
| 247 | +# Variant of markup_abi_ref() that warns when a reference is not found | |
| 248 | 248 | # |
| 249 | 249 | def markup_abi_file_ref(docname, app, match): |
| 250 | 250 | return markup_abi_ref(docname, app, match, warning=True) |
| @@ -1,247 +0,0 @@ | ||
| 1 | −# -*- coding: utf-8; mode: python -*- | |
| 2 | −# SPDX-License-Identifier: GPL-2.0 | |
| 3 | −# pylint: disable=W0141,C0113,C0103,C0325 | |
| 4 | −""" | |
| 5 | − cdomain | |
| 6 | − ~~~~~~~ | |
| 7 | − | |
| 8 | − Replacement for the sphinx c-domain. | |
| 9 | − | |
| 10 | − :copyright: Copyright (C) 2016 Markus Heiser | |
| 11 | − :license: GPL Version 2, June 1991 see Linux/COPYING for details. | |
| 12 | − | |
| 13 | − List of customizations: | |
| 14 | − | |
| 15 | − * Moved the *duplicate C object description* warnings for function | |
| 16 | − declarations in the nitpicky mode. See Sphinx documentation for | |
| 17 | − the config values for ``nitpick`` and ``nitpick_ignore``. | |
| 18 | − | |
| 19 | − * Add option 'name' to the "c:function:" directive. With option 'name' the | |
| 20 | − ref-name of a function can be modified. E.g.:: | |
| 21 | − | |
| 22 | − .. c:function:: int ioctl( int fd, int request ) | |
| 23 | − :name: VIDIOC_LOG_STATUS | |
| 24 | − | |
| 25 | − The func-name (e.g. ioctl) remains in the output but the ref-name changed | |
| 26 | − from 'ioctl' to 'VIDIOC_LOG_STATUS'. The function is referenced by:: | |
| 27 | − | |
| 28 | − * :c:func:`VIDIOC_LOG_STATUS` or | |
| 29 | − * :any:`VIDIOC_LOG_STATUS` (``:any:`` needs sphinx 1.3) | |
| 30 | − | |
| 31 | − * Handle signatures of function-like macros well. Don't try to deduce | |
| 32 | − arguments types of function-like macros. | |
| 33 | − | |
| 34 | −""" | |
| 35 | − | |
| 36 | −from docutils import nodes | |
| 37 | −from docutils.parsers.rst import directives | |
| 38 | − | |
| 39 | −import sphinx | |
| 40 | −from sphinx import addnodes | |
| 41 | −from sphinx.domains.c import c_funcptr_sig_re, c_sig_re | |
| 42 | −from sphinx.domains.c import CObject as Base_CObject | |
| 43 | −from sphinx.domains.c import CDomain as Base_CDomain | |
| 44 | −from itertools import chain | |
| 45 | −import re | |
| 46 | − | |
| 47 | −__version__ = '1.1' | |
| 48 | − | |
| 49 | −# Namespace to be prepended to the full name | |
| 50 | −namespace = None | |
| 51 | − | |
| 52 | −# | |
| 53 | −# Handle trivial newer c domain tags that are part of Sphinx 3.1 c domain tags | |
| 54 | −# - Store the namespace if ".. c:namespace::" tag is found | |
| 55 | −# | |
| 56 | −RE_namespace = re.compile(r'^\s*..\s*c:namespace::\s*(\S+)\s*$') | |
| 57 | − | |
| 58 | −def markup_namespace(match): | |
| 59 | − global namespace | |
| 60 | − | |
| 61 | − namespace = match.group(1) | |
| 62 | − | |
| 63 | − return "" | |
| 64 | − | |
| 65 | −# | |
| 66 | −# Handle c:macro for function-style declaration | |
| 67 | −# | |
| 68 | −RE_macro = re.compile(r'^\s*..\s*c:macro::\s*(\S+)\s+(\S.*)\s*$') | |
| 69 | −def markup_macro(match): | |
| 70 | − return ".. c:function:: " + match.group(1) + ' ' + match.group(2) | |
| 71 | − | |
| 72 | −# | |
| 73 | −# Handle newer c domain tags that are evaluated as .. c:type: for | |
| 74 | −# backward-compatibility with Sphinx < 3.0 | |
| 75 | −# | |
| 76 | −RE_ctype = re.compile(r'^\s*..\s*c:(struct|union|enum|enumerator|alias)::\s*(.*)$') | |
| 77 | − | |
| 78 | −def markup_ctype(match): | |
| 79 | − return ".. c:type:: " + match.group(2) | |
| 80 | − | |
| 81 | −# | |
| 82 | −# Handle newer c domain tags that are evaluated as :c:type: for | |
| 83 | −# backward-compatibility with Sphinx < 3.0 | |
| 84 | −# | |
| 85 | −RE_ctype_refs = re.compile(r':c:(var|struct|union|enum|enumerator)::`([^\`]+)`') | |
| 86 | −def markup_ctype_refs(match): | |
| 87 | − return ":c:type:`" + match.group(2) + '`' | |
| 88 | − | |
| 89 | −# | |
| 90 | −# Simply convert :c:expr: and :c:texpr: into a literal block. | |
| 91 | −# | |
| 92 | −RE_expr = re.compile(r':c:(expr|texpr):`([^\`]+)`') | |
| 93 | −def markup_c_expr(match): | |
| 94 | − return '\\ ``' + match.group(2) + '``\\ ' | |
| 95 | − | |
| 96 | −# | |
| 97 | −# Parse Sphinx 3.x C markups, replacing them by backward-compatible ones | |
| 98 | −# | |
| 99 | −def c_markups(app, docname, source): | |
| 100 | − result = "" | |
| 101 | − markup_func = { | |
| 102 | − RE_namespace: markup_namespace, | |
| 103 | − RE_expr: markup_c_expr, | |
| 104 | − RE_macro: markup_macro, | |
| 105 | − RE_ctype: markup_ctype, | |
| 106 | − RE_ctype_refs: markup_ctype_refs, | |
| 107 | − } | |
| 108 | − | |
| 109 | − lines = iter(source[0].splitlines(True)) | |
| 110 | − for n in lines: | |
| 111 | − match_iterators = [regex.finditer(n) for regex in markup_func] | |
| 112 | − matches = sorted(chain(*match_iterators), key=lambda m: m.start()) | |
| 113 | − for m in matches: | |
| 114 | − n = n[:m.start()] + markup_func[m.re](m) + n[m.end():] | |
| 115 | − | |
| 116 | − result = result + n | |
| 117 | − | |
| 118 | − source[0] = result | |
| 119 | − | |
| 120 | −# | |
| 121 | −# Now implements support for the cdomain namespacing logic | |
| 122 | −# | |
| 123 | − | |
| 124 | −def setup(app): | |
| 125 | − | |
| 126 | − # Handle easy Sphinx 3.1+ simple new tags: :c:expr and .. c:namespace:: | |
| 127 | − app.connect('source-read', c_markups) | |
| 128 | − app.add_domain(CDomain, override=True) | |
| 129 | − | |
| 130 | − return dict( | |
| 131 | − version = __version__, | |
| 132 | − parallel_read_safe = True, | |
| 133 | − parallel_write_safe = True | |
| 134 | − ) | |
| 135 | − | |
| 136 | −class CObject(Base_CObject): | |
| 137 | − | |
| 138 | − """ | |
| 139 | − Description of a C language object. | |
| 140 | − """ | |
| 141 | − option_spec = { | |
| 142 | − "name" : directives.unchanged | |
| 143 | − } | |
| 144 | − | |
| 145 | − def handle_func_like_macro(self, sig, signode): | |
| 146 | − """Handles signatures of function-like macros. | |
| 147 | − | |
| 148 | − If the objtype is 'function' and the signature ``sig`` is a | |
| 149 | − function-like macro, the name of the macro is returned. Otherwise | |
| 150 | − ``False`` is returned. """ | |
| 151 | − | |
| 152 | − global namespace | |
| 153 | − | |
| 154 | − if not self.objtype == 'function': | |
| 155 | − return False | |
| 156 | − | |
| 157 | − m = c_funcptr_sig_re.match(sig) | |
| 158 | − if m is None: | |
| 159 | − m = c_sig_re.match(sig) | |
| 160 | − if m is None: | |
| 161 | − raise ValueError('no match') | |
| 162 | − | |
| 163 | − rettype, fullname, arglist, _const = m.groups() | |
| 164 | − arglist = arglist.strip() | |
| 165 | − if rettype or not arglist: | |
| 166 | − return False | |
| 167 | − | |
| 168 | − arglist = arglist.replace('`', '').replace('\\ ', '') # remove markup | |
| 169 | − arglist = [a.strip() for a in arglist.split(",")] | |
| 170 | − | |
| 171 | − # has the first argument a type? | |
| 172 | − if len(arglist[0].split(" ")) > 1: | |
| 173 | − return False | |
| 174 | − | |
| 175 | − # This is a function-like macro, its arguments are typeless! | |
| 176 | − signode += addnodes.desc_name(fullname, fullname) | |
| 177 | − paramlist = addnodes.desc_parameterlist() | |
| 178 | − signode += paramlist | |
| 179 | − | |
| 180 | − for argname in arglist: | |
| 181 | − param = addnodes.desc_parameter('', '', noemph=True) | |
| 182 | − # separate by non-breaking space in the output | |
| 183 | − param += nodes.emphasis(argname, argname) | |
| 184 | − paramlist += param | |
| 185 | − | |
| 186 | − if namespace: | |
| 187 | − fullname = namespace + "." + fullname | |
| 188 | − | |
| 189 | − return fullname | |
| 190 | − | |
| 191 | − def handle_signature(self, sig, signode): | |
| 192 | − """Transform a C signature into RST nodes.""" | |
| 193 | − | |
| 194 | − global namespace | |
| 195 | − | |
| 196 | − fullname = self.handle_func_like_macro(sig, signode) | |
| 197 | − if not fullname: | |
| 198 | − fullname = super(CObject, self).handle_signature(sig, signode) | |
| 199 | − | |
| 200 | − if "name" in self.options: | |
| 201 | − if self.objtype == 'function': | |
| 202 | − fullname = self.options["name"] | |
| 203 | − else: | |
| 204 | − # FIXME: handle :name: value of other declaration types? | |
| 205 | − pass | |
| 206 | − else: | |
| 207 | − if namespace: | |
| 208 | − fullname = namespace + "." + fullname | |
| 209 | − | |
| 210 | − return fullname | |
| 211 | − | |
| 212 | − def add_target_and_index(self, name, sig, signode): | |
| 213 | − # for C API items we add a prefix since names are usually not qualified | |
| 214 | − # by a module name and so easily clash with e.g. section titles | |
| 215 | − targetname = 'c.' + name | |
| 216 | − if targetname not in self.state.document.ids: | |
| 217 | − signode['names'].append(targetname) | |
| 218 | − signode['ids'].append(targetname) | |
| 219 | − signode['first'] = (not self.names) | |
| 220 | − self.state.document.note_explicit_target(signode) | |
| 221 | − inv = self.env.domaindata['c']['objects'] | |
| 222 | − if (name in inv and self.env.config.nitpicky): | |
| 223 | − if self.objtype == 'function': | |
| 224 | − if ('c:func', name) not in self.env.config.nitpick_ignore: | |
| 225 | − self.state_machine.reporter.warning( | |
| 226 | − 'duplicate C object description of %s, ' % name + | |
| 227 | − 'other instance in ' + self.env.doc2path(inv[name][0]), | |
| 228 | − line=self.lineno) | |
| 229 | − inv[name] = (self.env.docname, self.objtype) | |
| 230 | − | |
| 231 | − indextext = self.get_index_text(name) | |
| 232 | − if indextext: | |
| 233 | − self.indexnode['entries'].append( | |
| 234 | − ('single', indextext, targetname, '', None)) | |
| 235 | − | |
| 236 | −class CDomain(Base_CDomain): | |
| 237 | − | |
| 238 | − """C language domain.""" | |
| 239 | − name = 'c' | |
| 240 | − label = 'C' | |
| 241 | − directives = { | |
| 242 | − 'function': CObject, | |
| 243 | − 'member': CObject, | |
| 244 | − 'macro': CObject, | |
| 245 | − 'type': CObject, | |
| 246 | − 'var': CObject, | |
| 247 | − } | |
| @@ -40,9 +40,11 @@ import sys · 39 unmodified lines | ||
| 40 | 40 | from docutils import nodes, statemachine |
| 41 | 41 | from docutils.statemachine import ViewList |
| 42 | 42 | from docutils.parsers.rst import directives, Directive |
| 43 | −from docutils.utils.error_reporting import ErrorString | |
| 44 | 43 | from sphinx.util.docutils import switch_source_input |
| 45 | 44 | |
| 45 | +def ErrorString(exc): # Shamelessly stolen from docutils | |
| 46 | + return f'{exc.__class__.__name}: {exc}' | |
| 47 | + | |
| 46 | 48 | __version__ = '1.0' |
| 47 | 49 | |
| 48 | 50 | def setup(app): |
| @@ -1,31 +1,82 @@ | ||
| 1 | 1 | #!/usr/bin/env python3 |
| 2 | −# -*- coding: utf-8; mode: python -*- | |
| 3 | 2 | # SPDX-License-Identifier: GPL-2.0 |
| 4 | −# pylint: disable=R0903, C0330, R0914, R0912, E0401 | |
| 3 | +# pylint: disable=R0903, R0912, R0914, R0915, C0209,W0707 | |
| 4 | + | |
| 5 | 5 | |
| 6 | 6 | """ |
| 7 | − kernel-include | |
| 8 | − ~~~~~~~~~~~~~~ | |
| 7 | +Implementation of the ``kernel-include`` reST-directive. | |
| 8 | + | |
| 9 | +:copyright: Copyright (C) 2016 Markus Heiser | |
| 10 | +:license: GPL Version 2, June 1991 see linux/COPYING for details. | |
| 11 | + | |
| 12 | +The ``kernel-include`` reST-directive is a replacement for the ``include`` | |
| 13 | +directive. The ``kernel-include`` directive expand environment variables in | |
| 14 | +the path name and allows to include files from arbitrary locations. | |
| 15 | + | |
| 16 | +.. hint:: | |
| 17 | + | |
| 18 | + Including files from arbitrary locations (e.g. from ``/etc``) is a | |
| 19 | + security risk for builders. This is why the ``include`` directive from | |
| 20 | + docutils *prohibit* pathnames pointing to locations *above* the filesystem | |
| 21 | + tree where the reST document with the include directive is placed. | |
| 22 | + | |
| 23 | +Substrings of the form $name or ${name} are replaced by the value of | |
| 24 | +environment variable name. Malformed variable names and references to | |
| 25 | +non-existing variables are left unchanged. | |
| 26 | + | |
| 27 | +**Supported Sphinx Include Options**: | |
| 28 | + | |
| 29 | +:param literal: | |
| 30 | + If present, the included file is inserted as a literal block. | |
| 31 | + | |
| 32 | +:param code: | |
| 33 | + Specify the language for syntax highlighting (e.g., 'c', 'python'). | |
| 34 | + | |
| 35 | +:param encoding: | |
| 36 | + Specify the encoding of the included file (default: 'utf-8'). | |
| 37 | + | |
| 38 | +:param tab-width: | |
| 39 | + Specify the number of spaces that a tab represents. | |
| 40 | + | |
| 41 | +:param start-line: | |
| 42 | + Line number at which to start including the file (1-based). | |
| 43 | + | |
| 44 | +:param end-line: | |
| 45 | + Line number at which to stop including the file (inclusive). | |
| 46 | + | |
| 47 | +:param start-after: | |
| 48 | + Include lines after the first line matching this text. | |
| 49 | + | |
| 50 | +:param end-before: | |
| 51 | + Include lines before the first line matching this text. | |
| 9 | 52 | |
| 10 | − Implementation of the ``kernel-include`` reST-directive. | |
| 53 | +:param number-lines: | |
| 54 | + Number the included lines (integer specifies start number). | |
| 55 | + Only effective with 'literal' or 'code' options. | |
| 11 | 56 | |
| 12 | − :copyright: Copyright (C) 2016 Markus Heiser | |
| 13 | − :license: GPL Version 2, June 1991 see linux/COPYING for details. | |
| 57 | +:param class: | |
| 58 | + Specify HTML class attribute for the included content. | |
| 14 | 59 | |
| 15 | − The ``kernel-include`` reST-directive is a replacement for the ``include`` | |
| 16 | − directive. The ``kernel-include`` directive expand environment variables in | |
| 17 | − the path name and allows to include files from arbitrary locations. | |
| 60 | +**Kernel-specific Extensions**: | |
| 18 | 61 | |
| 19 | − .. hint:: | |
| 62 | +:param generate-cross-refs: | |
| 63 | + If present, instead of directly including the file, it calls | |
| 64 | + ParseDataStructs() to convert C data structures into cross-references | |
| 65 | + that link to comprehensive documentation in other ReST files. | |
| 20 | 66 | |
| 21 | − Including files from arbitrary locations (e.g. from ``/etc``) is a | |
| 22 | − security risk for builders. This is why the ``include`` directive from | |
| 23 | − docutils *prohibit* pathnames pointing to locations *above* the filesystem | |
| 24 | − tree where the reST document with the include directive is placed. | |
| 67 | +:param exception-file: | |
| 68 | + (Used with generate-cross-refs) | |
| 25 | 69 | |
| 26 | − Substrings of the form $name or ${name} are replaced by the value of | |
| 27 | − environment variable name. Malformed variable names and references to | |
| 28 | − non-existing variables are left unchanged. | |
| 70 | + Path to a file containing rules for handling special cases: | |
| 71 | + - Ignore specific C data structures | |
| 72 | + - Use alternative reference names | |
| 73 | + - Specify different reference types | |
| 74 | + | |
| 75 | +:param warn-broken: | |
| 76 | + (Used with generate-cross-refs) | |
| 77 | + | |
| 78 | + Enables warnings when auto-generated cross-references don't point to | |
| 79 | + existing documentation targets. | |
| 29 | 80 | """ |
| 30 | 81 | |
| 31 | 82 | # ============================================================================== |
| @@ -33,161 +84,366 @@ · 1 unmodified line | ||
| 33 | 84 | # ============================================================================== |
| 34 | 85 | |
| 35 | 86 | import os.path |
| 87 | +import re | |
| 88 | +import sys | |
| 36 | 89 | |
| 37 | 90 | from docutils import io, nodes, statemachine |
| 38 | −from docutils.utils.error_reporting import SafeString, ErrorString | |
| 39 | −from docutils.parsers.rst import directives | |
| 91 | +from docutils.statemachine import ViewList | |
| 92 | +from docutils.parsers.rst import Directive, directives | |
| 40 | 93 | from docutils.parsers.rst.directives.body import CodeBlock, NumberLines |
| 41 | −from docutils.parsers.rst.directives.misc import Include | |
| 42 | 94 | |
| 43 | −__version__ = '1.0' | |
| 95 | +from sphinx.util import logging | |
| 44 | 96 | |
| 45 | −# ============================================================================== | |
| 46 | −def setup(app): | |
| 47 | −# ============================================================================== | |
| 97 | +srctree = os.path.abspath(os.environ["srctree"]) | |
| 98 | +sys.path.insert(0, os.path.join(srctree, "tools/docs/lib")) | |
| 48 | 99 | |
| 49 | − app.add_directive("kernel-include", KernelInclude) | |
| 50 | − return dict( | |
| 51 | − version = __version__, | |
| 52 | − parallel_read_safe = True, | |
| 53 | − parallel_write_safe = True | |
| 54 | − ) | |
| 100 | +from parse_data_structs import ParseDataStructs | |
| 55 | 101 | |
| 56 | −# ============================================================================== | |
| 57 | −class KernelInclude(Include): | |
| 58 | −# ============================================================================== | |
| 102 | +__version__ = "1.0" | |
| 103 | +logger = logging.getLogger(__name__) | |
| 59 | 104 | |
| 60 | − """KernelInclude (``kernel-include``) directive""" | |
| 105 | +RE_DOMAIN_REF = re.compile(r'\\ :(ref|c:type|c:func):`([^<`]+)(?:<([^>]+)>)?`\\') | |
| 106 | +RE_SIMPLE_REF = re.compile(r'`([^`]+)`') | |
| 61 | 107 | |
| 62 | − def run(self): | |
| 63 | − env = self.state.document.settings.env | |
| 64 | − path = os.path.realpath( | |
| 65 | − os.path.expandvars(self.arguments[0])) | |
| 108 | +def ErrorString(exc): # Shamelessly stolen from docutils | |
| 109 | + return f'{exc.__class__.__name}: {exc}' | |
| 66 | 110 | |
| 67 | − # to get a bit security back, prohibit /etc: | |
| 68 | − if path.startswith(os.sep + "etc"): | |
| 69 | − raise self.severe( | |
| 70 | − 'Problems with "%s" directive, prohibited path: %s' | |
| 71 | − % (self.name, path)) | |
| 72 | 111 | |
| 73 | − self.arguments[0] = path | |
| 112 | +# ============================================================================== | |
| 113 | +class KernelInclude(Directive): | |
| 114 | + """ | |
| 115 | + KernelInclude (``kernel-include``) directive | |
| 74 | 116 | |
| 75 | − env.note_dependency(os.path.abspath(path)) | |
| 117 | + Most of the stuff here came from Include directive defined at: | |
| 118 | + docutils/parsers/rst/directives/misc.py | |
| 76 | 119 | |
| 77 | − #return super(KernelInclude, self).run() # won't work, see HINTs in _run() | |
| 78 | − return self._run() | |
| 120 | + Yet, overriding the class don't has any benefits: the original class | |
| 121 | + only have run() and argument list. Not all of them are implemented, | |
| 122 | + when checked against latest Sphinx version, as with time more arguments | |
| 123 | + were added. | |
| 79 | 124 | |
| 80 | − def _run(self): | |
| 81 | − """Include a file as part of the content of this reST file.""" | |
| 125 | + So, keep its own list of supported arguments | |
| 126 | + """ | |
| 82 | 127 | |
| 83 | − # HINT: I had to copy&paste the whole Include.run method. I'am not happy | |
| 84 | − # with this, but due to security reasons, the Include.run method does | |
| 85 | − # not allow absolute or relative pathnames pointing to locations *above* | |
| 86 | − # the filesystem tree where the reST document is placed. | |
| 128 | + required_arguments = 1 | |
| 129 | + optional_arguments = 0 | |
| 130 | + final_argument_whitespace = True | |
| 131 | + option_spec = { | |
| 132 | + 'literal': directives.flag, | |
| 133 | + 'code': directives.unchanged, | |
| 134 | + 'encoding': directives.encoding, | |
| 135 | + 'tab-width': int, | |
| 136 | + 'start-line': int, | |
| 137 | + 'end-line': int, | |
| 138 | + 'start-after': directives.unchanged_required, | |
| 139 | + 'end-before': directives.unchanged_required, | |
| 140 | + # ignored except for 'literal' or 'code': | |
| 141 | + 'number-lines': directives.unchanged, # integer or None | |
| 142 | + 'class': directives.class_option, | |
| 87 | 143 | |
| 88 | − if not self.state.document.settings.file_insertion_enabled: | |
| 89 | − raise self.warning('"%s" directive disabled.' % self.name) | |
| 90 | − source = self.state_machine.input_lines.source( | |
| 91 | − self.lineno - self.state_machine.input_offset - 1) | |
| 92 | − source_dir = os.path.dirname(os.path.abspath(source)) | |
| 93 | − path = directives.path(self.arguments[0]) | |
| 94 | − if path.startswith('<') and path.endswith('>'): | |
| 95 | − path = os.path.join(self.standard_include_path, path[1:-1]) | |
| 96 | − path = os.path.normpath(os.path.join(source_dir, path)) | |
| 144 | + # Arguments that aren't from Sphinx Include directive | |
| 145 | + 'generate-cross-refs': directives.flag, | |
| 146 | + 'warn-broken': directives.flag, | |
| 147 | + 'toc': directives.flag, | |
| 148 | + 'exception-file': directives.unchanged, | |
| 149 | + } | |
| 97 | 150 | |
| 98 | − # HINT: this is the only line I had to change / commented out: | |
| 99 | − #path = utils.relative_path(None, path) | |
| 151 | + def read_rawtext(self, path, encoding): | |
| 152 | + """Read and process file content with error handling""" | |
| 153 | + try: | |
| 154 | + self.state.document.settings.record_dependencies.add(path) | |
| 155 | + include_file = io.FileInput(source_path=path, | |
| 156 | + encoding=encoding, | |
| 157 | + error_handler=self.state.document.settings.input_encoding_error_handler) | |
| 158 | + except UnicodeEncodeError: | |
| 159 | + raise self.severe('Problems with directive path:\n' | |
| 160 | + 'Cannot encode input file path "%s" ' | |
| 161 | + '(wrong locale?).' % path) | |
| 162 | + except IOError as error: | |
| 163 | + raise self.severe('Problems with directive path:\n%s.' % ErrorString(error)) | |
| 100 | 164 | |
| 101 | − encoding = self.options.get( | |
| 102 | − 'encoding', self.state.document.settings.input_encoding) | |
| 103 | − e_handler=self.state.document.settings.input_encoding_error_handler | |
| 104 | − tab_width = self.options.get( | |
| 105 | − 'tab-width', self.state.document.settings.tab_width) | |
| 106 | − try: | |
| 107 | − self.state.document.settings.record_dependencies.add(path) | |
| 108 | − include_file = io.FileInput(source_path=path, | |
| 109 | − encoding=encoding, | |
| 110 | − error_handler=e_handler) | |
| 111 | − except UnicodeEncodeError as error: | |
| 112 | − raise self.severe('Problems with "%s" directive path:\n' | |
| 113 | − 'Cannot encode input file path "%s" ' | |
| 114 | − '(wrong locale?).' % | |
| 115 | − (self.name, SafeString(path))) | |
| 116 | − except IOError as error: | |
| 117 | − raise self.severe('Problems with "%s" directive path:\n%s.' % | |
| 118 | − (self.name, ErrorString(error))) | |
| 165 | + try: | |
| 166 | + return include_file.read() | |
| 167 | + except UnicodeError as error: | |
| 168 | + raise self.severe('Problem with directive:\n%s' % ErrorString(error)) | |
| 169 | + | |
| 170 | + def apply_range(self, rawtext): | |
| 171 | + """ | |
| 172 | + Handles start-line, end-line, start-after and end-before parameters | |
| 173 | + """ | |
| 174 | + | |
| 175 | + # Get to-be-included content | |
| 119 | 176 | startline = self.options.get('start-line', None) |
| 120 | 177 | endline = self.options.get('end-line', None) |
| 121 | 178 | try: |
| 122 | 179 | if startline or (endline is not None): |
| 123 | − lines = include_file.readlines() | |
| 124 | − rawtext = ''.join(lines[startline:endline]) | |
| 125 | − else: | |
| 126 | − rawtext = include_file.read() | |
| 180 | + lines = rawtext.splitlines() | |
| 181 | + rawtext = '\n'.join(lines[startline:endline]) | |
| 127 | 182 | except UnicodeError as error: |
| 128 | − raise self.severe('Problem with "%s" directive:\n%s' % | |
| 129 | − (self.name, ErrorString(error))) | |
| 183 | + raise self.severe(f'Problem with "{self.name}" directive:\n' | |
| 184 | + + io.error_string(error)) | |
| 130 | 185 | # start-after/end-before: no restrictions on newlines in match-text, |
| 131 | 186 | # and no restrictions on matching inside lines vs. line boundaries |
| 132 | − after_text = self.options.get('start-after', None) | |
| 187 | + after_text = self.options.get("start-after", None) | |
| 133 | 188 | if after_text: |
| 134 | 189 | # skip content in rawtext before *and incl.* a matching text |
| 135 | 190 | after_index = rawtext.find(after_text) |
| 136 | 191 | if after_index < 0: |
| 137 | 192 | raise self.severe('Problem with "start-after" option of "%s" ' |
| 138 | − 'directive:\nText not found.' % self.name) | |
| 139 | − rawtext = rawtext[after_index + len(after_text):] | |
| 140 | − before_text = self.options.get('end-before', None) | |
| 193 | + "directive:\nText not found." % self.name) | |
| 194 | + rawtext = rawtext[after_index + len(after_text) :] | |
| 195 | + before_text = self.options.get("end-before", None) | |
| 141 | 196 | if before_text: |
| 142 | 197 | # skip content in rawtext after *and incl.* a matching text |
| 143 | 198 | before_index = rawtext.find(before_text) |
| 144 | 199 | if before_index < 0: |
| 145 | 200 | raise self.severe('Problem with "end-before" option of "%s" ' |
| 146 | − 'directive:\nText not found.' % self.name) | |
| 201 | + "directive:\nText not found." % self.name) | |
| 147 | 202 | rawtext = rawtext[:before_index] |
| 148 | 203 | |
| 204 | + return rawtext | |
| 205 | + | |
| 206 | + def xref_text(self, env, path, tab_width): | |
| 207 | + """ | |
| 208 | + Read and add contents from a C file parsed to have cross references. | |
| 209 | + | |
| 210 | + There are two types of supported output here: | |
| 211 | + - A C source code with cross-references; | |
| 212 | + - a TOC table containing cross references. | |
| 213 | + """ | |
| 214 | + parser = ParseDataStructs() | |
| 215 | + parser.parse_file(path) | |
| 216 | + | |
| 217 | + if 'exception-file' in self.options: | |
| 218 | + source_dir = os.path.dirname(os.path.abspath( | |
| 219 | + self.state_machine.input_lines.source( | |
| 220 | + self.lineno - self.state_machine.input_offset - 1))) | |
| 221 | + exceptions_file = os.path.join(source_dir, self.options['exception-file']) | |
| 222 | + parser.process_exceptions(exceptions_file) | |
| 223 | + | |
| 224 | + # Store references on a symbol dict to be used at check time | |
| 225 | + if 'warn-broken' in self.options: | |
| 226 | + env._xref_files.add(path) | |
| 227 | + | |
| 228 | + if "toc" not in self.options: | |
| 229 | + | |
| 230 | + rawtext = ".. parsed-literal::\n\n" + parser.gen_output() | |
| 231 | + self.apply_range(rawtext) | |
| 232 | + | |
| 233 | + include_lines = statemachine.string2lines(rawtext, tab_width, | |
| 234 | + convert_whitespace=True) | |
| 235 | + | |
| 236 | + # Sphinx always blame the ".. <directive>", so placing | |
| 237 | + # line numbers here won't make any difference | |
| 238 | + | |
| 239 | + self.state_machine.insert_input(include_lines, path) | |
| 240 | + return [] | |
| 241 | + | |
| 242 | + # TOC output is a ReST file, not a literal. So, we can add line | |
| 243 | + # numbers | |
| 244 | + | |
| 245 | + rawtext = parser.gen_toc() | |
| 246 | + | |
| 149 | 247 | include_lines = statemachine.string2lines(rawtext, tab_width, |
| 150 | 248 | convert_whitespace=True) |
| 151 | − if 'literal' in self.options: | |
| 152 | − # Convert tabs to spaces, if `tab_width` is positive. | |
| 153 | − if tab_width >= 0: | |
| 154 | − text = rawtext.expandtabs(tab_width) | |
| 155 | − else: | |
| 156 | − text = rawtext | |
| 157 | − literal_block = nodes.literal_block(rawtext, source=path, | |
| 158 | − classes=self.options.get('class', [])) | |
| 159 | − literal_block.line = 1 | |
| 160 | − self.add_name(literal_block) | |
| 161 | − if 'number-lines' in self.options: | |
| 162 | − try: | |
| 163 | − startline = int(self.options['number-lines'] or 1) | |
| 164 | − except ValueError: | |
| 165 | − raise self.error(':number-lines: with non-integer ' | |
| 166 | − 'start value') | |
| 167 | − endline = startline + len(include_lines) | |
| 168 | − if text.endswith('\n'): | |
| 169 | − text = text[:-1] | |
| 170 | − tokens = NumberLines([([], text)], startline, endline) | |
| 171 | − for classes, value in tokens: | |
| 172 | − if classes: | |
| 173 | − literal_block += nodes.inline(value, value, | |
| 174 | − classes=classes) | |
| 175 | − else: | |
| 176 | − literal_block += nodes.Text(value, value) | |
| 177 | − else: | |
| 178 | − literal_block += nodes.Text(text, text) | |
| 179 | − return [literal_block] | |
| 180 | − if 'code' in self.options: | |
| 181 | − self.options['source'] = path | |
| 182 | − codeblock = CodeBlock(self.name, | |
| 183 | − [self.options.pop('code')], # arguments | |
| 184 | − self.options, | |
| 185 | − include_lines, # content | |
| 186 | − self.lineno, | |
| 187 | − self.content_offset, | |
| 188 | − self.block_text, | |
| 189 | − self.state, | |
| 190 | − self.state_machine) | |
| 191 | − return codeblock.run() | |
| 192 | − self.state_machine.insert_input(include_lines, path) | |
| 249 | + | |
| 250 | + # Append line numbers data | |
| 251 | + | |
| 252 | + startline = self.options.get('start-line', None) | |
| 253 | + | |
| 254 | + result = ViewList() | |
| 255 | + if startline and startline > 0: | |
| 256 | + offset = startline - 1 | |
| 257 | + else: | |
| 258 | + offset = 0 | |
| 259 | + | |
| 260 | + for ln, line in enumerate(include_lines, start=offset): | |
| 261 | + result.append(line, path, ln) | |
| 262 | + | |
| 263 | + self.state_machine.insert_input(result, path) | |
| 264 | + | |
| 193 | 265 | return [] |
| 266 | + | |
| 267 | + def literal(self, path, tab_width, rawtext): | |
| 268 | + """Output a literal block""" | |
| 269 | + | |
| 270 | + # Convert tabs to spaces, if `tab_width` is positive. | |
| 271 | + if tab_width >= 0: | |
| 272 | + text = rawtext.expandtabs(tab_width) | |
| 273 | + else: | |
| 274 | + text = rawtext | |
| 275 | + literal_block = nodes.literal_block(rawtext, source=path, | |
| 276 | + classes=self.options.get("class", [])) | |
| 277 | + literal_block.line = 1 | |
| 278 | + self.add_name(literal_block) | |
| 279 | + if "number-lines" in self.options: | |
| 280 | + try: | |
| 281 | + startline = int(self.options["number-lines"] or 1) | |
| 282 | + except ValueError: | |
| 283 | + raise self.error(":number-lines: with non-integer start value") | |
| 284 | + endline = startline + len(include_lines) | |
| 285 | + if text.endswith("\n"): | |
| 286 | + text = text[:-1] | |
| 287 | + tokens = NumberLines([([], text)], startline, endline) | |
| 288 | + for classes, value in tokens: | |
| 289 | + if classes: | |
| 290 | + literal_block += nodes.inline(value, value, | |
| 291 | + classes=classes) | |
| 292 | + else: | |
| 293 | + literal_block += nodes.Text(value, value) | |
| 294 | + else: | |
| 295 | + literal_block += nodes.Text(text, text) | |
| 296 | + return [literal_block] | |
| 297 | + | |
| 298 | + def code(self, path, tab_width): | |
| 299 | + """Output a code block""" | |
| 300 | + | |
| 301 | + include_lines = statemachine.string2lines(rawtext, tab_width, | |
| 302 | + convert_whitespace=True) | |
| 303 | + | |
| 304 | + self.options["source"] = path | |
| 305 | + codeblock = CodeBlock(self.name, | |
| 306 | + [self.options.pop("code")], # arguments | |
| 307 | + self.options, | |
| 308 | + include_lines, | |
| 309 | + self.lineno, | |
| 310 | + self.content_offset, | |
| 311 | + self.block_text, | |
| 312 | + self.state, | |
| 313 | + self.state_machine) | |
| 314 | + return codeblock.run() | |
| 315 | + | |
| 316 | + def run(self): | |
| 317 | + """Include a file as part of the content of this reST file.""" | |
| 318 | + env = self.state.document.settings.env | |
| 319 | + | |
| 320 | + # | |
| 321 | + # The include logic accepts only patches relative to the | |
| 322 | + # Kernel source tree. The logic does check it to prevent | |
| 323 | + # directory traverse issues. | |
| 324 | + # | |
| 325 | + | |
| 326 | + srctree = os.path.abspath(os.environ["srctree"]) | |
| 327 | + | |
| 328 | + path = os.path.expandvars(self.arguments[0]) | |
| 329 | + src_path = os.path.join(srctree, path) | |
| 330 | + | |
| 331 | + if os.path.isfile(src_path): | |
| 332 | + base = srctree | |
| 333 | + path = src_path | |
| 334 | + else: | |
| 335 | + raise self.warning(f'File "%s" doesn\'t exist', path) | |
| 336 | + | |
| 337 | + abs_base = os.path.abspath(base) | |
| 338 | + abs_full_path = os.path.abspath(os.path.join(base, path)) | |
| 339 | + | |
| 340 | + try: | |
| 341 | + if os.path.commonpath([abs_full_path, abs_base]) != abs_base: | |
| 342 | + raise self.severe('Problems with "%s" directive, prohibited path: %s' % | |
| 343 | + (self.name, path)) | |
| 344 | + except ValueError: | |
| 345 | + # Paths don't have the same drive (Windows) or other incompatibility | |
| 346 | + raise self.severe('Problems with "%s" directive, invalid path: %s' % | |
| 347 | + (self.name, path)) | |
| 348 | + | |
| 349 | + self.arguments[0] = path | |
| 350 | + | |
| 351 | + # | |
| 352 | + # Add path location to Sphinx dependencies to ensure proper cache | |
| 353 | + # invalidation check. | |
| 354 | + # | |
| 355 | + | |
| 356 | + env.note_dependency(os.path.abspath(path)) | |
| 357 | + | |
| 358 | + if not self.state.document.settings.file_insertion_enabled: | |
| 359 | + raise self.warning('"%s" directive disabled.' % self.name) | |
| 360 | + source = self.state_machine.input_lines.source(self.lineno - | |
| 361 | + self.state_machine.input_offset - 1) | |
| 362 | + source_dir = os.path.dirname(os.path.abspath(source)) | |
| 363 | + path = directives.path(self.arguments[0]) | |
| 364 | + if path.startswith("<") and path.endswith(">"): | |
| 365 | + path = os.path.join(self.standard_include_path, path[1:-1]) | |
| 366 | + path = os.path.normpath(os.path.join(source_dir, path)) | |
| 367 | + | |
| 368 | + # HINT: this is the only line I had to change / commented out: | |
| 369 | + # path = utils.relative_path(None, path) | |
| 370 | + | |
| 371 | + encoding = self.options.get("encoding", | |
| 372 | + self.state.document.settings.input_encoding) | |
| 373 | + tab_width = self.options.get("tab-width", | |
| 374 | + self.state.document.settings.tab_width) | |
| 375 | + | |
| 376 | + # Get optional arguments to related to cross-references generation | |
| 377 | + if "generate-cross-refs" in self.options: | |
| 378 | + return self.xref_text(env, path, tab_width) | |
| 379 | + | |
| 380 | + rawtext = self.read_rawtext(path, encoding) | |
| 381 | + rawtext = self.apply_range(rawtext) | |
| 382 | + | |
| 383 | + if "code" in self.options: | |
| 384 | + return self.code(path, tab_width, rawtext) | |
| 385 | + | |
| 386 | + return self.literal(path, tab_width, rawtext) | |
| 387 | + | |
| 388 | +# ============================================================================== | |
| 389 | + | |
| 390 | +reported = set() | |
| 391 | + | |
| 392 | +def check_missing_refs(app, env, node, contnode): | |
| 393 | + """Check broken refs for the files it creates xrefs""" | |
| 394 | + if not node.source: | |
| 395 | + return None | |
| 396 | + | |
| 397 | + try: | |
| 398 | + xref_files = env._xref_files | |
| 399 | + except AttributeError: | |
| 400 | + logger.critical("FATAL: _xref_files not initialized!") | |
| 401 | + raise | |
| 402 | + | |
| 403 | + # Only show missing references for kernel-include reference-parsed files | |
| 404 | + if node.source not in xref_files: | |
| 405 | + return None | |
| 406 | + | |
| 407 | + target = node.get('reftarget', '') | |
| 408 | + domain = node.get('refdomain', 'std') | |
| 409 | + reftype = node.get('reftype', '') | |
| 410 | + | |
| 411 | + msg = f"can't link to: {domain}:{reftype}:: {target}" | |
| 412 | + | |
| 413 | + # Don't duplicate warnings | |
| 414 | + data = (node.source, msg) | |
| 415 | + if data in reported: | |
| 416 | + return None | |
| 417 | + reported.add(data) | |
| 418 | + | |
| 419 | + logger.warning(msg, location=node, type='ref', subtype='missing') | |
| 420 | + | |
| 421 | + return None | |
| 422 | + | |
| 423 | +def merge_xref_info(app, env, docnames, other): | |
| 424 | + """ | |
| 425 | + As each process modify env._xref_files, we need to merge them back. | |
| 426 | + """ | |
| 427 | + if not hasattr(other, "_xref_files"): | |
| 428 | + return | |
| 429 | + env._xref_files.update(getattr(other, "_xref_files", set())) | |
| 430 | + | |
| 431 | +def init_xref_docs(app, env, docnames): | |
| 432 | + """Initialize a list of files that we're generating cross references¨""" | |
| 433 | + app.env._xref_files = set() | |
| 434 | + | |
| 435 | +# ============================================================================== | |
| 436 | + | |
| 437 | +def setup(app): | |
| 438 | + """Setup Sphinx exension""" | |
| 439 | + | |
| 440 | + app.connect("env-before-read-docs", init_xref_docs) | |
| 441 | + app.connect("env-merge-info", merge_xref_info) | |
| 442 | + app.add_directive("kernel-include", KernelInclude) | |
| 443 | + app.connect("missing-reference", check_missing_refs) | |
| 444 | + | |
| 445 | + return { | |
| 446 | + "version": __version__, | |
| 447 | + "parallel_read_safe": True, | |
| 448 | + "parallel_write_safe": True, | |
| 449 | + } | |
| @@ -22,10 +22,12 @@ import re · 21 unmodified lines | ||
| 22 | 22 | import os.path |
| 23 | 23 | |
| 24 | 24 | from docutils import statemachine |
| 25 | −from docutils.utils.error_reporting import ErrorString | |
| 26 | 25 | from docutils.parsers.rst import Directive |
| 27 | 26 | from docutils.parsers.rst.directives.misc import Include |
| 28 | 27 | |
| 28 | +def ErrorString(exc): # Shamelessly stolen from docutils | |
| 29 | + return f'{exc.__class__.__name}: {exc}' | |
| 30 | + | |
| 29 | 31 | __version__ = '1.0' |
| 30 | 32 | |
| 31 | 33 | def setup(app): |
| @@ -1,404 +0,0 @@ | ||
| 1 | −#!/usr/bin/env perl | |
| 2 | −# SPDX-License-Identifier: GPL-2.0 | |
| 3 | −# Copyright (c) 2016 by Mauro Carvalho Chehab <mchehab@kernel.org>. | |
| 4 | − | |
| 5 | −use strict; | |
| 6 | −use Text::Tabs; | |
| 7 | −use Getopt::Long; | |
| 8 | −use Pod::Usage; | |
| 9 | − | |
| 10 | −my $debug; | |
| 11 | −my $help; | |
| 12 | −my $man; | |
| 13 | − | |
| 14 | −GetOptions( | |
| 15 | − "debug" => \$debug, | |
| 16 | − 'usage|?' => \$help, | |
| 17 | − 'help' => \$man | |
| 18 | −) or pod2usage(2); | |
| 19 | − | |
| 20 | −pod2usage(1) if $help; | |
| 21 | −pod2usage(-exitstatus => 0, -verbose => 2) if $man; | |
| 22 | −pod2usage(2) if (scalar @ARGV < 2 || scalar @ARGV > 3); | |
| 23 | − | |
| 24 | −my ($file_in, $file_out, $file_exceptions) = @ARGV; | |
| 25 | − | |
| 26 | −my $data; | |
| 27 | −my %ioctls; | |
| 28 | −my %defines; | |
| 29 | −my %typedefs; | |
| 30 | −my %enums; | |
| 31 | −my %enum_symbols; | |
| 32 | −my %structs; | |
| 33 | − | |
| 34 | −require Data::Dumper if ($debug); | |
| 35 | − | |
| 36 | −# | |
| 37 | −# read the file and get identifiers | |
| 38 | −# | |
| 39 | − | |
| 40 | −my $is_enum = 0; | |
| 41 | −my $is_comment = 0; | |
| 42 | −open IN, $file_in or die "Can't open $file_in"; | |
| 43 | −while (<IN>) { | |
| 44 | − $data .= $_; | |
| 45 | − | |
| 46 | − my $ln = $_; | |
| 47 | − if (!$is_comment) { | |
| 48 | − $ln =~ s,/\*.*(\*/),,g; | |
| 49 | − | |
| 50 | − $is_comment = 1 if ($ln =~ s,/\*.*,,); | |
| 51 | − } else { | |
| 52 | − if ($ln =~ s,^(.*\*/),,) { | |
| 53 | − $is_comment = 0; | |
| 54 | − } else { | |
| 55 | − next; | |
| 56 | − } | |
| 57 | − } | |
| 58 | − | |
| 59 | − if ($is_enum && $ln =~ m/^\s*([_\w][\w\d_]+)\s*[\,=]?/) { | |
| 60 | − my $s = $1; | |
| 61 | − my $n = $1; | |
| 62 | − $n =~ tr/A-Z/a-z/; | |
| 63 | − $n =~ tr/_/-/; | |
| 64 | − | |
| 65 | − $enum_symbols{$s} = "\\ :ref:`$s <$n>`\\ "; | |
| 66 | − | |
| 67 | − $is_enum = 0 if ($is_enum && m/\}/); | |
| 68 | − next; | |
| 69 | − } | |
| 70 | − $is_enum = 0 if ($is_enum && m/\}/); | |
| 71 | − | |
| 72 | − if ($ln =~ m/^\s*#\s*define\s+([_\w][\w\d_]+)\s+_IO/) { | |
| 73 | − my $s = $1; | |
| 74 | − my $n = $1; | |
| 75 | − $n =~ tr/A-Z/a-z/; | |
| 76 | − | |
| 77 | − $ioctls{$s} = "\\ :ref:`$s <$n>`\\ "; | |
| 78 | − next; | |
| 79 | − } | |
| 80 | − | |
| 81 | − if ($ln =~ m/^\s*#\s*define\s+([_\w][\w\d_]+)\s+/) { | |
| 82 | − my $s = $1; | |
| 83 | − my $n = $1; | |
| 84 | − $n =~ tr/A-Z/a-z/; | |
| 85 | − $n =~ tr/_/-/; | |
| 86 | − | |
| 87 | − $defines{$s} = "\\ :ref:`$s <$n>`\\ "; | |
| 88 | − next; | |
| 89 | − } | |
| 90 | − | |
| 91 | − if ($ln =~ m/^\s*typedef\s+([_\w][\w\d_]+)\s+(.*)\s+([_\w][\w\d_]+);/) { | |
| 92 | − my $s = $2; | |
| 93 | − my $n = $3; | |
| 94 | − | |
| 95 | − $typedefs{$n} = "\\ :c:type:`$n <$s>`\\ "; | |
| 96 | − next; | |
| 97 | − } | |
| 98 | − if ($ln =~ m/^\s*enum\s+([_\w][\w\d_]+)\s+\{/ | |
| 99 | − || $ln =~ m/^\s*enum\s+([_\w][\w\d_]+)$/ | |
| 100 | − || $ln =~ m/^\s*typedef\s*enum\s+([_\w][\w\d_]+)\s+\{/ | |
| 101 | − || $ln =~ m/^\s*typedef\s*enum\s+([_\w][\w\d_]+)$/) { | |
| 102 | − my $s = $1; | |
| 103 | − | |
| 104 | − $enums{$s} = "enum :c:type:`$s`\\ "; | |
| 105 | − | |
| 106 | − $is_enum = $1; | |
| 107 | − next; | |
| 108 | − } | |
| 109 | − if ($ln =~ m/^\s*struct\s+([_\w][\w\d_]+)\s+\{/ | |
| 110 | − || $ln =~ m/^\s*struct\s+([[_\w][\w\d_]+)$/ | |
| 111 | − || $ln =~ m/^\s*typedef\s*struct\s+([_\w][\w\d_]+)\s+\{/ | |
| 112 | − || $ln =~ m/^\s*typedef\s*struct\s+([[_\w][\w\d_]+)$/ | |
| 113 | − ) { | |
| 114 | − my $s = $1; | |
| 115 | − | |
| 116 | − $structs{$s} = "struct $s\\ "; | |
| 117 | − next; | |
| 118 | − } | |
| 119 | −} | |
| 120 | −close IN; | |
| 121 | − | |
| 122 | −# | |
| 123 | −# Handle multi-line typedefs | |
| 124 | −# | |
| 125 | − | |
| 126 | −my @matches = ($data =~ m/typedef\s+struct\s+\S+?\s*\{[^\}]+\}\s*(\S+)\s*\;/g, | |
| 127 | − $data =~ m/typedef\s+enum\s+\S+?\s*\{[^\}]+\}\s*(\S+)\s*\;/g,); | |
| 128 | −foreach my $m (@matches) { | |
| 129 | − my $s = $m; | |
| 130 | − | |
| 131 | − $typedefs{$s} = "\\ :c:type:`$s`\\ "; | |
| 132 | − next; | |
| 133 | −} | |
| 134 | − | |
| 135 | −# | |
| 136 | −# Handle exceptions, if any | |
| 137 | −# | |
| 138 | − | |
| 139 | −my %def_reftype = ( | |
| 140 | − "ioctl" => ":ref", | |
| 141 | − "define" => ":ref", | |
| 142 | − "symbol" => ":ref", | |
| 143 | − "typedef" => ":c:type", | |
| 144 | − "enum" => ":c:type", | |
| 145 | − "struct" => ":c:type", | |
| 146 | −); | |
| 147 | − | |
| 148 | −if ($file_exceptions) { | |
| 149 | − open IN, $file_exceptions or die "Can't read $file_exceptions"; | |
| 150 | − while (<IN>) { | |
| 151 | − next if (m/^\s*$/ || m/^\s*#/); | |
| 152 | − | |
| 153 | − # Parsers to ignore a symbol | |
| 154 | − | |
| 155 | − if (m/^ignore\s+ioctl\s+(\S+)/) { | |
| 156 | − delete $ioctls{$1} if (exists($ioctls{$1})); | |
| 157 | − next; | |
| 158 | − } | |
| 159 | − if (m/^ignore\s+define\s+(\S+)/) { | |
| 160 | − delete $defines{$1} if (exists($defines{$1})); | |
| 161 | − next; | |
| 162 | − } | |
| 163 | − if (m/^ignore\s+typedef\s+(\S+)/) { | |
| 164 | − delete $typedefs{$1} if (exists($typedefs{$1})); | |
| 165 | − next; | |
| 166 | − } | |
| 167 | − if (m/^ignore\s+enum\s+(\S+)/) { | |
| 168 | − delete $enums{$1} if (exists($enums{$1})); | |
| 169 | − next; | |
| 170 | − } | |
| 171 | − if (m/^ignore\s+struct\s+(\S+)/) { | |
| 172 | − delete $structs{$1} if (exists($structs{$1})); | |
| 173 | − next; | |
| 174 | − } | |
| 175 | − if (m/^ignore\s+symbol\s+(\S+)/) { | |
| 176 | − delete $enum_symbols{$1} if (exists($enum_symbols{$1})); | |
| 177 | − next; | |
| 178 | − } | |
| 179 | − | |
| 180 | − # Parsers to replace a symbol | |
| 181 | − my ($type, $old, $new, $reftype); | |
| 182 | − | |
| 183 | − if (m/^replace\s+(\S+)\s+(\S+)\s+(\S+)/) { | |
| 184 | − $type = $1; | |
| 185 | − $old = $2; | |
| 186 | − $new = $3; | |
| 187 | − } else { | |
| 188 | − die "Can't parse $file_exceptions: $_"; | |
| 189 | − } | |
| 190 | − | |
| 191 | − if ($new =~ m/^\:c\:(data|func|macro|type)\:\`(.+)\`/) { | |
| 192 | − $reftype = ":c:$1"; | |
| 193 | − $new = $2; | |
| 194 | − } elsif ($new =~ m/\:ref\:\`(.+)\`/) { | |
| 195 | − $reftype = ":ref"; | |
| 196 | − $new = $1; | |
| 197 | − } else { | |
| 198 | − $reftype = $def_reftype{$type}; | |
| 199 | − } | |
| 200 | − $new = "$reftype:`$old <$new>`"; | |
| 201 | − | |
| 202 | − if ($type eq "ioctl") { | |
| 203 | − $ioctls{$old} = $new if (exists($ioctls{$old})); | |
| 204 | − next; | |
| 205 | − } | |
| 206 | − if ($type eq "define") { | |
| 207 | − $defines{$old} = $new if (exists($defines{$old})); | |
| 208 | − next; | |
| 209 | − } | |
| 210 | − if ($type eq "symbol") { | |
| 211 | − $enum_symbols{$old} = $new if (exists($enum_symbols{$old})); | |
| 212 | − next; | |
| 213 | − } | |
| 214 | − if ($type eq "typedef") { | |
| 215 | − $typedefs{$old} = $new if (exists($typedefs{$old})); | |
| 216 | − next; | |
| 217 | − } | |
| 218 | − if ($type eq "enum") { | |
| 219 | − $enums{$old} = $new if (exists($enums{$old})); | |
| 220 | − next; | |
| 221 | − } | |
| 222 | − if ($type eq "struct") { | |
| 223 | − $structs{$old} = $new if (exists($structs{$old})); | |
| 224 | − next; | |
| 225 | − } | |
| 226 | − | |
| 227 | − die "Can't parse $file_exceptions: $_"; | |
| 228 | − } | |
| 229 | −} | |
| 230 | − | |
| 231 | −if ($debug) { | |
| 232 | − print Data::Dumper->Dump([\%ioctls], [qw(*ioctls)]) if (%ioctls); | |
| 233 | − print Data::Dumper->Dump([\%typedefs], [qw(*typedefs)]) if (%typedefs); | |
| 234 | − print Data::Dumper->Dump([\%enums], [qw(*enums)]) if (%enums); | |
| 235 | − print Data::Dumper->Dump([\%structs], [qw(*structs)]) if (%structs); | |
| 236 | − print Data::Dumper->Dump([\%defines], [qw(*defines)]) if (%defines); | |
| 237 | − print Data::Dumper->Dump([\%enum_symbols], [qw(*enum_symbols)]) if (%enum_symbols); | |
| 238 | −} | |
| 239 | − | |
| 240 | −# | |
| 241 | −# Align block | |
| 242 | −# | |
| 243 | −$data = expand($data); | |
| 244 | −$data = " " . $data; | |
| 245 | −$data =~ s/\n/\n /g; | |
| 246 | −$data =~ s/\n\s+$/\n/g; | |
| 247 | −$data =~ s/\n\s+\n/\n\n/g; | |
| 248 | − | |
| 249 | −# | |
| 250 | −# Add escape codes for special characters | |
| 251 | −# | |
| 252 | −$data =~ s,([\_\`\*\<\>\&\\\\:\/\|\%\$\#\{\}\~\^]),\\$1,g; | |
| 253 | − | |
| 254 | −$data =~ s,DEPRECATED,**DEPRECATED**,g; | |
| 255 | − | |
| 256 | −# | |
| 257 | −# Add references | |
| 258 | −# | |
| 259 | − | |
| 260 | −my $start_delim = "[ \n\t\(\=\*\@]"; | |
| 261 | −my $end_delim = "(\\s|,|\\\\=|\\\\:|\\;|\\\)|\\}|\\{)"; | |
| 262 | − | |
| 263 | −foreach my $r (keys %ioctls) { | |
| 264 | − my $s = $ioctls{$r}; | |
| 265 | − | |
| 266 | − $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g; | |
| 267 | − | |
| 268 | − print "$r -> $s\n" if ($debug); | |
| 269 | − | |
| 270 | − $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g; | |
| 271 | −} | |
| 272 | − | |
| 273 | −foreach my $r (keys %defines) { | |
| 274 | − my $s = $defines{$r}; | |
| 275 | − | |
| 276 | − $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g; | |
| 277 | − | |
| 278 | − print "$r -> $s\n" if ($debug); | |
| 279 | − | |
| 280 | − $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g; | |
| 281 | −} | |
| 282 | − | |
| 283 | −foreach my $r (keys %enum_symbols) { | |
| 284 | − my $s = $enum_symbols{$r}; | |
| 285 | − | |
| 286 | − $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g; | |
| 287 | − | |
| 288 | − print "$r -> $s\n" if ($debug); | |
| 289 | − | |
| 290 | − $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g; | |
| 291 | −} | |
| 292 | − | |
| 293 | −foreach my $r (keys %enums) { | |
| 294 | − my $s = $enums{$r}; | |
| 295 | − | |
| 296 | − $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g; | |
| 297 | − | |
| 298 | − print "$r -> $s\n" if ($debug); | |
| 299 | − | |
| 300 | − $data =~ s/enum\s+($r)$end_delim/$s$2/g; | |
| 301 | −} | |
| 302 | − | |
| 303 | −foreach my $r (keys %structs) { | |
| 304 | − my $s = $structs{$r}; | |
| 305 | − | |
| 306 | − $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g; | |
| 307 | − | |
| 308 | − print "$r -> $s\n" if ($debug); | |
| 309 | − | |
| 310 | − $data =~ s/struct\s+($r)$end_delim/$s$2/g; | |
| 311 | −} | |
| 312 | − | |
| 313 | −foreach my $r (keys %typedefs) { | |
| 314 | − my $s = $typedefs{$r}; | |
| 315 | − | |
| 316 | − $r =~ s,([\_\`\*\<\>\&\\\\:\/]),\\\\$1,g; | |
| 317 | − | |
| 318 | − print "$r -> $s\n" if ($debug); | |
| 319 | − $data =~ s/($start_delim)($r)$end_delim/$1$s$3/g; | |
| 320 | −} | |
| 321 | − | |
| 322 | −$data =~ s/\\ ([\n\s])/\1/g; | |
| 323 | − | |
| 324 | −# | |
| 325 | −# Generate output file | |
| 326 | −# | |
| 327 | − | |
| 328 | −my $title = $file_in; | |
| 329 | −$title =~ s,.*/,,; | |
| 330 | − | |
| 331 | −open OUT, "> $file_out" or die "Can't open $file_out"; | |
| 332 | −print OUT ".. -*- coding: utf-8; mode: rst -*-\n\n"; | |
| 333 | −print OUT "$title\n"; | |
| 334 | −print OUT "=" x length($title); | |
| 335 | −print OUT "\n\n.. parsed-literal::\n\n"; | |
| 336 | −print OUT $data; | |
| 337 | −close OUT; | |
| 338 | − | |
| 339 | −__END__ | |
| 340 | − | |
| 341 | −=head1 NAME | |
| 342 | − | |
| 343 | −parse_headers.pl - parse a C file, in order to identify functions, structs, | |
| 344 | −enums and defines and create cross-references to a Sphinx book. | |
| 345 | − | |
| 346 | −=head1 SYNOPSIS | |
| 347 | − | |
| 348 | −B<parse_headers.pl> [<options>] <C_FILE> <OUT_FILE> [<EXCEPTIONS_FILE>] | |
| 349 | − | |
| 350 | −Where <options> can be: --debug, --help or --usage. | |
| 351 | − | |
| 352 | −=head1 OPTIONS | |
| 353 | − | |
| 354 | −=over 8 | |
| 355 | − | |
| 356 | −=item B<--debug> | |
| 357 | − | |
| 358 | −Put the script in verbose mode, useful for debugging. | |
| 359 | − | |
| 360 | −=item B<--usage> | |
| 361 | − | |
| 362 | −Prints a brief help message and exits. | |
| 363 | − | |
| 364 | −=item B<--help> | |
| 365 | − | |
| 366 | −Prints a more detailed help message and exits. | |
| 367 | − | |
| 368 | −=back | |
| 369 | − | |
| 370 | −=head1 DESCRIPTION | |
| 371 | − | |
| 372 | −Convert a C header or source file (C_FILE), into a ReStructured Text | |
| 373 | −included via ..parsed-literal block with cross-references for the | |
| 374 | −documentation files that describe the API. It accepts an optional | |
| 375 | −EXCEPTIONS_FILE with describes what elements will be either ignored or | |
| 376 | −be pointed to a non-default reference. | |
| 377 | − | |
| 378 | −The output is written at the (OUT_FILE). | |
| 379 | − | |
| 380 | −It is capable of identifying defines, functions, structs, typedefs, | |
| 381 | −enums and enum symbols and create cross-references for all of them. | |
| 382 | −It is also capable of distinguish #define used for specifying a Linux | |
| 383 | −ioctl. | |
| 384 | − | |
| 385 | −The EXCEPTIONS_FILE contain two rules to allow ignoring a symbol or | |
| 386 | −to replace the default references by a custom one. | |
| 387 | − | |
| 388 | −Please read Documentation/doc-guide/parse-headers.rst at the Kernel's | |
| 389 | −tree for more details. | |
| 390 | − | |
| 391 | −=head1 BUGS | |
| 392 | − | |
| 393 | −Report bugs to Mauro Carvalho Chehab <mchehab@kernel.org> | |
| 394 | − | |
| 395 | −=head1 COPYRIGHT | |
| 396 | − | |
| 397 | −Copyright (c) 2016 by Mauro Carvalho Chehab <mchehab@kernel.org>. | |
| 398 | − | |
| 399 | −License GPLv2: GNU GPL version 2 <https://gnu.org/licenses/gpl.html>. | |
| 400 | − | |
| 401 | −This is free software: you are free to change and redistribute it. | |
| 402 | −There is NO WARRANTY, to the extent permitted by law. | |
| 403 | − | |
| 404 | −=cut | |
| @@ -1,4 +1,5 @@ | ||
| 1 | −<!-- SPDX-License-Identifier: GPL-2.0 --> | |
| 1 | +{# SPDX-License-Identifier: GPL-2.0 #} | |
| 2 | + | |
| 2 | 3 | {# Create a local TOC the kernel way #} |
| 3 | 4 | <p> |
| 4 | 5 | <h3 class="kernel-toc-contents">Contents</h3> |
| @@ -1,5 +1,5 @@ | ||
| 1 | −<!-- SPDX-License-Identifier: GPL-2.0 --> | |
| 2 | −<!-- Copyright © 2023, Oracle and/or its affiliates. --> | |
| 1 | +{# SPDX-License-Identifier: GPL-2.0 #} | |
| 2 | +{# Copyright © 2023, Oracle and/or its affiliates. #} | |
| 3 | 3 | |
| 4 | 4 | {# Create a language menu for translations #} |
| 5 | 5 | {% if languages|length > 0: %} |
| @@ -104,7 +104,7 @@ Typical usage · 103 unmodified lines | ||
| 104 | 104 | rproc_shutdown(my_rproc); |
| 105 | 105 | } |
| 106 | 106 | |
| 107 | −API for implementors | |
| 107 | +API for implementers | |
| 108 | 108 | ==================== |
| 109 | 109 | |
| 110 | 110 | :: |
| @@ -19,7 +19,7 @@ this uses bootconfig file to describe tracing feature programming. · 18 unmodified lines | ||
| 19 | 19 | Options in the Boot Config |
| 20 | 20 | ========================== |
| 21 | 21 | |
| 22 | −Here is the list of available options list for boot time tracing in | |
| 22 | +Here is the list of available options for boot time tracing in | |
| 23 | 23 | boot config file [1]_. All options are under "ftrace." or "kernel." |
| 24 | 24 | prefix. See kernel parameters for the options which starts |
| 25 | 25 | with "kernel." prefix [2]_. |
| @@ -59,7 +59,7 @@ There is various methods of acquiring the state of the system when a kernel · 58 unmodified lines | ||
| 59 | 59 | crash occurs. This could be from the oops message in printk, or one could |
| 60 | 60 | use kexec/kdump. But these just show what happened at the time of the crash. |
| 61 | 61 | It can be very useful in knowing what happened up to the point of the crash. |
| 62 | −The tracing ring buffer, by default, is a circular buffer than will | |
| 62 | +The tracing ring buffer, by default, is a circular buffer that will | |
| 63 | 63 | overwrite older events with newer ones. When a crash happens, the content of |
| 64 | 64 | the ring buffer will be all the events that lead up to the crash. |
| 65 | 65 | |
| @@ -629,8 +629,8 @@ following: · 628 unmodified lines | ||
| 629 | 629 | - tracing synthetic events from in-kernel code |
| 630 | 630 | - the low-level "dynevent_cmd" API |
| 631 | 631 | |
| 632 | −7.1 Dyamically creating synthetic event definitions | |
| 633 | −--------------------------------------------------- | |
| 632 | +7.1 Dynamically creating synthetic event definitions | |
| 633 | +---------------------------------------------------- | |
| 634 | 634 | |
| 635 | 635 | There are a couple ways to create a new synthetic event from a kernel |
| 636 | 636 | module or other kernel code. |
| @@ -944,8 +944,8 @@ Note that synth_event_trace_end() must be called at the end regardless · 307 unmodified lines | ||
| 944 | 944 | of whether any of the add calls failed (say due to a bad field name |
| 945 | 945 | being passed in). |
| 946 | 946 | |
| 947 | −7.3 Dyamically creating kprobe and kretprobe event definitions | |
| 948 | −-------------------------------------------------------------- | |
| 947 | +7.3 Dynamically creating kprobe and kretprobe event definitions | |
| 948 | +--------------------------------------------------------------- | |
| 949 | 949 | |
| 950 | 950 | To create a kprobe or kretprobe trace event from kernel code, the |
| 951 | 951 | kprobe_event_gen_cmd_start() or kretprobe_event_gen_cmd_start() |
| @@ -81,7 +81,7 @@ Same as ftrace, the registered callbacks will start being called some time · 80 unmodified lines | ||
| 81 | 81 | after the register_fprobe() is called and before it returns. See |
| 82 | 82 | :file:`Documentation/trace/ftrace.rst`. |
| 83 | 83 | |
| 84 | −Also, the unregister_fprobe() will guarantee that the both enter and exit | |
| 84 | +Also, the unregister_fprobe() will guarantee that both enter and exit | |
| 85 | 85 | handlers are no longer being called by functions after unregister_fprobe() |
| 86 | 86 | returns as same as unregister_ftrace_function(). |
| 87 | 87 | |
| @@ -193,7 +193,7 @@ FTRACE_OPS_FL_RECURSION · 192 unmodified lines | ||
| 193 | 193 | Note, if this flag is set, then the callback will always be called |
| 194 | 194 | with preemption disabled. If it is not set, then it is possible |
| 195 | 195 | (but not guaranteed) that the callback will be called in |
| 196 | − preemptable context. | |
| 196 | + preemptible context. | |
| 197 | 197 | |
| 198 | 198 | FTRACE_OPS_FL_IPMODIFY |
| 199 | 199 | Requires FTRACE_OPS_FL_SAVE_REGS set. If the callback is to "hijack" |
| @@ -30,7 +30,7 @@ disabled and enabled, as well as for preemption and from a time · 29 unmodified lines | ||
| 30 | 30 | a task is woken to the task is actually scheduled in. |
| 31 | 31 | |
| 32 | 32 | One of the most common uses of ftrace is the event tracing. |
| 33 | −Throughout the kernel is hundreds of static event points that | |
| 33 | +Throughout the kernel are hundreds of static event points that | |
| 34 | 34 | can be enabled via the tracefs file system to see what is |
| 35 | 35 | going on in certain parts of the kernel. |
| 36 | 36 | |
| @@ -383,7 +383,7 @@ of ftrace. Here is a list of some of the key files: · 346 unmodified lines | ||
| 383 | 383 | not be listed in this count. |
| 384 | 384 | |
| 385 | 385 | If the callback registered to be traced by a function with |
| 386 | − the "save regs" attribute (thus even more overhead), a 'R' | |
| 386 | + the "save regs" attribute (thus even more overhead), an 'R' | |
| 387 | 387 | will be displayed on the same line as the function that |
| 388 | 388 | is returning registers. |
| 389 | 389 | |
| @@ -392,7 +392,7 @@ of ftrace. Here is a list of some of the key files: · 2 unmodified lines | ||
| 392 | 392 | an 'I' will be displayed on the same line as the function that |
| 393 | 393 | can be overridden. |
| 394 | 394 | |
| 395 | − If a non ftrace trampoline is attached (BPF) a 'D' will be displayed. | |
| 395 | + If a non-ftrace trampoline is attached (BPF) a 'D' will be displayed. | |
| 396 | 396 | Note, normal ftrace trampolines can also be attached, but only one |
| 397 | 397 | "direct" trampoline can be attached to a given function at a time. |
| 398 | 398 | |
| @@ -402,7 +402,7 @@ of ftrace. Here is a list of some of the key files: · 3 unmodified lines | ||
| 402 | 402 | |
| 403 | 403 | If a function had either the "ip modify" or a "direct" call attached to |
| 404 | 404 | it in the past, a 'M' will be shown. This flag is never cleared. It is |
| 405 | − used to know if a function was every modified by the ftrace infrastructure, | |
| 405 | + used to know if a function was ever modified by the ftrace infrastructure, | |
| 406 | 406 | and can be used for debugging. |
| 407 | 407 | |
| 408 | 408 | If the architecture supports it, it will also show what callback |
| @@ -418,7 +418,7 @@ of ftrace. Here is a list of some of the key files: · 9 unmodified lines | ||
| 418 | 418 | |
| 419 | 419 | This file contains all the functions that ever had a function callback |
| 420 | 420 | to it via the ftrace infrastructure. It has the same format as |
| 421 | − enabled_functions but shows all functions that have every been | |
| 421 | + enabled_functions but shows all functions that have ever been | |
| 422 | 422 | traced. |
| 423 | 423 | |
| 424 | 424 | To see any function that has every been modified by "ip modify" or a |
| @@ -517,7 +517,7 @@ of ftrace. Here is a list of some of the key files: · 92 unmodified lines | ||
| 517 | 517 | Whenever an event is recorded into the ring buffer, a |
| 518 | 518 | "timestamp" is added. This stamp comes from a specified |
| 519 | 519 | clock. By default, ftrace uses the "local" clock. This |
| 520 | − clock is very fast and strictly per cpu, but on some | |
| 520 | + clock is very fast and strictly per CPU, but on some | |
| 521 | 521 | systems it may not be monotonic with respect to other |
| 522 | 522 | CPUs. In other words, the local clocks may not be in sync |
| 523 | 523 | with local clocks on other CPUs. |
| @@ -868,7 +868,7 @@ Here is the list of current tracers that may be configured. · 344 unmodified lines | ||
| 868 | 868 | |
| 869 | 869 | "mmiotrace" |
| 870 | 870 | |
| 871 | − A special tracer that is used to trace binary module. | |
| 871 | + A special tracer that is used to trace binary modules. | |
| 872 | 872 | It will trace all the calls that a module makes to the |
| 873 | 873 | hardware. Everything it writes and reads from the I/O |
| 874 | 874 | as well. |
| @@ -11,13 +11,14 @@ histograms work and how the individual pieces map to the data · 10 unmodified lines | ||
| 11 | 11 | structures used to implement them in trace_events_hist.c and |
| 12 | 12 | tracing_map.c. |
| 13 | 13 | |
| 14 | −Note: All the ftrace histogram command examples assume the working | |
| 15 | − directory is the ftrace /tracing directory. For example:: | |
| 14 | +.. note:: | |
| 15 | + All the ftrace histogram command examples assume the working | |
| 16 | + directory is the ftrace /tracing directory. For example:: | |
| 16 | 17 | |
| 17 | 18 | # cd /sys/kernel/tracing |
| 18 | 19 | |
| 19 | −Also, the histogram output displayed for those commands will be | |
| 20 | −generally be truncated - only enough to make the point is displayed. | |
| 20 | + Also, the histogram output displayed for those commands will be | |
| 21 | + generally be truncated - only enough to make the point is displayed. | |
| 21 | 22 | |
| 22 | 23 | 'hist_debug' trace event files |
| 23 | 24 | ============================== |
| @@ -142,30 +143,30 @@ elements for a couple hypothetical keys and values.:: · 118 unmodified lines | ||
| 142 | 143 | +--------------+ | | |
| 143 | 144 | n_keys = n_fields - n_vals | | |
| 144 | 145 | |
| 145 | −The hist_data n_vals and n_fields delineate the extent of the fields[] | | | |
| 146 | −array and separate keys from values for the rest of the code. | | | |
| 147 | − | |
| 148 | −Below is a run-time representation of the tracing_map part of the | | | |
| 149 | −histogram, with pointers from various parts of the fields[] array | | | |
| 150 | −to corresponding parts of the tracing_map. | | | |
| 151 | − | |
| 152 | −The tracing_map consists of an array of tracing_map_entrys and a set | | | |
| 153 | −of preallocated tracing_map_elts (abbreviated below as map_entry and | | | |
| 154 | −map_elt). The total number of map_entrys in the hist_data.map array = | | | |
| 155 | −map->max_elts (actually map->map_size but only max_elts of those are | | | |
| 156 | −used. This is a property required by the map_insert() algorithm). | | | |
| 157 | − | |
| 158 | −If a map_entry is unused, meaning no key has yet hashed into it, its | | | |
| 159 | −.key value is 0 and its .val pointer is NULL. Once a map_entry has | | | |
| 160 | −been claimed, the .key value contains the key's hash value and the | | | |
| 161 | −.val member points to a map_elt containing the full key and an entry | | | |
| 162 | −for each key or value in the map_elt.fields[] array. There is an | | | |
| 163 | −entry in the map_elt.fields[] array corresponding to each hist_field | | | |
| 164 | −in the histogram, and this is where the continually aggregated sums | | | |
| 165 | −corresponding to each histogram value are kept. | | | |
| 166 | − | |
| 167 | −The diagram attempts to show the relationship between the | | | |
| 168 | −hist_data.fields[] and the map_elt.fields[] with the links drawn | | | |
| 146 | +The hist_data n_vals and n_fields delineate the extent of the fields[] | |
| 147 | +array and separate keys from values for the rest of the code. | |
| 148 | + | |
| 149 | +Below is a run-time representation of the tracing_map part of the | |
| 150 | +histogram, with pointers from various parts of the fields[] array | |
| 151 | +to corresponding parts of the tracing_map. | |
| 152 | + | |
| 153 | +The tracing_map consists of an array of tracing_map_entrys and a set | |
| 154 | +of preallocated tracing_map_elts (abbreviated below as map_entry and | |
| 155 | +map_elt). The total number of map_entrys in the hist_data.map array = | |
| 156 | +map->max_elts (actually map->map_size but only max_elts of those are | |
| 157 | +used. This is a property required by the map_insert() algorithm). | |
| 158 | + | |
| 159 | +If a map_entry is unused, meaning no key has yet hashed into it, its | |
| 160 | +.key value is 0 and its .val pointer is NULL. Once a map_entry has | |
| 161 | +been claimed, the .key value contains the key's hash value and the | |
| 162 | +.val member points to a map_elt containing the full key and an entry | |
| 163 | +for each key or value in the map_elt.fields[] array. There is an | |
| 164 | +entry in the map_elt.fields[] array corresponding to each hist_field | |
| 165 | +in the histogram, and this is where the continually aggregated sums | |
| 166 | +corresponding to each histogram value are kept. | |
| 167 | + | |
| 168 | +The diagram attempts to show the relationship between the | |
| 169 | +hist_data.fields[] and the map_elt.fields[] with the links drawn | |
| 169 | 170 | between diagrams:: |
| 170 | 171 | |
| 171 | 172 | +-----------+ | | |
| @@ -380,7 +381,9 @@ entry, ts0, corresponding to the ts0 variable in the sched_waking · 208 unmodified lines | ||
| 380 | 381 | trigger above. |
| 381 | 382 | |
| 382 | 383 | sched_waking histogram |
| 383 | −----------------------:: | |
| 384 | +---------------------- | |
| 385 | + | |
| 386 | +.. code-block:: | |
| 384 | 387 | |
| 385 | 388 | +------------------+ |
| 386 | 389 | | hist_data |<-------------------------------------------------------+ |
| @@ -440,31 +443,31 @@ sched_waking histogram · 53 unmodified lines | ||
| 440 | 443 | n_keys = n_fields - n_vals | | | |
| 441 | 444 | | | | |
| 442 | 445 | |
| 443 | −This is very similar to the basic case. In the above diagram, we can | | | | |
| 444 | −see a new .flags member has been added to the struct hist_field | | | | |
| 445 | −struct, and a new entry added to hist_data.fields representing the ts0 | | | | |
| 446 | −variable. For a normal val hist_field, .flags is just 0 (modulo | | | | |
| 447 | −modifier flags), but if the value is defined as a variable, the .flags | | | | |
| 448 | −contains a set FL_VAR bit. | | | | |
| 449 | − | |
| 450 | −As you can see, the ts0 entry's .var.idx member contains the index | | | | |
| 451 | −into the tracing_map_elts' .vars[] array containing variable values. | | | | |
| 452 | −This idx is used whenever the value of the variable is set or read. | | | | |
| 453 | −The map_elt.vars idx assigned to the given variable is assigned and | | | | |
| 454 | −saved in .var.idx by create_tracing_map_fields() after it calls | | | | |
| 455 | −tracing_map_add_var(). | | | | |
| 456 | − | |
| 457 | −Below is a representation of the histogram at run-time, which | | | | |
| 458 | −populates the map, along with correspondence to the above hist_data and | | | | |
| 459 | −hist_field data structures. | | | | |
| 460 | − | |
| 461 | −The diagram attempts to show the relationship between the | | | | |
| 462 | −hist_data.fields[] and the map_elt.fields[] and map_elt.vars[] with | | | | |
| 463 | −the links drawn between diagrams. For each of the map_elts, you can | | | | |
| 464 | −see that the .fields[] members point to the .sum or .offset of a key | | | | |
| 465 | −or val and the .vars[] members point to the value of a variable. The | | | | |
| 466 | −arrows between the two diagrams show the linkages between those | | | | |
| 467 | −tracing_map members and the field definitions in the corresponding | | | | |
| 446 | +This is very similar to the basic case. In the above diagram, we can | |
| 447 | +see a new .flags member has been added to the struct hist_field | |
| 448 | +struct, and a new entry added to hist_data.fields representing the ts0 | |
| 449 | +variable. For a normal val hist_field, .flags is just 0 (modulo | |
| 450 | +modifier flags), but if the value is defined as a variable, the .flags | |
| 451 | +contains a set FL_VAR bit. | |
| 452 | + | |
| 453 | +As you can see, the ts0 entry's .var.idx member contains the index | |
| 454 | +into the tracing_map_elts' .vars[] array containing variable values. | |
| 455 | +This idx is used whenever the value of the variable is set or read. | |
| 456 | +The map_elt.vars idx assigned to the given variable is assigned and | |
| 457 | +saved in .var.idx by create_tracing_map_fields() after it calls | |
| 458 | +tracing_map_add_var(). | |
| 459 | + | |
| 460 | +Below is a representation of the histogram at run-time, which | |
| 461 | +populates the map, along with correspondence to the above hist_data and | |
| 462 | +hist_field data structures. | |
| 463 | + | |
| 464 | +The diagram attempts to show the relationship between the | |
| 465 | +hist_data.fields[] and the map_elt.fields[] and map_elt.vars[] with | |
| 466 | +the links drawn between diagrams. For each of the map_elts, you can | |
| 467 | +see that the .fields[] members point to the .sum or .offset of a key | |
| 468 | +or val and the .vars[] members point to the value of a variable. The | |
| 469 | +arrows between the two diagrams show the linkages between those | |
| 470 | +tracing_map members and the field definitions in the corresponding | |
| 468 | 471 | hist_data fields[] members.:: |
| 469 | 472 | |
| 470 | 473 | +-----------+ | | | |
| @@ -565,40 +568,40 @@ hist_data fields[] members.:: · 94 unmodified lines | ||
| 565 | 568 | | | | | |
| 566 | 569 | +---------------+ | | |
| 567 | 570 | |
| 568 | −For each used map entry, there's a map_elt pointing to an array of | | | |
| 569 | −.vars containing the current value of the variables associated with | | | |
| 570 | −that histogram entry. So in the above, the timestamp associated with | | | |
| 571 | −pid 999 is 113345679876, and the timestamp variable in the same | | | |
| 572 | −.var.idx for pid 4444 is 213499240729. | | | |
| 573 | − | |
| 574 | −sched_switch histogram | | | |
| 575 | −---------------------- | | | |
| 576 | − | |
| 577 | −The sched_switch histogram paired with the above sched_waking | | | |
| 578 | −histogram is shown below. The most important aspect of the | | | |
| 579 | −sched_switch histogram is that it references a variable on the | | | |
| 580 | −sched_waking histogram above. | | | |
| 581 | − | |
| 582 | −The histogram diagram is very similar to the others so far displayed, | | | |
| 583 | −but it adds variable references. You can see the normal hitcount and | | | |
| 584 | −key fields along with a new wakeup_lat variable implemented in the | | | |
| 585 | −same way as the sched_waking ts0 variable, but in addition there's an | | | |
| 586 | −entry with the new FL_VAR_REF (short for HIST_FIELD_FL_VAR_REF) flag. | | | |
| 587 | − | |
| 588 | −Associated with the new var ref field are a couple of new hist_field | | | |
| 589 | −members, var.hist_data and var_ref_idx. For a variable reference, the | | | |
| 590 | −var.hist_data goes with the var.idx, which together uniquely identify | | | |
| 591 | −a particular variable on a particular histogram. The var_ref_idx is | | | |
| 592 | −just the index into the var_ref_vals[] array that caches the values of | | | |
| 593 | −each variable whenever a hist trigger is updated. Those resulting | | | |
| 594 | −values are then finally accessed by other code such as trace action | | | |
| 595 | −code that uses the var_ref_idx values to assign param values. | | | |
| 596 | − | |
| 597 | −The diagram below describes the situation for the sched_switch | | | |
| 571 | +For each used map entry, there's a map_elt pointing to an array of | |
| 572 | +.vars containing the current value of the variables associated with | |
| 573 | +that histogram entry. So in the above, the timestamp associated with | |
| 574 | +pid 999 is 113345679876, and the timestamp variable in the same | |
| 575 | +.var.idx for pid 4444 is 213499240729. | |
| 576 | + | |
| 577 | +sched_switch histogram | |
| 578 | +---------------------- | |
| 579 | + | |
| 580 | +The sched_switch histogram paired with the above sched_waking | |
| 581 | +histogram is shown below. The most important aspect of the | |
| 582 | +sched_switch histogram is that it references a variable on the | |
| 583 | +sched_waking histogram above. | |
| 584 | + | |
| 585 | +The histogram diagram is very similar to the others so far displayed, | |
| 586 | +but it adds variable references. You can see the normal hitcount and | |
| 587 | +key fields along with a new wakeup_lat variable implemented in the | |
| 588 | +same way as the sched_waking ts0 variable, but in addition there's an | |
| 589 | +entry with the new FL_VAR_REF (short for HIST_FIELD_FL_VAR_REF) flag. | |
| 590 | + | |
| 591 | +Associated with the new var ref field are a couple of new hist_field | |
| 592 | +members, var.hist_data and var_ref_idx. For a variable reference, the | |
| 593 | +var.hist_data goes with the var.idx, which together uniquely identify | |
| 594 | +a particular variable on a particular histogram. The var_ref_idx is | |
| 595 | +just the index into the var_ref_vals[] array that caches the values of | |
| 596 | +each variable whenever a hist trigger is updated. Those resulting | |
| 597 | +values are then finally accessed by other code such as trace action | |
| 598 | +code that uses the var_ref_idx values to assign param values. | |
| 599 | + | |
| 600 | +The diagram below describes the situation for the sched_switch | |
| 598 | 601 | histogram referred to before:: |
| 599 | 602 | |
| 600 | − # echo 'hist:keys=next_pid:wakeup_lat=common_timestamp.usecs-$ts0' >> | | | |
| 601 | − events/sched/sched_switch/trigger | | | |
| 603 | + # echo 'hist:keys=next_pid:wakeup_lat=common_timestamp.usecs-$ts0' >> | |
| 604 | + events/sched/sched_switch/trigger | |
| 602 | 605 | | | |
| 603 | 606 | +------------------+ | | |
| 604 | 607 | | hist_data | | | |
| @@ -186,8 +186,8 @@ Documentation written by Tom Zanussi · 185 unmodified lines | ||
| 186 | 186 | The examples below provide a more concrete illustration of the |
| 187 | 187 | concepts and typical usage patterns discussed above. |
| 188 | 188 | |
| 189 | −'special' event fields | |
| 190 | −------------------------ | |
| 189 | +2.1. 'special' event fields | |
| 190 | +--------------------------- | |
| 191 | 191 | |
| 192 | 192 | There are a number of 'special event fields' available for use as |
| 193 | 193 | keys or values in a hist trigger. These look like and behave as if |
| @@ -204,16 +204,16 @@ Documentation written by Tom Zanussi · 10 unmodified lines | ||
| 204 | 204 | common_cpu int the cpu on which the event occurred. |
| 205 | 205 | ====================== ==== ======================================= |
| 206 | 206 | |
| 207 | −Extended error information | |
| 208 | −-------------------------- | |
| 207 | +2.2. Extended error information | |
| 208 | +------------------------------- | |
| 209 | 209 | |
| 210 | 210 | For some error conditions encountered when invoking a hist trigger |
| 211 | 211 | command, extended error information is available via the |
| 212 | − tracing/error_log file. See Error Conditions in | |
| 213 | − :file:`Documentation/trace/ftrace.rst` for details. | |
| 212 | + tracing/error_log file. See "Error conditions" section in | |
| 213 | + Documentation/trace/ftrace.rst for details. | |
| 214 | 214 | |
| 215 | −6.2 'hist' trigger examples | |
| 216 | −--------------------------- | |
| 215 | +2.3. 'hist' trigger examples | |
| 216 | +---------------------------- | |
| 217 | 217 | |
| 218 | 218 | The first set of examples creates aggregations using the kmalloc |
| 219 | 219 | event. The fields that can be used for the hist trigger are listed |
| @@ -840,7 +840,7 @@ Extended error information · 620 unmodified lines | ||
| 840 | 840 | |
| 841 | 841 | The compound key examples used a key and a sum value (hitcount) to |
| 842 | 842 | sort the output, but we can just as easily use two keys instead. |
| 843 | − Here's an example where we use a compound key composed of the the | |
| 843 | + Here's an example where we use a compound key composed of the | |
| 844 | 844 | common_pid and size event fields. Sorting with pid as the primary |
| 845 | 845 | key and 'size' as the secondary key allows us to display an |
| 846 | 846 | ordered summary of the recvfrom sizes, with counts, received by |
| @@ -1608,8 +1608,8 @@ Extended error information · 761 unmodified lines | ||
| 1608 | 1608 | Entries: 7 |
| 1609 | 1609 | Dropped: 0 |
| 1610 | 1610 | |
| 1611 | −2.2 Inter-event hist triggers | |
| 1612 | −----------------------------- | |
| 1611 | +2.4. Inter-event hist triggers | |
| 1612 | +------------------------------ | |
| 1613 | 1613 | |
| 1614 | 1614 | Inter-event hist triggers are hist triggers that combine values from |
| 1615 | 1615 | one or more other events and create a histogram using that data. Data |
| @@ -1685,8 +1685,8 @@ pseudo-file. · 69 unmodified lines | ||
| 1685 | 1685 | |
| 1686 | 1686 | These features are described in more detail in the following sections. |
| 1687 | 1687 | |
| 1688 | −2.2.1 Histogram Variables | |
| 1689 | −------------------------- | |
| 1688 | +2.5. Histogram Variables | |
| 1689 | +------------------------ | |
| 1690 | 1690 | |
| 1691 | 1691 | Variables are simply named locations used for saving and retrieving |
| 1692 | 1692 | values between matching events. A 'matching' event is defined as an |
| @@ -1789,8 +1789,8 @@ or assigned to a variable and referenced in a subsequent expression:: · 96 unmodified lines | ||
| 1789 | 1789 | |
| 1790 | 1790 | Variables can even hold stacktraces, which are useful with synthetic events. |
| 1791 | 1791 | |
| 1792 | −2.2.2 Synthetic Events | |
| 1793 | −---------------------- | |
| 1792 | +2.6. Synthetic Events | |
| 1793 | +--------------------- | |
| 1794 | 1794 | |
| 1795 | 1795 | Synthetic events are user-defined events generated from hist trigger |
| 1796 | 1796 | variables or fields associated with one or more other events. Their |
| @@ -1846,7 +1846,7 @@ the command that defined it with a '!':: · 49 unmodified lines | ||
| 1846 | 1846 | At this point, there isn't yet an actual 'wakeup_latency' event |
| 1847 | 1847 | instantiated in the event subsystem - for this to happen, a 'hist |
| 1848 | 1848 | trigger action' needs to be instantiated and bound to actual fields |
| 1849 | −and variables defined on other events (see Section 2.2.3 below on | |
| 1849 | +and variables defined on other events (see Section 2.7. below on | |
| 1850 | 1850 | how that is done using hist trigger 'onmatch' action). Once that is |
| 1851 | 1851 | done, the 'wakeup_latency' synthetic event instance is created. |
| 1852 | 1852 | |
| @@ -2094,8 +2094,8 @@ histogram:: · 241 unmodified lines | ||
| 2094 | 2094 | Entries: 7 |
| 2095 | 2095 | Dropped: 0 |
| 2096 | 2096 | |
| 2097 | −2.2.3 Hist trigger 'handlers' and 'actions' | |
| 2098 | −------------------------------------------- | |
| 2097 | +2.7. Hist trigger 'handlers' and 'actions' | |
| 2098 | +------------------------------------------ | |
| 2099 | 2099 | |
| 2100 | 2100 | A hist trigger 'action' is a function that's executed (in most cases |
| 2101 | 2101 | conditionally) whenever a histogram entry is added or updated. |
| @@ -2526,8 +2526,8 @@ The following commonly-used handler.action pairs are available: · 424 unmodified lines | ||
| 2526 | 2526 | kworker/3:2-135 [003] d..3 49.823123: sched_switch: prev_comm=kworker/3:2 prev_pid=135 prev_prio=120 prev_state=T ==> next_comm=swapper/3 next_pid=0 next_prio=120 |
| 2527 | 2527 | <idle>-0 [004] ..s7 49.823798: tcp_probe: src=10.0.0.10:54326 dest=23.215.104.193:80 mark=0x0 length=32 snd_nxt=0xe3ae2ff5 snd_una=0xe3ae2ecd snd_cwnd=10 ssthresh=2147483647 snd_wnd=28960 srtt=19604 rcv_wnd=29312 |
| 2528 | 2528 | |
| 2529 | −3. User space creating a trigger | |
| 2530 | −-------------------------------- | |
| 2529 | +2.8. User space creating a trigger | |
| 2530 | +---------------------------------- | |
| 2531 | 2531 | |
| 2532 | 2532 | Writing into /sys/kernel/tracing/trace_marker writes into the ftrace |
| 2533 | 2533 | ring buffer. This can also act like an event, by writing into the trigger |
| @@ -181,7 +181,7 @@ which is the list of atomic propositions present in the LTL specification · 180 unmodified lines | ||
| 181 | 181 | functions interacting with the Buchi automaton. |
| 182 | 182 | |
| 183 | 183 | While generating code, `rvgen` cannot understand the meaning of the atomic |
| 184 | −propositions. Thus, that task is left for manual work. The recommended pratice | |
| 184 | +propositions. Thus, that task is left for manual work. The recommended practice | |
| 185 | 185 | is adding tracepoints to places where the atomic propositions change; and in the |
| 186 | 186 | tracepoints' handlers: the Buchi automaton is executed using:: |
| 187 | 187 | |
| @@ -46,7 +46,6 @@ util-linux 2.10o mount --version · 45 unmodified lines | ||
| 46 | 46 | kmod 13 depmod -V |
| 47 | 47 | e2fsprogs 1.41.4 e2fsck -V |
| 48 | 48 | jfsutils 1.1.3 fsck.jfs -V |
| 49 | −reiserfsprogs 3.6.3 reiserfsck -V | |
| 50 | 49 | xfsprogs 2.6.0 xfs_db -V |
| 51 | 50 | squashfs-tools 4.0 mksquashfs -version |
| 52 | 51 | btrfs-progs 0.18 btrfsck |
| @@ -260,14 +259,6 @@ Sono disponibili i seguenti strumenti: · 207 unmodified lines | ||
| 260 | 259 | |
| 261 | 260 | - sono disponibili altri strumenti per il file-system. |
| 262 | 261 | |
| 263 | −Reiserfsprogs | |
| 264 | −------------- | |
| 265 | − | |
| 266 | −Il pacchetto reiserfsprogs dovrebbe essere usato con reiserfs-3.6.x (Linux | |
| 267 | −kernel 2.4.x). Questo è un pacchetto combinato che contiene versioni | |
| 268 | −funzionanti di ``mkreiserfs``, ``resize_reiserfs``, ``debugreiserfs`` e | |
| 269 | −``reiserfsck``. Questi programmi funzionano sulle piattaforme i386 e alpha. | |
| 270 | − | |
| 271 | 262 | Xfsprogs |
| 272 | 263 | -------- |
| 273 | 264 | |
| @@ -479,11 +470,6 @@ JFSutils · 205 unmodified lines | ||
| 479 | 470 | |
| 480 | 471 | - <https://jfs.sourceforge.net/> |
| 481 | 472 | |
| 482 | −Reiserfsprogs | |
| 483 | −------------- | |
| 484 | − | |
| 485 | −- <https://git.kernel.org/pub/scm/linux/kernel/git/jeffm/reiserfsprogs.git/> | |
| 486 | − | |
| 487 | 473 | Xfsprogs |
| 488 | 474 | -------- |
| 489 | 475 | |
| @@ -1,64 +0,0 @@ | ||
| 1 | −# SPDX-License-Identifier: GPL-2.0 | |
| 2 | − | |
| 3 | −# Rules to convert a .h file to inline RST documentation | |
| 4 | − | |
| 5 | −SRC_DIR=$(srctree)/Documentation/userspace-api/media | |
| 6 | −PARSER = $(srctree)/Documentation/sphinx/parse-headers.pl | |
| 7 | −UAPI = $(srctree)/include/uapi/linux | |
| 8 | −KAPI = $(srctree)/include/linux | |
| 9 | − | |
| 10 | −FILES = ca.h.rst dmx.h.rst frontend.h.rst net.h.rst \ | |
| 11 | − videodev2.h.rst media.h.rst cec.h.rst lirc.h.rst | |
| 12 | − | |
| 13 | −TARGETS := $(addprefix $(BUILDDIR)/, $(FILES)) | |
| 14 | − | |
| 15 | −gen_rst = \ | |
| 16 | − echo ${PARSER} $< $@ $(SRC_DIR)/$(notdir $@).exceptions; \ | |
| 17 | − ${PARSER} $< $@ $(SRC_DIR)/$(notdir $@).exceptions | |
| 18 | − | |
| 19 | −quiet_gen_rst = echo ' PARSE $(patsubst $(srctree)/%,%,$<)'; \ | |
| 20 | − ${PARSER} $< $@ $(SRC_DIR)/$(notdir $@).exceptions | |
| 21 | − | |
| 22 | −silent_gen_rst = ${gen_rst} | |
| 23 | − | |
| 24 | −$(BUILDDIR)/ca.h.rst: ${UAPI}/dvb/ca.h ${PARSER} $(SRC_DIR)/ca.h.rst.exceptions | |
| 25 | − @$($(quiet)gen_rst) | |
| 26 | − | |
| 27 | −$(BUILDDIR)/dmx.h.rst: ${UAPI}/dvb/dmx.h ${PARSER} $(SRC_DIR)/dmx.h.rst.exceptions | |
| 28 | − @$($(quiet)gen_rst) | |
| 29 | − | |
| 30 | −$(BUILDDIR)/frontend.h.rst: ${UAPI}/dvb/frontend.h ${PARSER} $(SRC_DIR)/frontend.h.rst.exceptions | |
| 31 | − @$($(quiet)gen_rst) | |
| 32 | − | |
| 33 | −$(BUILDDIR)/net.h.rst: ${UAPI}/dvb/net.h ${PARSER} $(SRC_DIR)/net.h.rst.exceptions | |
| 34 | − @$($(quiet)gen_rst) | |
| 35 | − | |
| 36 | −$(BUILDDIR)/videodev2.h.rst: ${UAPI}/videodev2.h ${PARSER} $(SRC_DIR)/videodev2.h.rst.exceptions | |
| 37 | − @$($(quiet)gen_rst) | |
| 38 | − | |
| 39 | −$(BUILDDIR)/media.h.rst: ${UAPI}/media.h ${PARSER} $(SRC_DIR)/media.h.rst.exceptions | |
| 40 | − @$($(quiet)gen_rst) | |
| 41 | − | |
| 42 | −$(BUILDDIR)/cec.h.rst: ${UAPI}/cec.h ${PARSER} $(SRC_DIR)/cec.h.rst.exceptions | |
| 43 | − @$($(quiet)gen_rst) | |
| 44 | − | |
| 45 | −$(BUILDDIR)/lirc.h.rst: ${UAPI}/lirc.h ${PARSER} $(SRC_DIR)/lirc.h.rst.exceptions | |
| 46 | − @$($(quiet)gen_rst) | |
| 47 | − | |
| 48 | −# Media build rules | |
| 49 | − | |
| 50 | −.PHONY: all html texinfo epub xml latex | |
| 51 | − | |
| 52 | −all: $(IMGDOT) $(BUILDDIR) ${TARGETS} | |
| 53 | −html: all | |
| 54 | −texinfo: all | |
| 55 | −epub: all | |
| 56 | −xml: all | |
| 57 | −latex: $(IMGPDF) all | |
| 58 | −linkcheck: | |
| 59 | − | |
| 60 | −clean: | |
| 61 | − -rm -f $(DOTTGT) $(IMGTGT) ${TARGETS} 2>/dev/null | |
| 62 | − | |
| 63 | −$(BUILDDIR): | |
| 64 | − $(Q)mkdir -p $@ | |
| @@ -6,5 +6,6 @@ · 5 unmodified lines | ||
| 6 | 6 | CEC Header File |
| 7 | 7 | *************** |
| 8 | 8 | |
| 9 | −.. kernel-include:: $BUILDDIR/cec.h.rst | |
| 10 | − | |
| 9 | +.. kernel-include:: include/uapi/linux/cec.h | |
| 10 | + :generate-cross-refs: | |
| 11 | + :exception-file: cec.h.rst.exceptions | |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
| @@ -7,10 +7,19 @@ Digital TV uAPI header files · 6 unmodified lines | ||
| 7 | 7 | Digital TV uAPI headers |
| 8 | 8 | *********************** |
| 9 | 9 | |
| 10 | −.. kernel-include:: $BUILDDIR/frontend.h.rst | |
| 10 | +.. kernel-include:: include/uapi/linux/dvb/frontend.h | |
| 11 | + :generate-cross-refs: | |
| 12 | + :exception-file: frontend.h.rst.exceptions | |
| 11 | 13 | |
| 12 | −.. kernel-include:: $BUILDDIR/dmx.h.rst | |
| 14 | +.. kernel-include:: include/uapi/linux/dvb/dmx.h | |
| 15 | + :generate-cross-refs: | |
| 16 | + :exception-file: dmx.h.rst.exceptions | |
| 13 | 17 | |
| 14 | −.. kernel-include:: $BUILDDIR/ca.h.rst | |
| 18 | +.. kernel-include:: include/uapi/linux/dvb/ca.h | |
| 19 | + :generate-cross-refs: | |
| 20 | + :exception-file: ca.h.rst.exceptions | |
| 21 | + | |
| 22 | +.. kernel-include:: include/uapi/linux/dvb/net.h | |
| 23 | + :generate-cross-refs: | |
| 24 | + :exception-file: net.h.rst.exceptions | |
| 15 | 25 | |
| 16 | −.. kernel-include:: $BUILDDIR/net.h.rst | |
File renamed without changes.
| @@ -6,5 +6,6 @@ · 5 unmodified lines | ||
| 6 | 6 | Media Controller Header File |
| 7 | 7 | **************************** |
| 8 | 8 | |
| 9 | −.. kernel-include:: $BUILDDIR/media.h.rst | |
| 10 | − | |
| 9 | +.. kernel-include:: include/uapi/linux/media.h | |
| 10 | + :generate-cross-refs: | |
| 11 | + :exception-file: media.h.rst.exceptions | |
File renamed without changes.
| @@ -6,5 +6,7 @@ · 5 unmodified lines | ||
| 6 | 6 | LIRC Header File |
| 7 | 7 | **************** |
| 8 | 8 | |
| 9 | −.. kernel-include:: $BUILDDIR/lirc.h.rst | |
| 9 | +.. kernel-include:: include/uapi/linux/lirc.h | |
| 10 | + :generate-cross-refs: | |
| 11 | + :exception-file: lirc.h.rst.exceptions | |
| 10 | 12 | |
File renamed without changes.
| @@ -6,4 +6,6 @@ · 5 unmodified lines | ||
| 6 | 6 | Video For Linux Two Header File |
| 7 | 7 | ******************************* |
| 8 | 8 | |
| 9 | −.. kernel-include:: $BUILDDIR/videodev2.h.rst | |
| 9 | +.. kernel-include:: include/uapi/linux/videodev2.h | |
| 10 | + :generate-cross-refs: | |
| 11 | + :exception-file: videodev2.h.rst.exceptions | |
File renamed without changes.
| @@ -98,7 +98,7 @@ New APIs · 97 unmodified lines | ||
| 98 | 98 | It is important to demonstrate your use case. This can be as simple as |
| 99 | 99 | explaining that the feature is already in use on bare metal, or it can be |
| 100 | 100 | a proof-of-concept implementation in userspace. The latter need not be |
| 101 | − open source, though that is of course preferrable for easier testing. | |
| 101 | + open source, though that is of course preferable for easier testing. | |
| 102 | 102 | Selftests should test corner cases of the APIs, and should also cover |
| 103 | 103 | basic host and guest operation if no open source VMM uses the feature. |
| 104 | 104 | |
| @@ -22,7 +22,7 @@ Description · 21 unmodified lines | ||
| 22 | 22 | ----------- |
| 23 | 23 | |
| 24 | 24 | The Maxim/Dallas Semiconductor DS2482 is a I2C device that provides |
| 25 | −one (DS2482-100) or eight (DS2482-800) 1-wire busses. | |
| 25 | +one (DS2482-100) or eight (DS2482-800) 1-wire buses. | |
| 26 | 26 | |
| 27 | 27 | |
| 28 | 28 | General Remarks |
| @@ -1,4 +1,4 @@ | ||
| 1 | −. SPDX-License-Identifier: GPL-2.0 | |
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | 2 | |
| 3 | 3 | ===================== |
| 4 | 4 | 1-wire Master Drivers |
| @@ -1,4 +1,4 @@ | ||
| 1 | −. SPDX-License-Identifier: GPL-2.0 | |
| 1 | +.. SPDX-License-Identifier: GPL-2.0 | |
| 2 | 2 | |
| 3 | 3 | ==================== |
| 4 | 4 | 1-wire Slave Drivers |
| @@ -7384,11 +7384,13 @@ P: Documentation/doc-guide/maintainer-profile.rst · 7383 unmodified lines | ||
| 7384 | 7384 | T: git git://git.lwn.net/linux.git docs-next |
| 7385 | 7385 | F: Documentation/ |
| 7386 | 7386 | F: scripts/check-variable-fonts.sh |
| 7387 | +F: scripts/checktransupdate.py | |
| 7387 | 7388 | F: scripts/documentation-file-ref-check |
| 7388 | 7389 | F: scripts/get_abi.py |
| 7389 | 7390 | F: scripts/kernel-doc* |
| 7390 | 7391 | F: scripts/lib/abi/* |
| 7391 | 7392 | F: scripts/lib/kdoc/* |
| 7393 | +F: tools/docs/* | |
| 7392 | 7394 | F: tools/net/ynl/pyynl/lib/doc_generator.py |
| 7393 | 7395 | F: scripts/sphinx-pre-install |
| 7394 | 7396 | X: Documentation/ABI/ |
| @@ -14323,6 +14325,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux.git rcu/dev · 6928 unmodified lines | ||
| 14323 | 14325 | F: Documentation/atomic_bitops.txt |
| 14324 | 14326 | F: Documentation/atomic_t.txt |
| 14325 | 14327 | F: Documentation/core-api/refcount-vs-atomic.rst |
| 14328 | +F: Documentation/dev-tools/lkmm/ | |
| 14326 | 14329 | F: Documentation/litmus-tests/ |
| 14327 | 14330 | F: Documentation/memory-barriers.txt |
| 14328 | 14331 | F: tools/memory-model/ |
| @@ -1797,8 +1797,9 @@ $(help-board-dirs): help-%: · 1796 unmodified lines | ||
| 1797 | 1797 | |
| 1798 | 1798 | # Documentation targets |
| 1799 | 1799 | # --------------------------------------------------------------------------- |
| 1800 | −DOC_TARGETS := xmldocs latexdocs pdfdocs htmldocs epubdocs cleandocs \ | |
| 1801 | − linkcheckdocs dochelp refcheckdocs texinfodocs infodocs | |
| 1800 | +DOC_TARGETS := xmldocs latexdocs pdfdocs htmldocs htmldocs-redirects \ | |
| 1801 | + epubdocs cleandocs linkcheckdocs dochelp refcheckdocs \ | |
| 1802 | + texinfodocs infodocs | |
| 1802 | 1803 | PHONY += $(DOC_TARGETS) |
| 1803 | 1804 | $(DOC_TARGETS): |
| 1804 | 1805 | $(Q)$(MAKE) $(build)=Documentation $@ |
| @@ -5829,7 +5829,7 @@ struct btrfs_dir_list { · 5828 unmodified lines | ||
| 5829 | 5829 | * See process_dir_items_leaf() for details about why it is needed. |
| 5830 | 5830 | * This is a recursive operation - if an existing dentry corresponds to a |
| 5831 | 5831 | * directory, that directory's new entries are logged too (same behaviour as |
| 5832 | − * ext3/4, xfs, f2fs, reiserfs, nilfs2). Note that when logging the inodes | |
| 5832 | + * ext3/4, xfs, f2fs, nilfs2). Note that when logging the inodes | |
| 5833 | 5833 | * the dentries point to we do not acquire their VFS lock, otherwise lockdep |
| 5834 | 5834 | * complains about the following circular lock dependency / possible deadlock: |
| 5835 | 5835 | * |
| @@ -2,8 +2,17 @@ · 1 unmodified line | ||
| 2 | 2 | # SPDX-License-Identifier: GPL-2.0 |
| 3 | 3 | # Copyright(c) 2025: Mauro Carvalho Chehab <mchehab@kernel.org>. |
| 4 | 4 | # |
| 5 | −# pylint: disable=C0103,R0915 | |
| 6 | −# | |
| 5 | +# pylint: disable=C0103,R0912,R0914,R0915 | |
| 6 | + | |
| 7 | +# NOTE: While kernel-doc requires at least version 3.6 to run, the | |
| 8 | +# command line should work with Python 3.2+ (tested with 3.4). | |
| 9 | +# The rationale is that it shall fail gracefully during Kernel | |
| 10 | +# compilation with older Kernel versions. Due to that: | |
| 11 | +# - encoding line is needed here; | |
| 12 | +# - no f-strings can be used on this file. | |
| 13 | +# - the libraries that require newer versions can only be included | |
| 14 | +# after Python version is checked. | |
| 15 | + | |
| 7 | 16 | # Converted from the kernel-doc script originally written in Perl |
| 8 | 17 | # under GPLv2, copyrighted since 1998 by the following authors: |
| 9 | 18 | # |
| @@ -107,9 +116,6 @@ SRC_DIR = os.path.dirname(os.path.realpath(__file__)) · 97 unmodified lines | ||
| 107 | 116 | |
| 108 | 117 | sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) |
| 109 | 118 | |
| 110 | −from kdoc_files import KernelFiles # pylint: disable=C0413 | |
| 111 | −from kdoc_output import RestFormat, ManFormat # pylint: disable=C0413 | |
| 112 | − | |
| 113 | 119 | DESC = """ |
| 114 | 120 | Read C language source or header FILEs, extract embedded documentation comments, |
| 115 | 121 | and print formatted documentation to standard output. |
| @@ -273,14 +279,22 @@ def main(): · 157 unmodified lines | ||
| 273 | 279 | |
| 274 | 280 | python_ver = sys.version_info[:2] |
| 275 | 281 | if python_ver < (3,6): |
| 276 | − logger.warning("Python 3.6 or later is required by kernel-doc") | |
| 282 | + # Depending on Kernel configuration, kernel-doc --none is called at | |
| 283 | + # build time. As we don't want to break compilation due to the | |
| 284 | + # usage of an old Python version, return 0 here. | |
| 285 | + if args.none: | |
| 286 | + logger.error("Python 3.6 or later is required by kernel-doc. skipping checks") | |
| 287 | + sys.exit(0) | |
| 277 | 288 | |
| 278 | − # Return 0 here to avoid breaking compilation | |
| 279 | − sys.exit(0) | |
| 289 | + sys.exit("Python 3.6 or later is required by kernel-doc. Aborting.") | |
| 280 | 290 | |
| 281 | 291 | if python_ver < (3,7): |
| 282 | 292 | logger.warning("Python 3.7 or later is required for correct results") |
| 283 | 293 | |
| 294 | + # Import kernel-doc libraries only after checking Python version | |
| 295 | + from kdoc_files import KernelFiles # pylint: disable=C0415 | |
| 296 | + from kdoc_output import RestFormat, ManFormat # pylint: disable=C0415 | |
| 297 | + | |
| 284 | 298 | if args.man: |
| 285 | 299 | out_style = ManFormat(modulename=args.modulename) |
| 286 | 300 | elif args.none: |
| @@ -308,11 +322,11 @@ def main(): · 21 unmodified lines | ||
| 308 | 322 | sys.exit(0) |
| 309 | 323 | |
| 310 | 324 | if args.werror: |
| 311 | − print(f"{error_count} warnings as errors") | |
| 325 | + print("%s warnings as errors" % error_count) # pylint: disable=C0209 | |
| 312 | 326 | sys.exit(error_count) |
| 313 | 327 | |
| 314 | 328 | if args.verbose: |
| 315 | − print(f"{error_count} errors") | |
| 329 | + print("%s errors" % error_count) # pylint: disable=C0209 | |
| 316 | 330 | |
| 317 | 331 | if args.none: |
| 318 | 332 | sys.exit(0) |
| @@ -46,7 +46,7 @@ doc_decl = doc_com + KernRe(r'(\w+)', cache=False) · 45 unmodified lines | ||
| 46 | 46 | known_section_names = 'description|context|returns?|notes?|examples?' |
| 47 | 47 | known_sections = KernRe(known_section_names, flags = re.I) |
| 48 | 48 | doc_sect = doc_com + \ |
| 49 | − KernRe(r'\s*(\@[.\w]+|\@\.\.\.|' + known_section_names + r')\s*:([^:].*)?$', | |
| 49 | + KernRe(r'\s*(@[.\w]+|@\.\.\.|' + known_section_names + r')\s*:([^:].*)?$', | |
| 50 | 50 | flags=re.I, cache=False) |
| 51 | 51 | |
| 52 | 52 | doc_content = doc_com_body + KernRe(r'(.*)', cache=False) |
| @@ -54,13 +54,11 @@ doc_inline_start = KernRe(r'^\s*/\*\*\s*$', cache=False) · 1 unmodified line | ||
| 54 | 54 | doc_inline_sect = KernRe(r'\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)', cache=False) |
| 55 | 55 | doc_inline_end = KernRe(r'^\s*\*/\s*$', cache=False) |
| 56 | 56 | doc_inline_oneline = KernRe(r'^\s*/\*\*\s*(@[\w\s]+):\s*(.*)\s*\*/\s*$', cache=False) |
| 57 | −attribute = KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", | |
| 58 | − flags=re.I | re.S, cache=False) | |
| 59 | 57 | |
| 60 | 58 | export_symbol = KernRe(r'^\s*EXPORT_SYMBOL(_GPL)?\s*\(\s*(\w+)\s*\)\s*', cache=False) |
| 61 | 59 | export_symbol_ns = KernRe(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+"\)\s*', cache=False) |
| 62 | 60 | |
| 63 | −type_param = KernRe(r"\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False) | |
| 61 | +type_param = KernRe(r"@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False) | |
| 64 | 62 | |
| 65 | 63 | # |
| 66 | 64 | # Tests for the beginning of a kerneldoc block in its various forms. |
| @@ -74,6 +72,133 @@ doc_begin_func = KernRe(str(doc_com) + # initial " * ' · 7 unmodified lines | ||
| 74 | 72 | r'(?:[-:].*)?$', # description (not captured) |
| 75 | 73 | cache = False) |
| 76 | 74 | |
| 75 | +# | |
| 76 | +# Here begins a long set of transformations to turn structure member prefixes | |
| 77 | +# and macro invocations into something we can parse and generate kdoc for. | |
| 78 | +# | |
| 79 | +struct_args_pattern = r'([^,)]+)' | |
| 80 | + | |
| 81 | +struct_xforms = [ | |
| 82 | + # Strip attributes | |
| 83 | + (KernRe(r"__attribute__\s*\(\([a-z0-9,_\*\s\(\)]*\)\)", flags=re.I | re.S, cache=False), ' '), | |
| 84 | + (KernRe(r'\s*__aligned\s*\([^;]*\)', re.S), ' '), | |
| 85 | + (KernRe(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '), | |
| 86 | + (KernRe(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '), | |
| 87 | + (KernRe(r'\s*__packed\s*', re.S), ' '), | |
| 88 | + (KernRe(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '), | |
| 89 | + (KernRe(r'\s*____cacheline_aligned_in_smp', re.S), ' '), | |
| 90 | + (KernRe(r'\s*____cacheline_aligned', re.S), ' '), | |
| 91 | + (KernRe(r'\s*__cacheline_group_(begin|end)\([^\)]+\);'), ''), | |
| 92 | + # | |
| 93 | + # Unwrap struct_group macros based on this definition: | |
| 94 | + # __struct_group(TAG, NAME, ATTRS, MEMBERS...) | |
| 95 | + # which has variants like: struct_group(NAME, MEMBERS...) | |
| 96 | + # Only MEMBERS arguments require documentation. | |
| 97 | + # | |
| 98 | + # Parsing them happens on two steps: | |
| 99 | + # | |
| 100 | + # 1. drop struct group arguments that aren't at MEMBERS, | |
| 101 | + # storing them as STRUCT_GROUP(MEMBERS) | |
| 102 | + # | |
| 103 | + # 2. remove STRUCT_GROUP() ancillary macro. | |
| 104 | + # | |
| 105 | + # The original logic used to remove STRUCT_GROUP() using an | |
| 106 | + # advanced regex: | |
| 107 | + # | |
| 108 | + # \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*; | |
| 109 | + # | |
| 110 | + # with two patterns that are incompatible with | |
| 111 | + # Python re module, as it has: | |
| 112 | + # | |
| 113 | + # - a recursive pattern: (?1) | |
| 114 | + # - an atomic grouping: (?>...) | |
| 115 | + # | |
| 116 | + # I tried a simpler version: but it didn't work either: | |
| 117 | + # \bSTRUCT_GROUP\(([^\)]+)\)[^;]*; | |
| 118 | + # | |
| 119 | + # As it doesn't properly match the end parenthesis on some cases. | |
| 120 | + # | |
| 121 | + # So, a better solution was crafted: there's now a NestedMatch | |
| 122 | + # class that ensures that delimiters after a search are properly | |
| 123 | + # matched. So, the implementation to drop STRUCT_GROUP() will be | |
| 124 | + # handled in separate. | |
| 125 | + # | |
| 126 | + (KernRe(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('), | |
| 127 | + (KernRe(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_GROUP('), | |
| 128 | + (KernRe(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r'struct \1 \2; STRUCT_GROUP('), | |
| 129 | + (KernRe(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROUP('), | |
| 130 | + # | |
| 131 | + # Replace macros | |
| 132 | + # | |
| 133 | + # TODO: use NestedMatch for FOO($1, $2, ...) matches | |
| 134 | + # | |
| 135 | + # it is better to also move those to the NestedMatch logic, | |
| 136 | + # to ensure that parenthesis will be properly matched. | |
| 137 | + # | |
| 138 | + (KernRe(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S), | |
| 139 | + r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'), | |
| 140 | + (KernRe(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S), | |
| 141 | + r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'), | |
| 142 | + (KernRe(r'DECLARE_BITMAP\s*\(' + struct_args_pattern + r',\s*' + struct_args_pattern + r'\)', | |
| 143 | + re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'), | |
| 144 | + (KernRe(r'DECLARE_HASHTABLE\s*\(' + struct_args_pattern + r',\s*' + struct_args_pattern + r'\)', | |
| 145 | + re.S), r'unsigned long \1[1 << ((\2) - 1)]'), | |
| 146 | + (KernRe(r'DECLARE_KFIFO\s*\(' + struct_args_pattern + r',\s*' + struct_args_pattern + | |
| 147 | + r',\s*' + struct_args_pattern + r'\)', re.S), r'\2 *\1'), | |
| 148 | + (KernRe(r'DECLARE_KFIFO_PTR\s*\(' + struct_args_pattern + r',\s*' + | |
| 149 | + struct_args_pattern + r'\)', re.S), r'\2 *\1'), | |
| 150 | + (KernRe(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + struct_args_pattern + r',\s*' + | |
| 151 | + struct_args_pattern + r'\)', re.S), r'\1 \2[]'), | |
| 152 | + (KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + struct_args_pattern + r'\)', re.S), r'dma_addr_t \1'), | |
| 153 | + (KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + struct_args_pattern + r'\)', re.S), r'__u32 \1'), | |
| 154 | +] | |
| 155 | +# | |
| 156 | +# Regexes here are guaranteed to have the end limiter matching | |
| 157 | +# the start delimiter. Yet, right now, only one replace group | |
| 158 | +# is allowed. | |
| 159 | +# | |
| 160 | +struct_nested_prefixes = [ | |
| 161 | + (re.compile(r'\bSTRUCT_GROUP\('), r'\1'), | |
| 162 | +] | |
| 163 | + | |
| 164 | +# | |
| 165 | +# Transforms for function prototypes | |
| 166 | +# | |
| 167 | +function_xforms = [ | |
| 168 | + (KernRe(r"^static +"), ""), | |
| 169 | + (KernRe(r"^extern +"), ""), | |
| 170 | + (KernRe(r"^asmlinkage +"), ""), | |
| 171 | + (KernRe(r"^inline +"), ""), | |
| 172 | + (KernRe(r"^__inline__ +"), ""), | |
| 173 | + (KernRe(r"^__inline +"), ""), | |
| 174 | + (KernRe(r"^__always_inline +"), ""), | |
| 175 | + (KernRe(r"^noinline +"), ""), | |
| 176 | + (KernRe(r"^__FORTIFY_INLINE +"), ""), | |
| 177 | + (KernRe(r"__init +"), ""), | |
| 178 | + (KernRe(r"__init_or_module +"), ""), | |
| 179 | + (KernRe(r"__deprecated +"), ""), | |
| 180 | + (KernRe(r"__flatten +"), ""), | |
| 181 | + (KernRe(r"__meminit +"), ""), | |
| 182 | + (KernRe(r"__must_check +"), ""), | |
| 183 | + (KernRe(r"__weak +"), ""), | |
| 184 | + (KernRe(r"__sched +"), ""), | |
| 185 | + (KernRe(r"_noprof"), ""), | |
| 186 | + (KernRe(r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +"), ""), | |
| 187 | + (KernRe(r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +"), ""), | |
| 188 | + (KernRe(r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +"), ""), | |
| 189 | + (KernRe(r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)"), r"\1, \2"), | |
| 190 | + (KernRe(r"__attribute_const__ +"), ""), | |
| 191 | + (KernRe(r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+"), ""), | |
| 192 | +] | |
| 193 | + | |
| 194 | +# | |
| 195 | +# Apply a set of transforms to a block of text. | |
| 196 | +# | |
| 197 | +def apply_transforms(xforms, text): | |
| 198 | + for search, subst in xforms: | |
| 199 | + text = search.sub(subst, text) | |
| 200 | + return text | |
| 201 | + | |
| 77 | 202 | # |
| 78 | 203 | # A little helper to get rid of excess white space |
| 79 | 204 | # |
| @@ -81,6 +206,21 @@ multi_space = KernRe(r'\s\s+') · 1 unmodified line | ||
| 81 | 206 | def trim_whitespace(s): |
| 82 | 207 | return multi_space.sub(' ', s.strip()) |
| 83 | 208 | |
| 209 | +# | |
| 210 | +# Remove struct/enum members that have been marked "private". | |
| 211 | +# | |
| 212 | +def trim_private_members(text): | |
| 213 | + # | |
| 214 | + # First look for a "public:" block that ends a private region, then | |
| 215 | + # handle the "private until the end" case. | |
| 216 | + # | |
| 217 | + text = KernRe(r'/\*\s*private:.*?/\*\s*public:.*?\*/', flags=re.S).sub('', text) | |
| 218 | + text = KernRe(r'/\*\s*private:.*', flags=re.S).sub('', text) | |
| 219 | + # | |
| 220 | + # We needed the comments to do the above, but now we can take them out. | |
| 221 | + # | |
| 222 | + return KernRe(r'\s*/\*.*?\*/\s*', flags=re.S).sub('', text).strip() | |
| 223 | + | |
| 84 | 224 | class state: |
| 85 | 225 | """ |
| 86 | 226 | State machine enums |
| @@ -318,36 +458,26 @@ class KernelDoc: · 231 unmodified lines | ||
| 318 | 458 | |
| 319 | 459 | param = KernRe(r'[\[\)].*').sub('', param, count=1) |
| 320 | 460 | |
| 321 | − if dtype == "" and param.endswith("..."): | |
| 322 | − if KernRe(r'\w\.\.\.$').search(param): | |
| 323 | − # For named variable parameters of the form `x...`, | |
| 324 | − # remove the dots | |
| 325 | − param = param[:-3] | |
| 326 | − else: | |
| 327 | − # Handles unnamed variable parameters | |
| 328 | − param = "..." | |
| 329 | − | |
| 330 | − if param not in self.entry.parameterdescs or \ | |
| 331 | − not self.entry.parameterdescs[param]: | |
| 332 | − | |
| 333 | − self.entry.parameterdescs[param] = "variable arguments" | |
| 334 | − | |
| 335 | − elif dtype == "" and (not param or param == "void"): | |
| 336 | − param = "void" | |
| 337 | − self.entry.parameterdescs[param] = "no arguments" | |
| 338 | − | |
| 339 | − elif dtype == "" and param in ["struct", "union"]: | |
| 340 | − # Handle unnamed (anonymous) union or struct | |
| 341 | − dtype = param | |
| 342 | − param = "{unnamed_" + param + "}" | |
| 343 | − self.entry.parameterdescs[param] = "anonymous\n" | |
| 344 | − self.entry.anon_struct_union = True | |
| 345 | − | |
| 346 | − # Handle cache group enforcing variables: they do not need | |
| 347 | − # to be described in header files | |
| 348 | − elif "__cacheline_group" in param: | |
| 349 | − # Ignore __cacheline_group_begin and __cacheline_group_end | |
| 350 | − return | |
| 461 | + # | |
| 462 | + # Look at various "anonymous type" cases. | |
| 463 | + # | |
| 464 | + if dtype == '': | |
| 465 | + if param.endswith("..."): | |
| 466 | + if len(param) > 3: # there is a name provided, use that | |
| 467 | + param = param[:-3] | |
| 468 | + if not self.entry.parameterdescs.get(param): | |
| 469 | + self.entry.parameterdescs[param] = "variable arguments" | |
| 470 | + | |
| 471 | + elif (not param) or param == "void": | |
| 472 | + param = "void" | |
| 473 | + self.entry.parameterdescs[param] = "no arguments" | |
| 474 | + | |
| 475 | + elif param in ["struct", "union"]: | |
| 476 | + # Handle unnamed (anonymous) union or struct | |
| 477 | + dtype = param | |
| 478 | + param = "{unnamed_" + param + "}" | |
| 479 | + self.entry.parameterdescs[param] = "anonymous\n" | |
| 480 | + self.entry.anon_struct_union = True | |
| 351 | 481 | |
| 352 | 482 | # Warn if parameter has no description |
| 353 | 483 | # (but ignore ones starting with # as these are not parameters |
| @@ -389,9 +519,6 @@ class KernelDoc: · 35 unmodified lines | ||
| 389 | 519 | args = arg_expr.sub(r"\1#", args) |
| 390 | 520 | |
| 391 | 521 | for arg in args.split(splitter): |
| 392 | − # Strip comments | |
| 393 | − arg = KernRe(r'\/\*.*\*\/').sub('', arg) | |
| 394 | − | |
| 395 | 522 | # Ignore argument attributes |
| 396 | 523 | arg = KernRe(r'\sPOS0?\s').sub(' ', arg) |
| 397 | 524 | |
| @@ -407,81 +534,76 @@ class KernelDoc: · 9 unmodified lines | ||
| 407 | 534 | # Treat preprocessor directive as a typeless variable |
| 408 | 535 | self.push_parameter(ln, decl_type, arg, "", |
| 409 | 536 | "", declaration_name) |
| 410 | − | |
| 537 | + # | |
| 538 | + # The pointer-to-function case. | |
| 539 | + # | |
| 411 | 540 | elif KernRe(r'\(.+\)\s*\(').search(arg): |
| 412 | − # Pointer-to-function | |
| 413 | − | |
| 414 | 541 | arg = arg.replace('#', ',') |
| 415 | − | |
| 416 | − r = KernRe(r'[^\(]+\(\*?\s*([\w\[\]\.]*)\s*\)') | |
| 542 | + r = KernRe(r'[^\(]+\(\*?\s*' # Everything up to "(*" | |
| 543 | + r'([\w\[\].]*)' # Capture the name and possible [array] | |
| 544 | + r'\s*\)') # Make sure the trailing ")" is there | |
| 417 | 545 | if r.match(arg): |
| 418 | 546 | param = r.group(1) |
| 419 | 547 | else: |
| 420 | 548 | self.emit_msg(ln, f"Invalid param: {arg}") |
| 421 | 549 | param = arg |
| 422 | − | |
| 423 | − dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) | |
| 424 | − self.push_parameter(ln, decl_type, param, dtype, | |
| 425 | − arg, declaration_name) | |
| 426 | − | |
| 550 | + dtype = arg.replace(param, '') | |
| 551 | + self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name) | |
| 552 | + # | |
| 553 | + # The array-of-pointers case. Dig the parameter name out from the middle | |
| 554 | + # of the declaration. | |
| 555 | + # | |
| 427 | 556 | elif KernRe(r'\(.+\)\s*\[').search(arg): |
| 428 | − # Array-of-pointers | |
| 429 | − | |
| 430 | − arg = arg.replace('#', ',') | |
| 431 | − r = KernRe(r'[^\(]+\(\s*\*\s*([\w\[\]\.]*?)\s*(\s*\[\s*[\w]+\s*\]\s*)*\)') | |
| 557 | + r = KernRe(r'[^\(]+\(\s*\*\s*' # Up to "(" and maybe "*" | |
| 558 | + r'([\w.]*?)' # The actual pointer name | |
| 559 | + r'\s*(\[\s*\w+\s*\]\s*)*\)') # The [array portion] | |
| 432 | 560 | if r.match(arg): |
| 433 | 561 | param = r.group(1) |
| 434 | 562 | else: |
| 435 | 563 | self.emit_msg(ln, f"Invalid param: {arg}") |
| 436 | 564 | param = arg |
| 437 | − | |
| 438 | − dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg) | |
| 439 | − | |
| 440 | − self.push_parameter(ln, decl_type, param, dtype, | |
| 441 | − arg, declaration_name) | |
| 442 | − | |
| 565 | + dtype = arg.replace(param, '') | |
| 566 | + self.push_parameter(ln, decl_type, param, dtype, arg, declaration_name) | |
| 443 | 567 | elif arg: |
| 568 | + # | |
| 569 | + # Clean up extraneous spaces and split the string at commas; the first | |
| 570 | + # element of the resulting list will also include the type information. | |
| 571 | + # | |
| 444 | 572 | arg = KernRe(r'\s*:\s*').sub(":", arg) |
| 445 | 573 | arg = KernRe(r'\s*\[').sub('[', arg) |
| 446 | − | |
| 447 | 574 | args = KernRe(r'\s*,\s*').split(arg) |
| 448 | − if args[0] and '*' in args[0]: | |
| 449 | − args[0] = re.sub(r'(\*+)\s*', r' \1', args[0]) | |
| 450 | − | |
| 451 | − first_arg = [] | |
| 452 | − r = KernRe(r'^(.*\s+)(.*?\[.*\].*)$') | |
| 453 | − if args[0] and r.match(args[0]): | |
| 454 | − args.pop(0) | |
| 455 | − first_arg.extend(r.group(1)) | |
| 456 | − first_arg.append(r.group(2)) | |
| 575 | + args[0] = re.sub(r'(\*+)\s*', r' \1', args[0]) | |
| 576 | + # | |
| 577 | + # args[0] has a string of "type a". If "a" includes an [array] | |
| 578 | + # declaration, we want to not be fooled by any white space inside | |
| 579 | + # the brackets, so detect and handle that case specially. | |
| 580 | + # | |
| 581 | + r = KernRe(r'^([^[\]]*\s+)(.*)$') | |
| 582 | + if r.match(args[0]): | |
| 583 | + args[0] = r.group(2) | |
| 584 | + dtype = r.group(1) | |
| 457 | 585 | else: |
| 458 | − first_arg = KernRe(r'\s+').split(args.pop(0)) | |
| 459 | − | |
| 460 | − args.insert(0, first_arg.pop()) | |
| 461 | − dtype = ' '.join(first_arg) | |
| 586 | + # No space in args[0]; this seems wrong but preserves previous behavior | |
| 587 | + dtype = '' | |
| 462 | 588 | |
| 589 | + bitfield_re = KernRe(r'(.*?):(\w+)') | |
| 463 | 590 | for param in args: |
| 464 | − if KernRe(r'^(\*+)\s*(.*)').match(param): | |
| 465 | − r = KernRe(r'^(\*+)\s*(.*)') | |
| 466 | − if not r.match(param): | |
| 467 | − self.emit_msg(ln, f"Invalid param: {param}") | |
| 468 | − continue | |
| 469 | − | |
| 470 | − param = r.group(1) | |
| 471 | − | |
| 591 | + # | |
| 592 | + # For pointers, shift the star(s) from the variable name to the | |
| 593 | + # type declaration. | |
| 594 | + # | |
| 595 | + r = KernRe(r'^(\*+)\s*(.*)') | |
| 596 | + if r.match(param): | |
| 472 | 597 | self.push_parameter(ln, decl_type, r.group(2), |
| 473 | 598 | f"{dtype} {r.group(1)}", |
| 474 | 599 | arg, declaration_name) |
| 475 | − | |
| 476 | − elif KernRe(r'(.*?):(\w+)').search(param): | |
| 477 | − r = KernRe(r'(.*?):(\w+)') | |
| 478 | − if not r.match(param): | |
| 479 | − self.emit_msg(ln, f"Invalid param: {param}") | |
| 480 | − continue | |
| 481 | − | |
| 600 | + # | |
| 601 | + # Perform a similar shift for bitfields. | |
| 602 | + # | |
| 603 | + elif bitfield_re.search(param): | |
| 482 | 604 | if dtype != "": # Skip unnamed bit-fields |
| 483 | − self.push_parameter(ln, decl_type, r.group(1), | |
| 484 | − f"{dtype}:{r.group(2)}", | |
| 605 | + self.push_parameter(ln, decl_type, bitfield_re.group(1), | |
| 606 | + f"{dtype}:{bitfield_re.group(2)}", | |
| 485 | 607 | arg, declaration_name) |
| 486 | 608 | else: |
| 487 | 609 | self.push_parameter(ln, decl_type, param, dtype, |
| @@ -520,13 +642,11 @@ class KernelDoc: · 32 unmodified lines | ||
| 520 | 642 | self.emit_msg(ln, |
| 521 | 643 | f"No description found for return value of '{declaration_name}'") |
| 522 | 644 | |
| 523 | − def dump_struct(self, ln, proto): | |
| 524 | − """ | |
| 525 | − Store an entry for an struct or union | |
| 526 | − """ | |
| 527 | − | |
| 645 | + # | |
| 646 | + # Split apart a structure prototype; returns (struct|union, name, members) or None | |
| 647 | + # | |
| 648 | + def split_struct_proto(self, proto): | |
| 528 | 649 | type_pattern = r'(struct|union)' |
| 529 | − | |
| 530 | 650 | qualifiers = [ |
| 531 | 651 | "__attribute__", |
| 532 | 652 | "__packed", |
| @@ -534,288 +654,202 @@ class KernelDoc: · 1 unmodified line | ||
| 534 | 654 | "____cacheline_aligned_in_smp", |
| 535 | 655 | "____cacheline_aligned", |
| 536 | 656 | ] |
| 537 | − | |
| 538 | 657 | definition_body = r'\{(.*)\}\s*' + "(?:" + '|'.join(qualifiers) + ")?" |
| 539 | − struct_members = KernRe(type_pattern + r'([^\{\};]+)(\{)([^\{\}]*)(\})([^\{\}\;]*)(\;)') | |
| 540 | − | |
| 541 | − # Extract struct/union definition | |
| 542 | − members = None | |
| 543 | − declaration_name = None | |
| 544 | − decl_type = None | |
| 545 | 658 | |
| 546 | 659 | r = KernRe(type_pattern + r'\s+(\w+)\s*' + definition_body) |
| 547 | 660 | if r.search(proto): |
| 548 | − decl_type = r.group(1) | |
| 549 | − declaration_name = r.group(2) | |
| 550 | − members = r.group(3) | |
| 661 | + return (r.group(1), r.group(2), r.group(3)) | |
| 551 | 662 | else: |
| 552 | 663 | r = KernRe(r'typedef\s+' + type_pattern + r'\s*' + definition_body + r'\s*(\w+)\s*;') |
| 553 | − | |
| 554 | 664 | if r.search(proto): |
| 555 | − decl_type = r.group(1) | |
| 556 | − declaration_name = r.group(3) | |
| 557 | − members = r.group(2) | |
| 558 | − | |
| 559 | − if not members: | |
| 560 | − self.emit_msg(ln, f"{proto} error: Cannot parse struct or union!") | |
| 561 | − return | |
| 562 | − | |
| 563 | − if self.entry.identifier != declaration_name: | |
| 564 | − self.emit_msg(ln, | |
| 565 | − f"expecting prototype for {decl_type} {self.entry.identifier}. Prototype was for {decl_type} {declaration_name} instead\n") | |
| 566 | − return | |
| 567 | − | |
| 568 | − args_pattern = r'([^,)]+)' | |
| 569 | − | |
| 570 | − sub_prefixes = [ | |
| 571 | − (KernRe(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', re.S | re.I), ''), | |
| 572 | − (KernRe(r'\/\*\s*private:.*', re.S | re.I), ''), | |
| 573 | − | |
| 574 | − # Strip comments | |
| 575 | − (KernRe(r'\/\*.*?\*\/', re.S), ''), | |
| 576 | − | |
| 577 | − # Strip attributes | |
| 578 | − (attribute, ' '), | |
| 579 | − (KernRe(r'\s*__aligned\s*\([^;]*\)', re.S), ' '), | |
| 580 | − (KernRe(r'\s*__counted_by\s*\([^;]*\)', re.S), ' '), | |
| 581 | − (KernRe(r'\s*__counted_by_(le|be)\s*\([^;]*\)', re.S), ' '), | |
| 582 | − (KernRe(r'\s*__packed\s*', re.S), ' '), | |
| 583 | − (KernRe(r'\s*CRYPTO_MINALIGN_ATTR', re.S), ' '), | |
| 584 | − (KernRe(r'\s*____cacheline_aligned_in_smp', re.S), ' '), | |
| 585 | − (KernRe(r'\s*____cacheline_aligned', re.S), ' '), | |
| 586 | − | |
| 587 | − # Unwrap struct_group macros based on this definition: | |
| 588 | − # __struct_group(TAG, NAME, ATTRS, MEMBERS...) | |
| 589 | − # which has variants like: struct_group(NAME, MEMBERS...) | |
| 590 | − # Only MEMBERS arguments require documentation. | |
| 591 | − # | |
| 592 | − # Parsing them happens on two steps: | |
| 593 | − # | |
| 594 | − # 1. drop struct group arguments that aren't at MEMBERS, | |
| 595 | − # storing them as STRUCT_GROUP(MEMBERS) | |
| 596 | − # | |
| 597 | − # 2. remove STRUCT_GROUP() ancillary macro. | |
| 598 | − # | |
| 599 | − # The original logic used to remove STRUCT_GROUP() using an | |
| 600 | − # advanced regex: | |
| 601 | − # | |
| 602 | − # \bSTRUCT_GROUP(\(((?:(?>[^)(]+)|(?1))*)\))[^;]*; | |
| 603 | − # | |
| 604 | − # with two patterns that are incompatible with | |
| 605 | − # Python re module, as it has: | |
| 606 | − # | |
| 607 | − # - a recursive pattern: (?1) | |
| 608 | − # - an atomic grouping: (?>...) | |
| 609 | − # | |
| 610 | − # I tried a simpler version: but it didn't work either: | |
| 611 | − # \bSTRUCT_GROUP\(([^\)]+)\)[^;]*; | |
| 612 | − # | |
| 613 | − # As it doesn't properly match the end parenthesis on some cases. | |
| 614 | − # | |
| 615 | − # So, a better solution was crafted: there's now a NestedMatch | |
| 616 | − # class that ensures that delimiters after a search are properly | |
| 617 | − # matched. So, the implementation to drop STRUCT_GROUP() will be | |
| 618 | − # handled in separate. | |
| 619 | − | |
| 620 | − (KernRe(r'\bstruct_group\s*\(([^,]*,)', re.S), r'STRUCT_GROUP('), | |
| 621 | − (KernRe(r'\bstruct_group_attr\s*\(([^,]*,){2}', re.S), r'STRUCT_GROUP('), | |
| 622 | − (KernRe(r'\bstruct_group_tagged\s*\(([^,]*),([^,]*),', re.S), r'struct \1 \2; STRUCT_GROUP('), | |
| 623 | − (KernRe(r'\b__struct_group\s*\(([^,]*,){3}', re.S), r'STRUCT_GROUP('), | |
| 624 | − | |
| 625 | − # Replace macros | |
| 626 | − # | |
| 627 | − # TODO: use NestedMatch for FOO($1, $2, ...) matches | |
| 628 | − # | |
| 629 | − # it is better to also move those to the NestedMatch logic, | |
| 630 | − # to ensure that parenthesis will be properly matched. | |
| 631 | − | |
| 632 | − (KernRe(r'__ETHTOOL_DECLARE_LINK_MODE_MASK\s*\(([^\)]+)\)', re.S), r'DECLARE_BITMAP(\1, __ETHTOOL_LINK_MODE_MASK_NBITS)'), | |
| 633 | − (KernRe(r'DECLARE_PHY_INTERFACE_MASK\s*\(([^\)]+)\)', re.S), r'DECLARE_BITMAP(\1, PHY_INTERFACE_MODE_MAX)'), | |
| 634 | − (KernRe(r'DECLARE_BITMAP\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[BITS_TO_LONGS(\2)]'), | |
| 635 | − (KernRe(r'DECLARE_HASHTABLE\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'unsigned long \1[1 << ((\2) - 1)]'), | |
| 636 | − (KernRe(r'DECLARE_KFIFO\s*\(' + args_pattern + r',\s*' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'), | |
| 637 | − (KernRe(r'DECLARE_KFIFO_PTR\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\2 *\1'), | |
| 638 | − (KernRe(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\1 \2[]'), | |
| 639 | − (KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + args_pattern + r'\)', re.S), r'dma_addr_t \1'), | |
| 640 | − (KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + args_pattern + r'\)', re.S), r'__u32 \1'), | |
| 641 | − (KernRe(r'VIRTIO_DECLARE_FEATURES\s*\(' + args_pattern + r'\)', re.S), r'u64 \1; u64 \1_array[VIRTIO_FEATURES_DWORDS]'), | |
| 642 | − ] | |
| 643 | − | |
| 644 | − # Regexes here are guaranteed to have the end limiter matching | |
| 645 | − # the start delimiter. Yet, right now, only one replace group | |
| 646 | − # is allowed. | |
| 647 | − | |
| 648 | − sub_nested_prefixes = [ | |
| 649 | − (re.compile(r'\bSTRUCT_GROUP\('), r'\1'), | |
| 650 | − ] | |
| 651 | − | |
| 652 | − for search, sub in sub_prefixes: | |
| 653 | − members = search.sub(sub, members) | |
| 654 | − | |
| 655 | − nested = NestedMatch() | |
| 656 | − | |
| 657 | − for search, sub in sub_nested_prefixes: | |
| 658 | − members = nested.sub(search, sub, members) | |
| 659 | − | |
| 660 | − # Keeps the original declaration as-is | |
| 661 | − declaration = members | |
| 662 | − | |
| 663 | − # Split nested struct/union elements | |
| 664 | − # | |
| 665 | − # This loop was simpler at the original kernel-doc perl version, as | |
| 666 | − # while ($members =~ m/$struct_members/) { ... } | |
| 667 | − # reads 'members' string on each interaction. | |
| 668 | − # | |
| 669 | − # Python behavior is different: it parses 'members' only once, | |
| 670 | − # creating a list of tuples from the first interaction. | |
| 665 | + return (r.group(1), r.group(3), r.group(2)) | |
| 666 | + return None | |
| 667 | + # | |
| 668 | + # Rewrite the members of a structure or union for easier formatting later on. | |
| 669 | + # Among other things, this function will turn a member like: | |
| 670 | + # | |
| 671 | + # struct { inner_members; } foo; | |
| 672 | + # | |
| 673 | + # into: | |
| 674 | + # | |
| 675 | + # struct foo; inner_members; | |
| 676 | + # | |
| 677 | + def rewrite_struct_members(self, members): | |
| 671 | 678 | # |
| 672 | − # On other words, this won't get nested structs. | |
| 679 | + # Process struct/union members from the most deeply nested outward. The | |
| 680 | + # trick is in the ^{ below - it prevents a match of an outer struct/union | |
| 681 | + # until the inner one has been munged (removing the "{" in the process). | |
| 673 | 682 | # |
| 674 | − # So, we need to have an extra loop on Python to override such | |
| 675 | − # re limitation. | |
| 676 | − | |
| 677 | − while True: | |
| 678 | − tuples = struct_members.findall(members) | |
| 679 | − if not tuples: | |
| 680 | − break | |
| 681 | − | |
| 683 | + struct_members = KernRe(r'(struct|union)' # 0: declaration type | |
| 684 | + r'([^\{\};]+)' # 1: possible name | |
| 685 | + r'(\{)' | |
| 686 | + r'([^\{\}]*)' # 3: Contents of declaration | |
| 687 | + r'(\})' | |
| 688 | + r'([^\{\};]*)(;)') # 5: Remaining stuff after declaration | |
| 689 | + tuples = struct_members.findall(members) | |
| 690 | + while tuples: | |
| 682 | 691 | for t in tuples: |
| 683 | 692 | newmember = "" |
| 684 | − maintype = t[0] | |
| 685 | − s_ids = t[5] | |
| 686 | − content = t[3] | |
| 687 | − | |
| 688 | − oldmember = "".join(t) | |
| 689 | − | |
| 690 | − for s_id in s_ids.split(','): | |
| 693 | + oldmember = "".join(t) # Reconstruct the original formatting | |
| 694 | + dtype, name, lbr, content, rbr, rest, semi = t | |
| 695 | + # | |
| 696 | + # Pass through each field name, normalizing the form and formatting. | |
| 697 | + # | |
| 698 | + for s_id in rest.split(','): | |
| 691 | 699 | s_id = s_id.strip() |
| 692 | − | |
| 693 | − newmember += f"{maintype} {s_id}; " | |
| 700 | + newmember += f"{dtype} {s_id}; " | |
| 701 | + # | |
| 702 | + # Remove bitfield/array/pointer info, getting the bare name. | |
| 703 | + # | |
| 694 | 704 | s_id = KernRe(r'[:\[].*').sub('', s_id) |
| 695 | 705 | s_id = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', s_id) |
| 696 | − | |
| 706 | + # | |
| 707 | + # Pass through the members of this inner structure/union. | |
| 708 | + # | |
| 697 | 709 | for arg in content.split(';'): |
| 698 | 710 | arg = arg.strip() |
| 699 | − | |
| 700 | − if not arg: | |
| 701 | − continue | |
| 702 | − | |
| 703 | − r = KernRe(r'^([^\(]+\(\*?\s*)([\w\.]*)(\s*\).*)') | |
| 711 | + # | |
| 712 | + # Look for (type)(*name)(args) - pointer to function | |
| 713 | + # | |
| 714 | + r = KernRe(r'^([^\(]+\(\*?\s*)([\w.]*)(\s*\).*)') | |
| 704 | 715 | if r.match(arg): |
| 716 | + dtype, name, extra = r.group(1), r.group(2), r.group(3) | |
| 705 | 717 | # Pointer-to-function |
| 706 | − dtype = r.group(1) | |
| 707 | − name = r.group(2) | |
| 708 | − extra = r.group(3) | |
| 709 | − | |
| 710 | − if not name: | |
| 711 | − continue | |
| 712 | − | |
| 713 | 718 | if not s_id: |
| 714 | 719 | # Anonymous struct/union |
| 715 | 720 | newmember += f"{dtype}{name}{extra}; " |
| 716 | 721 | else: |
| 717 | 722 | newmember += f"{dtype}{s_id}.{name}{extra}; " |
| 718 | − | |
| 723 | + # | |
| 724 | + # Otherwise a non-function member. | |
| 725 | + # | |
| 719 | 726 | else: |
| 720 | − arg = arg.strip() | |
| 721 | − # Handle bitmaps | |
| 727 | + # | |
| 728 | + # Remove bitmap and array portions and spaces around commas | |
| 729 | + # | |
| 722 | 730 | arg = KernRe(r':\s*\d+\s*').sub('', arg) |
| 723 | − | |
| 724 | − # Handle arrays | |
| 725 | 731 | arg = KernRe(r'\[.*\]').sub('', arg) |
| 726 | − | |
| 727 | − # Handle multiple IDs | |
| 728 | 732 | arg = KernRe(r'\s*,\s*').sub(',', arg) |
| 729 | − | |
| 733 | + # | |
| 734 | + # Look for a normal decl - "type name[,name...]" | |
| 735 | + # | |
| 730 | 736 | r = KernRe(r'(.*)\s+([\S+,]+)') |
| 731 | − | |
| 732 | 737 | if r.search(arg): |
| 733 | − dtype = r.group(1) | |
| 734 | − names = r.group(2) | |
| 738 | + for name in r.group(2).split(','): | |
| 739 | + name = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', name) | |
| 740 | + if not s_id: | |
| 741 | + # Anonymous struct/union | |
| 742 | + newmember += f"{r.group(1)} {name}; " | |
| 743 | + else: | |
| 744 | + newmember += f"{r.group(1)} {s_id}.{name}; " | |
| 735 | 745 | else: |
| 736 | 746 | newmember += f"{arg}; " |
| 737 | − continue | |
| 738 | − | |
| 739 | − for name in names.split(','): | |
| 740 | − name = KernRe(r'^\s*\**(\S+)\s*').sub(r'\1', name).strip() | |
| 741 | − | |
| 742 | − if not name: | |
| 743 | − continue | |
| 744 | − | |
| 745 | − if not s_id: | |
| 746 | − # Anonymous struct/union | |
| 747 | − newmember += f"{dtype} {name}; " | |
| 748 | − else: | |
| 749 | − newmember += f"{dtype} {s_id}.{name}; " | |
| 750 | − | |
| 747 | + # | |
| 748 | + # At the end of the s_id loop, replace the original declaration with | |
| 749 | + # the munged version. | |
| 750 | + # | |
| 751 | 751 | members = members.replace(oldmember, newmember) |
| 752 | + # | |
| 753 | + # End of the tuple loop - search again and see if there are outer members | |
| 754 | + # that now turn up. | |
| 755 | + # | |
| 756 | + tuples = struct_members.findall(members) | |
| 757 | + return members | |
| 752 | 758 | |
| 753 | − # Ignore other nested elements, like enums | |
| 754 | − members = re.sub(r'(\{[^\{\}]*\})', '', members) | |
| 755 | − | |
| 756 | − self.create_parameter_list(ln, decl_type, members, ';', | |
| 757 | − declaration_name) | |
| 758 | − self.check_sections(ln, declaration_name, decl_type) | |
| 759 | − | |
| 760 | − # Adjust declaration for better display | |
| 759 | + # | |
| 760 | + # Format the struct declaration into a standard form for inclusion in the | |
| 761 | + # resulting docs. | |
| 762 | + # | |
| 763 | + def format_struct_decl(self, declaration): | |
| 764 | + # | |
| 765 | + # Insert newlines, get rid of extra spaces. | |
| 766 | + # | |
| 761 | 767 | declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration) |
| 762 | 768 | declaration = KernRe(r'\}\s+;').sub('};', declaration) |
| 763 | − | |
| 764 | − # Better handle inlined enums | |
| 765 | − while True: | |
| 766 | − r = KernRe(r'(enum\s+\{[^\}]+),([^\n])') | |
| 767 | − if not r.search(declaration): | |
| 768 | − break | |
| 769 | − | |
| 769 | + # | |
| 770 | + # Format inline enums with each member on its own line. | |
| 771 | + # | |
| 772 | + r = KernRe(r'(enum\s+\{[^\}]+),([^\n])') | |
| 773 | + while r.search(declaration): | |
| 770 | 774 | declaration = r.sub(r'\1,\n\2', declaration) |
| 771 | − | |
| 775 | + # | |
| 776 | + # Now go through and supply the right number of tabs | |
| 777 | + # for each line. | |
| 778 | + # | |
| 772 | 779 | def_args = declaration.split('\n') |
| 773 | 780 | level = 1 |
| 774 | 781 | declaration = "" |
| 775 | 782 | for clause in def_args: |
| 783 | + clause = KernRe(r'\s+').sub(' ', clause.strip(), count=1) | |
| 784 | + if clause: | |
| 785 | + if '}' in clause and level > 1: | |
| 786 | + level -= 1 | |
| 787 | + if not clause.startswith('#'): | |
| 788 | + declaration += "\t" * level | |
| 789 | + declaration += "\t" + clause + "\n" | |
| 790 | + if "{" in clause and "}" not in clause: | |
| 791 | + level += 1 | |
| 792 | + return declaration | |
| 776 | 793 | |
| 777 | − clause = clause.strip() | |
| 778 | − clause = KernRe(r'\s+').sub(' ', clause, count=1) | |
| 779 | − | |
| 780 | − if not clause: | |
| 781 | − continue | |
| 782 | − | |
| 783 | − if '}' in clause and level > 1: | |
| 784 | − level -= 1 | |
| 785 | 794 | |
| 786 | − if not KernRe(r'^\s*#').match(clause): | |
| 787 | − declaration += "\t" * level | |
| 795 | + def dump_struct(self, ln, proto): | |
| 796 | + """ | |
| 797 | + Store an entry for an struct or union | |
| 798 | + """ | |
| 799 | + # | |
| 800 | + # Do the basic parse to get the pieces of the declaration. | |
| 801 | + # | |
| 802 | + struct_parts = self.split_struct_proto(proto) | |
| 803 | + if not struct_parts: | |
| 804 | + self.emit_msg(ln, f"{proto} error: Cannot parse struct or union!") | |
| 805 | + return | |
| 806 | + decl_type, declaration_name, members = struct_parts | |
| 788 | 807 | |
| 789 | − declaration += "\t" + clause + "\n" | |
| 790 | − if "{" in clause and "}" not in clause: | |
| 791 | − level += 1 | |
| 808 | + if self.entry.identifier != declaration_name: | |
| 809 | + self.emit_msg(ln, f"expecting prototype for {decl_type} {self.entry.identifier}. " | |
| 810 | + f"Prototype was for {decl_type} {declaration_name} instead\n") | |
| 811 | + return | |
| 812 | + # | |
| 813 | + # Go through the list of members applying all of our transformations. | |
| 814 | + # | |
| 815 | + members = trim_private_members(members) | |
| 816 | + members = apply_transforms(struct_xforms, members) | |
| 792 | 817 | |
| 818 | + nested = NestedMatch() | |
| 819 | + for search, sub in struct_nested_prefixes: | |
| 820 | + members = nested.sub(search, sub, members) | |
| 821 | + # | |
| 822 | + # Deal with embedded struct and union members, and drop enums entirely. | |
| 823 | + # | |
| 824 | + declaration = members | |
| 825 | + members = self.rewrite_struct_members(members) | |
| 826 | + members = re.sub(r'(\{[^\{\}]*\})', '', members) | |
| 827 | + # | |
| 828 | + # Output the result and we are done. | |
| 829 | + # | |
| 830 | + self.create_parameter_list(ln, decl_type, members, ';', | |
| 831 | + declaration_name) | |
| 832 | + self.check_sections(ln, declaration_name, decl_type) | |
| 793 | 833 | self.output_declaration(decl_type, declaration_name, |
| 794 | − definition=declaration, | |
| 834 | + definition=self.format_struct_decl(declaration), | |
| 795 | 835 | purpose=self.entry.declaration_purpose) |
| 796 | 836 | |
| 797 | 837 | def dump_enum(self, ln, proto): |
| 798 | 838 | """ |
| 799 | 839 | Stores an enum inside self.entries array. |
| 800 | 840 | """ |
| 801 | − | |
| 802 | − # Ignore members marked private | |
| 803 | − proto = KernRe(r'\/\*\s*private:.*?\/\*\s*public:.*?\*\/', flags=re.S).sub('', proto) | |
| 804 | − proto = KernRe(r'\/\*\s*private:.*}', flags=re.S).sub('}', proto) | |
| 805 | − | |
| 806 | − # Strip comments | |
| 807 | − proto = KernRe(r'\/\*.*?\*\/', flags=re.S).sub('', proto) | |
| 808 | − | |
| 809 | − # Strip #define macros inside enums | |
| 841 | + # | |
| 842 | + # Strip preprocessor directives. Note that this depends on the | |
| 843 | + # trailing semicolon we added in process_proto_type(). | |
| 844 | + # | |
| 810 | 845 | proto = KernRe(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=re.S).sub('', proto) |
| 811 | − | |
| 812 | 846 | # |
| 813 | 847 | # Parse out the name and members of the enum. Typedef form first. |
| 814 | 848 | # |
| 815 | 849 | r = KernRe(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;') |
| 816 | 850 | if r.search(proto): |
| 817 | 851 | declaration_name = r.group(2) |
| 818 | − members = r.group(1).rstrip() | |
| 852 | + members = trim_private_members(r.group(1)) | |
| 819 | 853 | # |
| 820 | 854 | # Failing that, look for a straight enum |
| 821 | 855 | # |
| @@ -823,7 +857,7 @@ class KernelDoc: · 1 unmodified line | ||
| 823 | 857 | r = KernRe(r'enum\s+(\w*)\s*\{(.*)\}') |
| 824 | 858 | if r.match(proto): |
| 825 | 859 | declaration_name = r.group(1) |
| 826 | − members = r.group(2).rstrip() | |
| 860 | + members = trim_private_members(r.group(2)) | |
| 827 | 861 | # |
| 828 | 862 | # OK, this isn't going to work. |
| 829 | 863 | # |
| @@ -892,62 +926,31 @@ class KernelDoc: · 62 unmodified lines | ||
| 892 | 926 | Stores a function of function macro inside self.entries array. |
| 893 | 927 | """ |
| 894 | 928 | |
| 895 | − func_macro = False | |
| 929 | + found = func_macro = False | |
| 896 | 930 | return_type = '' |
| 897 | 931 | decl_type = 'function' |
| 898 | − | |
| 899 | − # Prefixes that would be removed | |
| 900 | − sub_prefixes = [ | |
| 901 | − (r"^static +", "", 0), | |
| 902 | − (r"^extern +", "", 0), | |
| 903 | − (r"^asmlinkage +", "", 0), | |
| 904 | − (r"^inline +", "", 0), | |
| 905 | − (r"^__inline__ +", "", 0), | |
| 906 | − (r"^__inline +", "", 0), | |
| 907 | − (r"^__always_inline +", "", 0), | |
| 908 | − (r"^noinline +", "", 0), | |
| 909 | − (r"^__FORTIFY_INLINE +", "", 0), | |
| 910 | − (r"__init +", "", 0), | |
| 911 | − (r"__init_or_module +", "", 0), | |
| 912 | − (r"__deprecated +", "", 0), | |
| 913 | − (r"__flatten +", "", 0), | |
| 914 | − (r"__meminit +", "", 0), | |
| 915 | − (r"__must_check +", "", 0), | |
| 916 | − (r"__weak +", "", 0), | |
| 917 | − (r"__sched +", "", 0), | |
| 918 | − (r"_noprof", "", 0), | |
| 919 | − (r"__printf\s*\(\s*\d*\s*,\s*\d*\s*\) +", "", 0), | |
| 920 | − (r"__(?:re)?alloc_size\s*\(\s*\d+\s*(?:,\s*\d+\s*)?\) +", "", 0), | |
| 921 | − (r"__diagnose_as\s*\(\s*\S+\s*(?:,\s*\d+\s*)*\) +", "", 0), | |
| 922 | − (r"DECL_BUCKET_PARAMS\s*\(\s*(\S+)\s*,\s*(\S+)\s*\)", r"\1, \2", 0), | |
| 923 | − (r"__attribute_const__ +", "", 0), | |
| 924 | − | |
| 925 | − # It seems that Python support for re.X is broken: | |
| 926 | − # At least for me (Python 3.13), this didn't work | |
| 927 | −# (r""" | |
| 928 | −# __attribute__\s*\(\( | |
| 929 | −# (?: | |
| 930 | −# [\w\s]+ # attribute name | |
| 931 | −# (?:\([^)]*\))? # attribute arguments | |
| 932 | −# \s*,? # optional comma at the end | |
| 933 | −# )+ | |
| 934 | −# \)\)\s+ | |
| 935 | −# """, "", re.X), | |
| 936 | − | |
| 937 | − # So, remove whitespaces and comments from it | |
| 938 | − (r"__attribute__\s*\(\((?:[\w\s]+(?:\([^)]*\))?\s*,?)+\)\)\s+", "", 0), | |
| 939 | − ] | |
| 940 | − | |
| 941 | − for search, sub, flags in sub_prefixes: | |
| 942 | − prototype = KernRe(search, flags).sub(sub, prototype) | |
| 943 | − | |
| 944 | − # Macros are a special case, as they change the prototype format | |
| 932 | + # | |
| 933 | + # Apply the initial transformations. | |
| 934 | + # | |
| 935 | + prototype = apply_transforms(function_xforms, prototype) | |
| 936 | + # | |
| 937 | + # If we have a macro, remove the "#define" at the front. | |
| 938 | + # | |
| 945 | 939 | new_proto = KernRe(r"^#\s*define\s+").sub("", prototype) |
| 946 | 940 | if new_proto != prototype: |
| 947 | − is_define_proto = True | |
| 948 | 941 | prototype = new_proto |
| 949 | − else: | |
| 950 | − is_define_proto = False | |
| 942 | + # | |
| 943 | + # Dispense with the simple "#define A B" case here; the key | |
| 944 | + # is the space after the name of the symbol being defined. | |
| 945 | + # NOTE that the seemingly misnamed "func_macro" indicates a | |
| 946 | + # macro *without* arguments. | |
| 947 | + # | |
| 948 | + r = KernRe(r'^(\w+)\s+') | |
| 949 | + if r.search(prototype): | |
| 950 | + return_type = '' | |
| 951 | + declaration_name = r.group(1) | |
| 952 | + func_macro = True | |
| 953 | + found = True | |
| 951 | 954 | |
| 952 | 955 | # Yes, this truly is vile. We are looking for: |
| 953 | 956 | # 1. Return type (may be nothing if we're looking at a macro) |
| @@ -965,91 +968,73 @@ class KernelDoc: · 11 unmodified lines | ||
| 965 | 968 | # - atomic_set (macro) |
| 966 | 969 | # - pci_match_device, __copy_to_user (long return type) |
| 967 | 970 | |
| 968 | − name = r'[a-zA-Z0-9_~:]+' | |
| 969 | − prototype_end1 = r'[^\(]*' | |
| 970 | − prototype_end2 = r'[^\{]*' | |
| 971 | − prototype_end = fr'\(({prototype_end1}|{prototype_end2})\)' | |
| 972 | − | |
| 973 | − # Besides compiling, Perl qr{[\w\s]+} works as a non-capturing group. | |
| 974 | − # So, this needs to be mapped in Python with (?:...)? or (?:...)+ | |
| 975 | − | |
| 971 | + name = r'\w+' | |
| 976 | 972 | type1 = r'(?:[\w\s]+)?' |
| 977 | 973 | type2 = r'(?:[\w\s]+\*+)+' |
| 978 | − | |
| 979 | − found = False | |
| 980 | − | |
| 981 | − if is_define_proto: | |
| 982 | − r = KernRe(r'^()(' + name + r')\s+') | |
| 983 | − | |
| 984 | − if r.search(prototype): | |
| 985 | − return_type = '' | |
| 986 | − declaration_name = r.group(2) | |
| 987 | − func_macro = True | |
| 988 | − | |
| 989 | − found = True | |
| 990 | − | |
| 974 | + # | |
| 975 | + # Attempt to match first on (args) with no internal parentheses; this | |
| 976 | + # lets us easily filter out __acquires() and other post-args stuff. If | |
| 977 | + # that fails, just grab the rest of the line to the last closing | |
| 978 | + # parenthesis. | |
| 979 | + # | |
| 980 | + proto_args = r'\(([^\(]*|.*)\)' | |
| 981 | + # | |
| 982 | + # (Except for the simple macro case) attempt to split up the prototype | |
| 983 | + # in the various ways we understand. | |
| 984 | + # | |
| 991 | 985 | if not found: |
| 992 | 986 | patterns = [ |
| 993 | − rf'^()({name})\s*{prototype_end}', | |
| 994 | − rf'^({type1})\s+({name})\s*{prototype_end}', | |
| 995 | − rf'^({type2})\s*({name})\s*{prototype_end}', | |
| 987 | + rf'^()({name})\s*{proto_args}', | |
| 988 | + rf'^({type1})\s+({name})\s*{proto_args}', | |
| 989 | + rf'^({type2})\s*({name})\s*{proto_args}', | |
| 996 | 990 | ] |
| 997 | 991 | |
| 998 | 992 | for p in patterns: |
| 999 | 993 | r = KernRe(p) |
| 1000 | − | |
| 1001 | 994 | if r.match(prototype): |
| 1002 | − | |
| 1003 | 995 | return_type = r.group(1) |
| 1004 | 996 | declaration_name = r.group(2) |
| 1005 | 997 | args = r.group(3) |
| 1006 | − | |
| 1007 | 998 | self.create_parameter_list(ln, decl_type, args, ',', |
| 1008 | 999 | declaration_name) |
| 1009 | − | |
| 1010 | 1000 | found = True |
| 1011 | 1001 | break |
| 1002 | + # | |
| 1003 | + # Parsing done; make sure that things are as we expect. | |
| 1004 | + # | |
| 1012 | 1005 | if not found: |
| 1013 | 1006 | self.emit_msg(ln, |
| 1014 | 1007 | f"cannot understand function prototype: '{prototype}'") |
| 1015 | 1008 | return |
| 1016 | − | |
| 1017 | 1009 | if self.entry.identifier != declaration_name: |
| 1018 | − self.emit_msg(ln, | |
| 1019 | − f"expecting prototype for {self.entry.identifier}(). Prototype was for {declaration_name}() instead") | |
| 1010 | + self.emit_msg(ln, f"expecting prototype for {self.entry.identifier}(). " | |
| 1011 | + f"Prototype was for {declaration_name}() instead") | |
| 1020 | 1012 | return |
| 1021 | − | |
| 1022 | 1013 | self.check_sections(ln, declaration_name, "function") |
| 1023 | − | |
| 1024 | 1014 | self.check_return_section(ln, declaration_name, return_type) |
| 1015 | + # | |
| 1016 | + # Store the result. | |
| 1017 | + # | |
| 1018 | + self.output_declaration(decl_type, declaration_name, | |
| 1019 | + typedef=('typedef' in return_type), | |
| 1020 | + functiontype=return_type, | |
| 1021 | + purpose=self.entry.declaration_purpose, | |
| 1022 | + func_macro=func_macro) | |
| 1025 | 1023 | |
| 1026 | − if 'typedef' in return_type: | |
| 1027 | − self.output_declaration(decl_type, declaration_name, | |
| 1028 | − typedef=True, | |
| 1029 | − functiontype=return_type, | |
| 1030 | − purpose=self.entry.declaration_purpose, | |
| 1031 | − func_macro=func_macro) | |
| 1032 | − else: | |
| 1033 | − self.output_declaration(decl_type, declaration_name, | |
| 1034 | − typedef=False, | |
| 1035 | − functiontype=return_type, | |
| 1036 | − purpose=self.entry.declaration_purpose, | |
| 1037 | − func_macro=func_macro) | |
| 1038 | 1024 | |
| 1039 | 1025 | def dump_typedef(self, ln, proto): |
| 1040 | 1026 | """ |
| 1041 | 1027 | Stores a typedef inside self.entries array. |
| 1042 | 1028 | """ |
| 1043 | − | |
| 1044 | − typedef_type = r'((?:\s+[\w\*]+\b){0,7}\s+(?:\w+\b|\*+))\s*' | |
| 1029 | + # | |
| 1030 | + # We start by looking for function typedefs. | |
| 1031 | + # | |
| 1032 | + typedef_type = r'typedef((?:\s+[\w*]+\b){0,7}\s+(?:\w+\b|\*+))\s*' | |
| 1045 | 1033 | typedef_ident = r'\*?\s*(\w\S+)\s*' |
| 1046 | 1034 | typedef_args = r'\s*\((.*)\);' |
| 1047 | 1035 | |
| 1048 | − typedef1 = KernRe(r'typedef' + typedef_type + r'\(' + typedef_ident + r'\)' + typedef_args) | |
| 1049 | − typedef2 = KernRe(r'typedef' + typedef_type + typedef_ident + typedef_args) | |
| 1050 | − | |
| 1051 | − # Strip comments | |
| 1052 | − proto = KernRe(r'/\*.*?\*/', flags=re.S).sub('', proto) | |
| 1036 | + typedef1 = KernRe(typedef_type + r'\(' + typedef_ident + r'\)' + typedef_args) | |
| 1037 | + typedef2 = KernRe(typedef_type + typedef_ident + typedef_args) | |
| 1053 | 1038 | |
| 1054 | 1039 | # Parse function typedef prototypes |
| 1055 | 1040 | for r in [typedef1, typedef2]: |
| @@ -1065,21 +1050,16 @@ class KernelDoc: · 9 unmodified lines | ||
| 1065 | 1050 | f"expecting prototype for typedef {self.entry.identifier}. Prototype was for typedef {declaration_name} instead\n") |
| 1066 | 1051 | return |
| 1067 | 1052 | |
| 1068 | − decl_type = 'function' | |
| 1069 | − self.create_parameter_list(ln, decl_type, args, ',', declaration_name) | |
| 1053 | + self.create_parameter_list(ln, 'function', args, ',', declaration_name) | |
| 1070 | 1054 | |
| 1071 | − self.output_declaration(decl_type, declaration_name, | |
| 1055 | + self.output_declaration('function', declaration_name, | |
| 1072 | 1056 | typedef=True, |
| 1073 | 1057 | functiontype=return_type, |
| 1074 | 1058 | purpose=self.entry.declaration_purpose) |
| 1075 | 1059 | return |
| 1076 | − | |
| 1077 | − # Handle nested parentheses or brackets | |
| 1078 | − r = KernRe(r'(\(*.\)\s*|\[*.\]\s*);$') | |
| 1079 | − while r.search(proto): | |
| 1080 | − proto = r.sub('', proto) | |
| 1081 | − | |
| 1082 | − # Parse simple typedefs | |
| 1060 | + # | |
| 1061 | + # Not a function, try to parse a simple typedef. | |
| 1062 | + # | |
| 1083 | 1063 | r = KernRe(r'typedef.*\s+(\w+)\s*;') |
| 1084 | 1064 | if r.match(proto): |
| 1085 | 1065 | declaration_name = r.group(1) |
| @@ -1262,7 +1242,7 @@ class KernelDoc: · 176 unmodified lines | ||
| 1262 | 1242 | self.dump_section() |
| 1263 | 1243 | |
| 1264 | 1244 | # Look for doc_com + <text> + doc_end: |
| 1265 | − r = KernRe(r'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') | |
| 1245 | + r = KernRe(r'\s*\*\s*[a-zA-Z_0-9:.]+\*/') | |
| 1266 | 1246 | if r.match(line): |
| 1267 | 1247 | self.emit_msg(ln, f"suspicious ending line: {line}") |
| 1268 | 1248 | |
| @@ -1473,7 +1453,7 @@ class KernelDoc: · 204 unmodified lines | ||
| 1473 | 1453 | """Ancillary routine to process a function prototype""" |
| 1474 | 1454 | |
| 1475 | 1455 | # strip C99-style comments to end of line |
| 1476 | − line = KernRe(r"\/\/.*$", re.S).sub('', line) | |
| 1456 | + line = KernRe(r"//.*$", re.S).sub('', line) | |
| 1477 | 1457 | # |
| 1478 | 1458 | # Soak up the line's worth of prototype text, stopping at { or ; if present. |
| 1479 | 1459 | # |
| @@ -74,7 +74,7 @@ cd /etc/selinux/dummy/contexts/files · 73 unmodified lines | ||
| 74 | 74 | $SF -F file_contexts / |
| 75 | 75 | |
| 76 | 76 | mounts=`cat /proc/$$/mounts | \ |
| 77 | − grep -E "ext[234]|jfs|xfs|reiserfs|jffs2|gfs2|btrfs|f2fs|ocfs2" | \ | |
| 77 | + grep -E "ext[234]|jfs|xfs|jffs2|gfs2|btrfs|f2fs|ocfs2" | \ | |
| 78 | 78 | awk '{ print $2 '}` |
| 79 | 79 | $SF -F file_contexts $mounts |
| 80 | 80 | |
| @@ -0,0 +1,719 @@ | ||
| 1 | +#!/usr/bin/env python3 | |
| 2 | +# SPDX-License-Identifier: GPL-2.0 | |
| 3 | +# Copyright (C) 2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org> | |
| 4 | +# | |
| 5 | +# pylint: disable=R0902, R0912, R0913, R0914, R0915, R0917, C0103 | |
| 6 | +# | |
| 7 | +# Converted from docs Makefile and parallel-wrapper.sh, both under | |
| 8 | +# GPLv2, copyrighted since 2008 by the following authors: | |
| 9 | +# | |
| 10 | +# Akira Yokosawa <akiyks@gmail.com> | |
| 11 | +# Arnd Bergmann <arnd@arndb.de> | |
| 12 | +# Breno Leitao <leitao@debian.org> | |
| 13 | +# Carlos Bilbao <carlos.bilbao@amd.com> | |
| 14 | +# Dave Young <dyoung@redhat.com> | |
| 15 | +# Donald Hunter <donald.hunter@gmail.com> | |
| 16 | +# Geert Uytterhoeven <geert+renesas@glider.be> | |
| 17 | +# Jani Nikula <jani.nikula@intel.com> | |
| 18 | +# Jan Stancek <jstancek@redhat.com> | |
| 19 | +# Jonathan Corbet <corbet@lwn.net> | |
| 20 | +# Joshua Clayton <stillcompiling@gmail.com> | |
| 21 | +# Kees Cook <keescook@chromium.org> | |
| 22 | +# Linus Torvalds <torvalds@linux-foundation.org> | |
| 23 | +# Magnus Damm <damm+renesas@opensource.se> | |
| 24 | +# Masahiro Yamada <masahiroy@kernel.org> | |
| 25 | +# Mauro Carvalho Chehab <mchehab+huawei@kernel.org> | |
| 26 | +# Maxim Cournoyer <maxim.cournoyer@gmail.com> | |
| 27 | +# Peter Foley <pefoley2@pefoley.com> | |
| 28 | +# Randy Dunlap <rdunlap@infradead.org> | |
| 29 | +# Rob Herring <robh@kernel.org> | |
| 30 | +# Shuah Khan <shuahkh@osg.samsung.com> | |
| 31 | +# Thorsten Blum <thorsten.blum@toblux.com> | |
| 32 | +# Tomas Winkler <tomas.winkler@intel.com> | |
| 33 | + | |
| 34 | + | |
| 35 | +""" | |
| 36 | +Sphinx build wrapper that handles Kernel-specific business rules: | |
| 37 | + | |
| 38 | +- it gets the Kernel build environment vars; | |
| 39 | +- it determines what's the best parallelism; | |
| 40 | +- it handles SPHINXDIRS | |
| 41 | + | |
| 42 | +This tool ensures that MIN_PYTHON_VERSION is satisfied. If version is | |
| 43 | +below that, it seeks for a new Python version. If found, it re-runs using | |
| 44 | +the newer version. | |
| 45 | +""" | |
| 46 | + | |
| 47 | +import argparse | |
| 48 | +import locale | |
| 49 | +import os | |
| 50 | +import re | |
| 51 | +import shlex | |
| 52 | +import shutil | |
| 53 | +import subprocess | |
| 54 | +import sys | |
| 55 | + | |
| 56 | +from concurrent import futures | |
| 57 | +from glob import glob | |
| 58 | + | |
| 59 | +LIB_DIR = "lib" | |
| 60 | +SRC_DIR = os.path.dirname(os.path.realpath(__file__)) | |
| 61 | + | |
| 62 | +sys.path.insert(0, os.path.join(SRC_DIR, LIB_DIR)) | |
| 63 | + | |
| 64 | +from jobserver import JobserverExec # pylint: disable=C0413 | |
| 65 | + | |
| 66 | + | |
| 67 | +def parse_version(version): | |
| 68 | + """Convert a major.minor.patch version into a tuple""" | |
| 69 | + return tuple(int(x) for x in version.split(".")) | |
| 70 | + | |
| 71 | +def ver_str(version): | |
| 72 | + """Returns a version tuple as major.minor.patch""" | |
| 73 | + | |
| 74 | + return ".".join([str(x) for x in version]) | |
| 75 | + | |
| 76 | +# Minimal supported Python version needed by Sphinx and its extensions | |
| 77 | +MIN_PYTHON_VERSION = parse_version("3.7") | |
| 78 | + | |
| 79 | +# Default value for --venv parameter | |
| 80 | +VENV_DEFAULT = "sphinx_latest" | |
| 81 | + | |
| 82 | +# List of make targets and its corresponding builder and output directory | |
| 83 | +TARGETS = { | |
| 84 | + "cleandocs": { | |
| 85 | + "builder": "clean", | |
| 86 | + }, | |
| 87 | + "htmldocs": { | |
| 88 | + "builder": "html", | |
| 89 | + }, | |
| 90 | + "epubdocs": { | |
| 91 | + "builder": "epub", | |
| 92 | + "out_dir": "epub", | |
| 93 | + }, | |
| 94 | + "texinfodocs": { | |
| 95 | + "builder": "texinfo", | |
| 96 | + "out_dir": "texinfo", | |
| 97 | + }, | |
| 98 | + "infodocs": { | |
| 99 | + "builder": "texinfo", | |
| 100 | + "out_dir": "texinfo", | |
| 101 | + }, | |
| 102 | + "latexdocs": { | |
| 103 | + "builder": "latex", | |
| 104 | + "out_dir": "latex", | |
| 105 | + }, | |
| 106 | + "pdfdocs": { | |
| 107 | + "builder": "latex", | |
| 108 | + "out_dir": "latex", | |
| 109 | + }, | |
| 110 | + "xmldocs": { | |
| 111 | + "builder": "xml", | |
| 112 | + "out_dir": "xml", | |
| 113 | + }, | |
| 114 | + "linkcheckdocs": { | |
| 115 | + "builder": "linkcheck" | |
| 116 | + }, | |
| 117 | +} | |
| 118 | + | |
| 119 | +# Paper sizes. An empty value will pick the default | |
| 120 | +PAPER = ["", "a4", "letter"] | |
| 121 | + | |
| 122 | +class SphinxBuilder: | |
| 123 | + """ | |
| 124 | + Handles a sphinx-build target, adding needed arguments to build | |
| 125 | + with the Kernel. | |
| 126 | + """ | |
| 127 | + | |
| 128 | + def is_rust_enabled(self): | |
| 129 | + """Check if rust is enabled at .config""" | |
| 130 | + config_path = os.path.join(self.srctree, ".config") | |
| 131 | + if os.path.isfile(config_path): | |
| 132 | + with open(config_path, "r", encoding="utf-8") as f: | |
| 133 | + return "CONFIG_RUST=y" in f.read() | |
| 134 | + return False | |
| 135 | + | |
| 136 | + def get_path(self, path, abs_path=False): | |
| 137 | + """ | |
| 138 | + Ancillary routine to handle patches the right way, as shell does. | |
| 139 | + | |
| 140 | + It first expands "~" and "~user". Then, if patch is not absolute, | |
| 141 | + join self.srctree. Finally, if requested, convert to abspath. | |
| 142 | + """ | |
| 143 | + | |
| 144 | + path = os.path.expanduser(path) | |
| 145 | + if not path.startswith("/"): | |
| 146 | + path = os.path.join(self.srctree, path) | |
| 147 | + | |
| 148 | + if abs_path: | |
| 149 | + return os.path.abspath(path) | |
| 150 | + | |
| 151 | + return path | |
| 152 | + | |
| 153 | + def __init__(self, venv=None, verbose=False, n_jobs=None, interactive=None): | |
| 154 | + """Initialize internal variables""" | |
| 155 | + self.venv = venv | |
| 156 | + self.verbose = None | |
| 157 | + | |
| 158 | + # Normal variables passed from Kernel's makefile | |
| 159 | + self.kernelversion = os.environ.get("KERNELVERSION", "unknown") | |
| 160 | + self.kernelrelease = os.environ.get("KERNELRELEASE", "unknown") | |
| 161 | + self.pdflatex = os.environ.get("PDFLATEX", "xelatex") | |
| 162 | + | |
| 163 | + if not interactive: | |
| 164 | + self.latexopts = os.environ.get("LATEXOPTS", "-interaction=batchmode -no-shell-escape") | |
| 165 | + else: | |
| 166 | + self.latexopts = os.environ.get("LATEXOPTS", "") | |
| 167 | + | |
| 168 | + if not verbose: | |
| 169 | + verbose = bool(os.environ.get("KBUILD_VERBOSE", "") != "") | |
| 170 | + | |
| 171 | + # Handle SPHINXOPTS evironment | |
| 172 | + sphinxopts = shlex.split(os.environ.get("SPHINXOPTS", "")) | |
| 173 | + | |
| 174 | + # As we handle number of jobs and quiet in separate, we need to pick | |
| 175 | + # it the same way as sphinx-build would pick, so let's use argparse | |
| 176 | + # do to the right argument expansion | |
| 177 | + parser = argparse.ArgumentParser() | |
| 178 | + parser.add_argument('-j', '--jobs', type=int) | |
| 179 | + parser.add_argument('-q', '--quiet', type=int) | |
| 180 | + | |
| 181 | + # Other sphinx-build arguments go as-is, so place them | |
| 182 | + # at self.sphinxopts | |
| 183 | + sphinx_args, self.sphinxopts = parser.parse_known_args(sphinxopts) | |
| 184 | + if sphinx_args.quiet == True: | |
| 185 | + self.verbose = False | |
| 186 | + | |
| 187 | + if sphinx_args.jobs: | |
| 188 | + self.n_jobs = sphinx_args.jobs | |
| 189 | + | |
| 190 | + # Command line arguments was passed, override SPHINXOPTS | |
| 191 | + if verbose is not None: | |
| 192 | + self.verbose = verbose | |
| 193 | + | |
| 194 | + self.n_jobs = n_jobs | |
| 195 | + | |
| 196 | + # Source tree directory. This needs to be at os.environ, as | |
| 197 | + # Sphinx extensions and media uAPI makefile needs it | |
| 198 | + self.srctree = os.environ.get("srctree") | |
| 199 | + if not self.srctree: | |
| 200 | + self.srctree = "." | |
| 201 | + os.environ["srctree"] = self.srctree | |
| 202 | + | |
| 203 | + # Now that we can expand srctree, get other directories as well | |
| 204 | + self.sphinxbuild = os.environ.get("SPHINXBUILD", "sphinx-build") | |
| 205 | + self.kerneldoc = self.get_path(os.environ.get("KERNELDOC", | |
| 206 | + "scripts/kernel-doc.py")) | |
| 207 | + self.obj = os.environ.get("obj", "Documentation") | |
| 208 | + self.builddir = self.get_path(os.path.join(self.obj, "output"), | |
| 209 | + abs_path=True) | |
| 210 | + | |
| 211 | + # Media uAPI needs it | |
| 212 | + os.environ["BUILDDIR"] = self.builddir | |
| 213 | + | |
| 214 | + # Detect if rust is enabled | |
| 215 | + self.config_rust = self.is_rust_enabled() | |
| 216 | + | |
| 217 | + # Get directory locations for LaTeX build toolchain | |
| 218 | + self.pdflatex_cmd = shutil.which(self.pdflatex) | |
| 219 | + self.latexmk_cmd = shutil.which("latexmk") | |
| 220 | + | |
| 221 | + self.env = os.environ.copy() | |
| 222 | + | |
| 223 | + # If venv parameter is specified, run Sphinx from venv | |
| 224 | + if venv: | |
| 225 | + bin_dir = os.path.join(venv, "bin") | |
| 226 | + if os.path.isfile(os.path.join(bin_dir, "activate")): | |
| 227 | + # "activate" virtual env | |
| 228 | + self.env["PATH"] = bin_dir + ":" + self.env["PATH"] | |
| 229 | + self.env["VIRTUAL_ENV"] = venv | |
| 230 | + if "PYTHONHOME" in self.env: | |
| 231 | + del self.env["PYTHONHOME"] | |
| 232 | + print(f"Setting venv to {venv}") | |
| 233 | + else: | |
| 234 | + sys.exit(f"Venv {venv} not found.") | |
| 235 | + | |
| 236 | + def run_sphinx(self, sphinx_build, build_args, *args, **pwargs): | |
| 237 | + """ | |
| 238 | + Executes sphinx-build using current python3 command and setting | |
| 239 | + -j parameter if possible to run the build in parallel. | |
| 240 | + """ | |
| 241 | + | |
| 242 | + with JobserverExec() as jobserver: | |
| 243 | + if jobserver.claim: | |
| 244 | + n_jobs = str(jobserver.claim) | |
| 245 | + else: | |
| 246 | + n_jobs = "auto" # Supported since Sphinx 1.7 | |
| 247 | + | |
| 248 | + cmd = [] | |
| 249 | + | |
| 250 | + if self.venv: | |
| 251 | + cmd.append("python") | |
| 252 | + else: | |
| 253 | + cmd.append(sys.executable) | |
| 254 | + | |
| 255 | + cmd.append(sphinx_build) | |
| 256 | + | |
| 257 | + # if present, SPHINXOPTS or command line --jobs overrides default | |
| 258 | + if self.n_jobs: | |
| 259 | + n_jobs = str(self.n_jobs) | |
| 260 | + | |
| 261 | + if n_jobs: | |
| 262 | + cmd += [f"-j{n_jobs}"] | |
| 263 | + | |
| 264 | + if not self.verbose: | |
| 265 | + cmd.append("-q") | |
| 266 | + | |
| 267 | + cmd += self.sphinxopts | |
| 268 | + | |
| 269 | + cmd += build_args | |
| 270 | + | |
| 271 | + if self.verbose: | |
| 272 | + print(" ".join(cmd)) | |
| 273 | + | |
| 274 | + rc = subprocess.call(cmd, *args, **pwargs) | |
| 275 | + | |
| 276 | + def handle_html(self, css, output_dir): | |
| 277 | + """ | |
| 278 | + Extra steps for HTML and epub output. | |
| 279 | + | |
| 280 | + For such targets, we need to ensure that CSS will be properly | |
| 281 | + copied to the output _static directory | |
| 282 | + """ | |
| 283 | + | |
| 284 | + if not css: | |
| 285 | + return | |
| 286 | + | |
| 287 | + css = os.path.expanduser(css) | |
| 288 | + if not css.startswith("/"): | |
| 289 | + css = os.path.join(self.srctree, css) | |
| 290 | + | |
| 291 | + static_dir = os.path.join(output_dir, "_static") | |
| 292 | + os.makedirs(static_dir, exist_ok=True) | |
| 293 | + | |
| 294 | + try: | |
| 295 | + shutil.copy2(css, static_dir) | |
| 296 | + except (OSError, IOError) as e: | |
| 297 | + print(f"Warning: Failed to copy CSS: {e}", file=sys.stderr) | |
| 298 | + | |
| 299 | + def build_pdf_file(self, latex_cmd, from_dir, path): | |
| 300 | + """Builds a single pdf file using latex_cmd""" | |
| 301 | + try: | |
| 302 | + subprocess.run(latex_cmd + [path], | |
| 303 | + cwd=from_dir, check=True) | |
| 304 | + | |
| 305 | + return True | |
| 306 | + except subprocess.CalledProcessError: | |
| 307 | + # LaTeX PDF error code is almost useless: it returns | |
| 308 | + # error codes even when build succeeds but has warnings. | |
| 309 | + # So, we'll ignore the results | |
| 310 | + return False | |
| 311 | + | |
| 312 | + def pdf_parallel_build(self, tex_suffix, latex_cmd, tex_files, n_jobs): | |
| 313 | + """Build PDF files in parallel if possible""" | |
| 314 | + builds = {} | |
| 315 | + build_failed = False | |
| 316 | + max_len = 0 | |
| 317 | + has_tex = False | |
| 318 | + | |
| 319 | + # Process files in parallel | |
| 320 | + with futures.ThreadPoolExecutor(max_workers=n_jobs) as executor: | |
| 321 | + jobs = {} | |
| 322 | + | |
| 323 | + for from_dir, pdf_dir, entry in tex_files: | |
| 324 | + name = entry.name | |
| 325 | + | |
| 326 | + if not name.endswith(tex_suffix): | |
| 327 | + continue | |
| 328 | + | |
| 329 | + name = name[:-len(tex_suffix)] | |
| 330 | + | |
| 331 | + max_len = max(max_len, len(name)) | |
| 332 | + | |
| 333 | + has_tex = True | |
| 334 | + | |
| 335 | + future = executor.submit(self.build_pdf_file, latex_cmd, | |
| 336 | + from_dir, entry.path) | |
| 337 | + jobs[future] = (from_dir, name, entry.path) | |
| 338 | + | |
| 339 | + for future in futures.as_completed(jobs): | |
| 340 | + from_dir, name, path = jobs[future] | |
| 341 | + | |
| 342 | + pdf_name = name + ".pdf" | |
| 343 | + pdf_from = os.path.join(from_dir, pdf_name) | |
| 344 | + | |
| 345 | + try: | |
| 346 | + success = future.result() | |
| 347 | + | |
| 348 | + if success and os.path.exists(pdf_from): | |
| 349 | + pdf_to = os.path.join(pdf_dir, pdf_name) | |
| 350 | + | |
| 351 | + os.rename(pdf_from, pdf_to) | |
| 352 | + builds[name] = os.path.relpath(pdf_to, self.builddir) | |
| 353 | + else: | |
| 354 | + builds[name] = "FAILED" | |
| 355 | + build_failed = True | |
| 356 | + except Exception as e: | |
| 357 | + builds[name] = f"FAILED ({str(e)})" | |
| 358 | + build_failed = True | |
| 359 | + | |
| 360 | + # Handle case where no .tex files were found | |
| 361 | + if not has_tex: | |
| 362 | + name = "Sphinx LaTeX builder" | |
| 363 | + max_len = max(max_len, len(name)) | |
| 364 | + builds[name] = "FAILED (no .tex file was generated)" | |
| 365 | + build_failed = True | |
| 366 | + | |
| 367 | + return builds, build_failed, max_len | |
| 368 | + | |
| 369 | + def handle_pdf(self, output_dirs): | |
| 370 | + """ | |
| 371 | + Extra steps for PDF output. | |
| 372 | + | |
| 373 | + As PDF is handled via a LaTeX output, after building the .tex file, | |
| 374 | + a new build is needed to create the PDF output from the latex | |
| 375 | + directory. | |
| 376 | + """ | |
| 377 | + builds = {} | |
| 378 | + max_len = 0 | |
| 379 | + tex_suffix = ".tex" | |
| 380 | + | |
| 381 | + # Get all tex files that will be used for PDF build | |
| 382 | + tex_files = [] | |
| 383 | + for from_dir in output_dirs: | |
| 384 | + pdf_dir = os.path.join(from_dir, "../pdf") | |
| 385 | + os.makedirs(pdf_dir, exist_ok=True) | |
| 386 | + | |
| 387 | + if self.latexmk_cmd: | |
| 388 | + latex_cmd = [self.latexmk_cmd, f"-{self.pdflatex}"] | |
| 389 | + else: | |
| 390 | + latex_cmd = [self.pdflatex] | |
| 391 | + | |
| 392 | + latex_cmd.extend(shlex.split(self.latexopts)) | |
| 393 | + | |
| 394 | + # Get a list of tex files to process | |
| 395 | + with os.scandir(from_dir) as it: | |
| 396 | + for entry in it: | |
| 397 | + if entry.name.endswith(tex_suffix): | |
| 398 | + tex_files.append((from_dir, pdf_dir, entry)) | |
| 399 | + | |
| 400 | + # When using make, this won't be used, as the number of jobs comes | |
| 401 | + # from POSIX jobserver. So, this covers the case where build comes | |
| 402 | + # from command line. On such case, serialize by default, except if | |
| 403 | + # the user explicitly sets the number of jobs. | |
| 404 | + n_jobs = 1 | |
| 405 | + | |
| 406 | + # n_jobs is either an integer or "auto". Only use it if it is a number | |
| 407 | + if self.n_jobs: | |
| 408 | + try: | |
| 409 | + n_jobs = int(self.n_jobs) | |
| 410 | + except ValueError: | |
| 411 | + pass | |
| 412 | + | |
| 413 | + # When using make, jobserver.claim is the number of jobs that were | |
| 414 | + # used with "-j" and that aren't used by other make targets | |
| 415 | + with JobserverExec() as jobserver: | |
| 416 | + n_jobs = 1 | |
| 417 | + | |
| 418 | + # Handle the case when a parameter is passed via command line, | |
| 419 | + # using it as default, if jobserver doesn't claim anything | |
| 420 | + if self.n_jobs: | |
| 421 | + try: | |
| 422 | + n_jobs = int(self.n_jobs) | |
| 423 | + except ValueError: | |
| 424 | + pass | |
| 425 | + | |
| 426 | + if jobserver.claim: | |
| 427 | + n_jobs = jobserver.claim | |
| 428 | + | |
| 429 | + # Build files in parallel | |
| 430 | + builds, build_failed, max_len = self.pdf_parallel_build(tex_suffix, | |
| 431 | + latex_cmd, | |
| 432 | + tex_files, | |
| 433 | + n_jobs) | |
| 434 | + | |
| 435 | + msg = "Summary" | |
| 436 | + msg += "\n" + "=" * len(msg) | |
| 437 | + print() | |
| 438 | + print(msg) | |
| 439 | + | |
| 440 | + for pdf_name, pdf_file in builds.items(): | |
| 441 | + print(f"{pdf_name:<{max_len}}: {pdf_file}") | |
| 442 | + | |
| 443 | + print() | |
| 444 | + | |
| 445 | + # return an error if a PDF file is missing | |
| 446 | + | |
| 447 | + if build_failed: | |
| 448 | + sys.exit(f"PDF build failed: not all PDF files were created.") | |
| 449 | + else: | |
| 450 | + print("All PDF files were built.") | |
| 451 | + | |
| 452 | + def handle_info(self, output_dirs): | |
| 453 | + """ | |
| 454 | + Extra steps for Info output. | |
| 455 | + | |
| 456 | + For texinfo generation, an additional make is needed from the | |
| 457 | + texinfo directory. | |
| 458 | + """ | |
| 459 | + | |
| 460 | + for output_dir in output_dirs: | |
| 461 | + try: | |
| 462 | + subprocess.run(["make", "info"], cwd=output_dir, check=True) | |
| 463 | + except subprocess.CalledProcessError as e: | |
| 464 | + sys.exit(f"Error generating info docs: {e}") | |
| 465 | + | |
| 466 | + def cleandocs(self, builder): | |
| 467 | + | |
| 468 | + shutil.rmtree(self.builddir, ignore_errors=True) | |
| 469 | + | |
| 470 | + def build(self, target, sphinxdirs=None, conf="conf.py", | |
| 471 | + theme=None, css=None, paper=None): | |
| 472 | + """ | |
| 473 | + Build documentation using Sphinx. This is the core function of this | |
| 474 | + module. It prepares all arguments required by sphinx-build. | |
| 475 | + """ | |
| 476 | + | |
| 477 | + builder = TARGETS[target]["builder"] | |
| 478 | + out_dir = TARGETS[target].get("out_dir", "") | |
| 479 | + | |
| 480 | + # Cleandocs doesn't require sphinx-build | |
| 481 | + if target == "cleandocs": | |
| 482 | + self.cleandocs(builder) | |
| 483 | + return | |
| 484 | + | |
| 485 | + # Other targets require sphinx-build | |
| 486 | + sphinxbuild = shutil.which(self.sphinxbuild, path=self.env["PATH"]) | |
| 487 | + if not sphinxbuild: | |
| 488 | + sys.exit(f"Error: {self.sphinxbuild} not found in PATH.\n") | |
| 489 | + | |
| 490 | + if builder == "latex": | |
| 491 | + if not self.pdflatex_cmd and not self.latexmk_cmd: | |
| 492 | + sys.exit("Error: pdflatex or latexmk required for PDF generation") | |
| 493 | + | |
| 494 | + docs_dir = os.path.abspath(os.path.join(self.srctree, "Documentation")) | |
| 495 | + | |
| 496 | + # Prepare base arguments for Sphinx build | |
| 497 | + kerneldoc = self.kerneldoc | |
| 498 | + if kerneldoc.startswith(self.srctree): | |
| 499 | + kerneldoc = os.path.relpath(kerneldoc, self.srctree) | |
| 500 | + | |
| 501 | + # Prepare common Sphinx options | |
| 502 | + args = [ | |
| 503 | + "-b", builder, | |
| 504 | + "-c", docs_dir, | |
| 505 | + ] | |
| 506 | + | |
| 507 | + if builder == "latex": | |
| 508 | + if not paper: | |
| 509 | + paper = PAPER[1] | |
| 510 | + | |
| 511 | + args.extend(["-D", f"latex_elements.papersize={paper}paper"]) | |
| 512 | + | |
| 513 | + if self.config_rust: | |
| 514 | + args.extend(["-t", "rustdoc"]) | |
| 515 | + | |
| 516 | + if conf: | |
| 517 | + self.env["SPHINX_CONF"] = self.get_path(conf, abs_path=True) | |
| 518 | + | |
| 519 | + if not sphinxdirs: | |
| 520 | + sphinxdirs = os.environ.get("SPHINXDIRS", ".") | |
| 521 | + | |
| 522 | + # The sphinx-build tool has a bug: internally, it tries to set | |
| 523 | + # locale with locale.setlocale(locale.LC_ALL, ''). This causes a | |
| 524 | + # crash if language is not set. Detect and fix it. | |
| 525 | + try: | |
| 526 | + locale.setlocale(locale.LC_ALL, '') | |
| 527 | + except Exception: | |
| 528 | + self.env["LC_ALL"] = "C" | |
| 529 | + self.env["LANG"] = "C" | |
| 530 | + | |
| 531 | + # sphinxdirs can be a list or a whitespace-separated string | |
| 532 | + sphinxdirs_list = [] | |
| 533 | + for sphinxdir in sphinxdirs: | |
| 534 | + if isinstance(sphinxdir, list): | |
| 535 | + sphinxdirs_list += sphinxdir | |
| 536 | + else: | |
| 537 | + for name in sphinxdir.split(" "): | |
| 538 | + sphinxdirs_list.append(name) | |
| 539 | + | |
| 540 | + # Build each directory | |
| 541 | + output_dirs = [] | |
| 542 | + for sphinxdir in sphinxdirs_list: | |
| 543 | + src_dir = os.path.join(docs_dir, sphinxdir) | |
| 544 | + doctree_dir = os.path.join(self.builddir, ".doctrees") | |
| 545 | + output_dir = os.path.join(self.builddir, sphinxdir, out_dir) | |
| 546 | + | |
| 547 | + # Make directory names canonical | |
| 548 | + src_dir = os.path.normpath(src_dir) | |
| 549 | + doctree_dir = os.path.normpath(doctree_dir) | |
| 550 | + output_dir = os.path.normpath(output_dir) | |
| 551 | + | |
| 552 | + os.makedirs(doctree_dir, exist_ok=True) | |
| 553 | + os.makedirs(output_dir, exist_ok=True) | |
| 554 | + | |
| 555 | + output_dirs.append(output_dir) | |
| 556 | + | |
| 557 | + build_args = args + [ | |
| 558 | + "-d", doctree_dir, | |
| 559 | + "-D", f"kerneldoc_bin={kerneldoc}", | |
| 560 | + "-D", f"version={self.kernelversion}", | |
| 561 | + "-D", f"release={self.kernelrelease}", | |
| 562 | + "-D", f"kerneldoc_srctree={self.srctree}", | |
| 563 | + src_dir, | |
| 564 | + output_dir, | |
| 565 | + ] | |
| 566 | + | |
| 567 | + # Execute sphinx-build | |
| 568 | + try: | |
| 569 | + self.run_sphinx(sphinxbuild, build_args, env=self.env) | |
| 570 | + except Exception as e: | |
| 571 | + sys.exit(f"Build failed: {e}") | |
| 572 | + | |
| 573 | + # Ensure that html/epub will have needed static files | |
| 574 | + if target in ["htmldocs", "epubdocs"]: | |
| 575 | + self.handle_html(css, output_dir) | |
| 576 | + | |
| 577 | + # PDF and Info require a second build step | |
| 578 | + if target == "pdfdocs": | |
| 579 | + self.handle_pdf(output_dirs) | |
| 580 | + elif target == "infodocs": | |
| 581 | + self.handle_info(output_dirs) | |
| 582 | + | |
| 583 | + @staticmethod | |
| 584 | + def get_python_version(cmd): | |
| 585 | + """ | |
| 586 | + Get python version from a Python binary. As we need to detect if | |
| 587 | + are out there newer python binaries, we can't rely on sys.release here. | |
| 588 | + """ | |
| 589 | + | |
| 590 | + result = subprocess.run([cmd, "--version"], check=True, | |
| 591 | + stdout=subprocess.PIPE, stderr=subprocess.PIPE, | |
| 592 | + universal_newlines=True) | |
| 593 | + version = result.stdout.strip() | |
| 594 | + | |
| 595 | + match = re.search(r"(\d+\.\d+\.\d+)", version) | |
| 596 | + if match: | |
| 597 | + return parse_version(match.group(1)) | |
| 598 | + | |
| 599 | + print(f"Can't parse version {version}") | |
| 600 | + return (0, 0, 0) | |
| 601 | + | |
| 602 | + @staticmethod | |
| 603 | + def find_python(): | |
| 604 | + """ | |
| 605 | + Detect if are out there any python 3.xy version newer than the | |
| 606 | + current one. | |
| 607 | + | |
| 608 | + Note: this routine is limited to up to 2 digits for python3. We | |
| 609 | + may need to update it one day, hopefully on a distant future. | |
| 610 | + """ | |
| 611 | + patterns = [ | |
| 612 | + "python3.[0-9]", | |
| 613 | + "python3.[0-9][0-9]", | |
| 614 | + ] | |
| 615 | + | |
| 616 | + # Seek for a python binary newer than MIN_PYTHON_VERSION | |
| 617 | + for path in os.getenv("PATH", "").split(":"): | |
| 618 | + for pattern in patterns: | |
| 619 | + for cmd in glob(os.path.join(path, pattern)): | |
| 620 | + if os.path.isfile(cmd) and os.access(cmd, os.X_OK): | |
| 621 | + version = SphinxBuilder.get_python_version(cmd) | |
| 622 | + if version >= MIN_PYTHON_VERSION: | |
| 623 | + return cmd | |
| 624 | + | |
| 625 | + return None | |
| 626 | + | |
| 627 | + @staticmethod | |
| 628 | + def check_python(): | |
| 629 | + """ | |
| 630 | + Check if the current python binary satisfies our minimal requirement | |
| 631 | + for Sphinx build. If not, re-run with a newer version if found. | |
| 632 | + """ | |
| 633 | + cur_ver = sys.version_info[:3] | |
| 634 | + if cur_ver >= MIN_PYTHON_VERSION: | |
| 635 | + return | |
| 636 | + | |
| 637 | + python_ver = ver_str(cur_ver) | |
| 638 | + | |
| 639 | + new_python_cmd = SphinxBuilder.find_python() | |
| 640 | + if not new_python_cmd: | |
| 641 | + sys.exit(f"Python version {python_ver} is not supported anymore.") | |
| 642 | + | |
| 643 | + # Restart script using the newer version | |
| 644 | + script_path = os.path.abspath(sys.argv[0]) | |
| 645 | + args = [new_python_cmd, script_path] + sys.argv[1:] | |
| 646 | + | |
| 647 | + print(f"Python {python_ver} not supported. Changing to {new_python_cmd}") | |
| 648 | + | |
| 649 | + try: | |
| 650 | + os.execv(new_python_cmd, args) | |
| 651 | + except OSError as e: | |
| 652 | + sys.exit(f"Failed to restart with {new_python_cmd}: {e}") | |
| 653 | + | |
| 654 | +def jobs_type(value): | |
| 655 | + """ | |
| 656 | + Handle valid values for -j. Accepts Sphinx "-jauto", plus a number | |
| 657 | + equal or bigger than one. | |
| 658 | + """ | |
| 659 | + if value is None: | |
| 660 | + return None | |
| 661 | + | |
| 662 | + if value.lower() == 'auto': | |
| 663 | + return value.lower() | |
| 664 | + | |
| 665 | + try: | |
| 666 | + if int(value) >= 1: | |
| 667 | + return value | |
| 668 | + | |
| 669 | + raise argparse.ArgumentTypeError(f"Minimum jobs is 1, got {value}") | |
| 670 | + except ValueError: | |
| 671 | + raise argparse.ArgumentTypeError(f"Must be 'auto' or positive integer, got {value}") | |
| 672 | + | |
| 673 | +def main(): | |
| 674 | + """ | |
| 675 | + Main function. The only mandatory argument is the target. If not | |
| 676 | + specified, the other arguments will use default values if not | |
| 677 | + specified at os.environ. | |
| 678 | + """ | |
| 679 | + parser = argparse.ArgumentParser(description="Kernel documentation builder") | |
| 680 | + | |
| 681 | + parser.add_argument("target", choices=list(TARGETS.keys()), | |
| 682 | + help="Documentation target to build") | |
| 683 | + parser.add_argument("--sphinxdirs", nargs="+", | |
| 684 | + help="Specific directories to build") | |
| 685 | + parser.add_argument("--conf", default="conf.py", | |
| 686 | + help="Sphinx configuration file") | |
| 687 | + | |
| 688 | + parser.add_argument("--theme", help="Sphinx theme to use") | |
| 689 | + | |
| 690 | + parser.add_argument("--css", help="Custom CSS file for HTML/EPUB") | |
| 691 | + | |
| 692 | + parser.add_argument("--paper", choices=PAPER, default=PAPER[0], | |
| 693 | + help="Paper size for LaTeX/PDF output") | |
| 694 | + | |
| 695 | + parser.add_argument("-v", "--verbose", action='store_true', | |
| 696 | + help="place build in verbose mode") | |
| 697 | + | |
| 698 | + parser.add_argument('-j', '--jobs', type=jobs_type, | |
| 699 | + help="Sets number of jobs to use with sphinx-build") | |
| 700 | + | |
| 701 | + parser.add_argument('-i', '--interactive', action='store_true', | |
| 702 | + help="Change latex default to run in interactive mode") | |
| 703 | + | |
| 704 | + parser.add_argument("-V", "--venv", nargs='?', const=f'{VENV_DEFAULT}', | |
| 705 | + default=None, | |
| 706 | + help=f'If used, run Sphinx from a venv dir (default dir: {VENV_DEFAULT})') | |
| 707 | + | |
| 708 | + args = parser.parse_args() | |
| 709 | + | |
| 710 | + SphinxBuilder.check_python() | |
| 711 | + | |
| 712 | + builder = SphinxBuilder(venv=args.venv, verbose=args.verbose, | |
| 713 | + n_jobs=args.jobs, interactive=args.interactive) | |
| 714 | + | |
| 715 | + builder.build(args.target, sphinxdirs=args.sphinxdirs, conf=args.conf, | |
| 716 | + theme=args.theme, css=args.css, paper=args.paper) | |
| 717 | + | |
| 718 | +if __name__ == "__main__": | |
| 719 | + main() | |
| @@ -1,1056 +1,1621 @@ | ||
| 1 | −#!/usr/bin/env perl | |
| 1 | +#!/usr/bin/env python3 | |
| 2 | 2 | # SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | −use strict; | |
| 4 | − | |
| 5 | −# Copyright (c) 2017-2020 Mauro Carvalho Chehab <mchehab@kernel.org> | |
| 6 | −# | |
| 7 | − | |
| 8 | −my $prefix = "./"; | |
| 9 | −$prefix = "$ENV{'srctree'}/" if ($ENV{'srctree'}); | |
| 10 | − | |
| 11 | −my $conf = $prefix . "Documentation/conf.py"; | |
| 12 | −my $requirement_file = $prefix . "Documentation/sphinx/requirements.txt"; | |
| 13 | −my $virtenv_prefix = "sphinx_"; | |
| 14 | − | |
| 15 | −# | |
| 16 | −# Static vars | |
| 17 | −# | |
| 18 | − | |
| 19 | −my %missing; | |
| 20 | −my $system_release; | |
| 21 | −my $need = 0; | |
| 22 | −my $optional = 0; | |
| 23 | −my $need_symlink = 0; | |
| 24 | −my $need_sphinx = 0; | |
| 25 | −my $need_pip = 0; | |
| 26 | −my $need_virtualenv = 0; | |
| 27 | −my $rec_sphinx_upgrade = 0; | |
| 28 | −my $verbose_warn_install = 1; | |
| 29 | −my $install = ""; | |
| 30 | −my $virtenv_dir = ""; | |
| 31 | −my $python_cmd = ""; | |
| 32 | −my $activate_cmd; | |
| 33 | −my $min_version; | |
| 34 | −my $cur_version; | |
| 35 | −my $rec_version = "3.4.3"; | |
| 36 | −my $latest_avail_ver; | |
| 37 | − | |
| 38 | −# | |
| 39 | −# Command line arguments | |
| 40 | −# | |
| 41 | − | |
| 42 | −my $pdf = 1; | |
| 43 | −my $virtualenv = 1; | |
| 44 | −my $version_check = 0; | |
| 45 | − | |
| 46 | −# | |
| 47 | −# List of required texlive packages on Fedora and OpenSuse | |
| 48 | −# | |
| 49 | − | |
| 50 | −my %texlive = ( | |
| 51 | − 'amsfonts.sty' => 'texlive-amsfonts', | |
| 52 | − 'amsmath.sty' => 'texlive-amsmath', | |
| 53 | − 'amssymb.sty' => 'texlive-amsfonts', | |
| 54 | − 'amsthm.sty' => 'texlive-amscls', | |
| 55 | − 'anyfontsize.sty' => 'texlive-anyfontsize', | |
| 56 | − 'atbegshi.sty' => 'texlive-oberdiek', | |
| 57 | − 'bm.sty' => 'texlive-tools', | |
| 58 | − 'capt-of.sty' => 'texlive-capt-of', | |
| 59 | − 'cmap.sty' => 'texlive-cmap', | |
| 60 | − 'ecrm1000.tfm' => 'texlive-ec', | |
| 61 | − 'eqparbox.sty' => 'texlive-eqparbox', | |
| 62 | − 'eu1enc.def' => 'texlive-euenc', | |
| 63 | − 'fancybox.sty' => 'texlive-fancybox', | |
| 64 | − 'fancyvrb.sty' => 'texlive-fancyvrb', | |
| 65 | − 'float.sty' => 'texlive-float', | |
| 66 | − 'fncychap.sty' => 'texlive-fncychap', | |
| 67 | − 'footnote.sty' => 'texlive-mdwtools', | |
| 68 | − 'framed.sty' => 'texlive-framed', | |
| 69 | − 'luatex85.sty' => 'texlive-luatex85', | |
| 70 | − 'multirow.sty' => 'texlive-multirow', | |
| 71 | − 'needspace.sty' => 'texlive-needspace', | |
| 72 | − 'palatino.sty' => 'texlive-psnfss', | |
| 73 | − 'parskip.sty' => 'texlive-parskip', | |
| 74 | − 'polyglossia.sty' => 'texlive-polyglossia', | |
| 75 | − 'tabulary.sty' => 'texlive-tabulary', | |
| 76 | − 'threeparttable.sty' => 'texlive-threeparttable', | |
| 77 | − 'titlesec.sty' => 'texlive-titlesec', | |
| 78 | − 'ucs.sty' => 'texlive-ucs', | |
| 79 | − 'upquote.sty' => 'texlive-upquote', | |
| 80 | − 'wrapfig.sty' => 'texlive-wrapfig', | |
| 81 | − 'ctexhook.sty' => 'texlive-ctex', | |
| 82 | −); | |
| 83 | − | |
| 84 | −# | |
| 85 | −# Subroutines that checks if a feature exists | |
| 86 | −# | |
| 87 | − | |
| 88 | −sub check_missing(%) | |
| 89 | −{ | |
| 90 | − my %map = %{$_[0]}; | |
| 91 | − | |
| 92 | − foreach my $prog (sort keys %missing) { | |
| 93 | − my $is_optional = $missing{$prog}; | |
| 94 | − | |
| 95 | − # At least on some LTS distros like CentOS 7, texlive doesn't | |
| 96 | − # provide all packages we need. When such distros are | |
| 97 | − # detected, we have to disable PDF output. | |
| 98 | − # | |
| 99 | − # So, we need to ignore the packages that distros would | |
| 100 | − # need for LaTeX to work | |
| 101 | − if ($is_optional == 2 && !$pdf) { | |
| 102 | − $optional--; | |
| 103 | − next; | |
| 104 | − } | |
| 105 | − | |
| 106 | − if ($verbose_warn_install) { | |
| 107 | − if ($is_optional) { | |
| 108 | − print "Warning: better to also install \"$prog\".\n"; | |
| 109 | − } else { | |
| 110 | − print "ERROR: please install \"$prog\", otherwise, build won't work.\n"; | |
| 111 | − } | |
| 112 | − } | |
| 113 | − if (defined($map{$prog})) { | |
| 114 | − $install .= " " . $map{$prog}; | |
| 115 | − } else { | |
| 116 | − $install .= " " . $prog; | |
| 117 | − } | |
| 118 | − } | |
| 119 | − | |
| 120 | − $install =~ s/^\s//; | |
| 121 | −} | |
| 122 | − | |
| 123 | −sub add_package($$) | |
| 124 | −{ | |
| 125 | − my $package = shift; | |
| 126 | − my $is_optional = shift; | |
| 127 | − | |
| 128 | − $missing{$package} = $is_optional; | |
| 129 | − if ($is_optional) { | |
| 130 | − $optional++; | |
| 131 | − } else { | |
| 132 | − $need++; | |
| 133 | − } | |
| 134 | −} | |
| 135 | − | |
| 136 | −sub check_missing_file($$$) | |
| 137 | −{ | |
| 138 | − my $files = shift; | |
| 139 | − my $package = shift; | |
| 140 | − my $is_optional = shift; | |
| 141 | − | |
| 142 | − for (@$files) { | |
| 143 | − return if(-e $_); | |
| 144 | − } | |
| 145 | − | |
| 146 | − add_package($package, $is_optional); | |
| 147 | −} | |
| 148 | − | |
| 149 | −sub findprog($) | |
| 150 | −{ | |
| 151 | − foreach(split(/:/, $ENV{PATH})) { | |
| 152 | − return "$_/$_[0]" if(-x "$_/$_[0]"); | |
| 153 | − } | |
| 154 | −} | |
| 155 | − | |
| 156 | −sub find_python_no_venv() | |
| 157 | −{ | |
| 158 | − my $prog = shift; | |
| 159 | − | |
| 160 | − my $cur_dir = qx(pwd); | |
| 161 | − $cur_dir =~ s/\s+$//; | |
| 162 | − | |
| 163 | − foreach my $dir (split(/:/, $ENV{PATH})) { | |
| 164 | − next if ($dir =~ m,($cur_dir)/sphinx,); | |
| 165 | − return "$dir/python3" if(-x "$dir/python3"); | |
| 166 | − } | |
| 167 | − foreach my $dir (split(/:/, $ENV{PATH})) { | |
| 168 | − next if ($dir =~ m,($cur_dir)/sphinx,); | |
| 169 | − return "$dir/python" if(-x "$dir/python"); | |
| 170 | − } | |
| 171 | − return "python"; | |
| 172 | −} | |
| 173 | − | |
| 174 | −sub check_program($$) | |
| 175 | −{ | |
| 176 | − my $prog = shift; | |
| 177 | − my $is_optional = shift; | |
| 178 | − | |
| 179 | − return $prog if findprog($prog); | |
| 180 | − | |
| 181 | − add_package($prog, $is_optional); | |
| 182 | −} | |
| 183 | − | |
| 184 | −sub check_perl_module($$) | |
| 185 | −{ | |
| 186 | − my $prog = shift; | |
| 187 | − my $is_optional = shift; | |
| 188 | − | |
| 189 | − my $err = system("perl -M$prog -e 1 2>/dev/null /dev/null"); | |
| 190 | − return if ($err == 0); | |
| 191 | − | |
| 192 | − add_package($prog, $is_optional); | |
| 193 | −} | |
| 194 | − | |
| 195 | −sub check_python_module($$) | |
| 196 | −{ | |
| 197 | − my $prog = shift; | |
| 198 | − my $is_optional = shift; | |
| 199 | − | |
| 200 | − return if (!$python_cmd); | |
| 201 | − | |
| 202 | − my $err = system("$python_cmd -c 'import $prog' 2>/dev/null /dev/null"); | |
| 203 | − return if ($err == 0); | |
| 204 | − | |
| 205 | − add_package($prog, $is_optional); | |
| 206 | −} | |
| 207 | − | |
| 208 | −sub check_rpm_missing($$) | |
| 209 | −{ | |
| 210 | − my @pkgs = @{$_[0]}; | |
| 211 | − my $is_optional = $_[1]; | |
| 212 | − | |
| 213 | − foreach my $prog(@pkgs) { | |
| 214 | − my $err = system("rpm -q '$prog' 2>/dev/null >/dev/null"); | |
| 215 | − add_package($prog, $is_optional) if ($err); | |
| 216 | − } | |
| 217 | −} | |
| 218 | − | |
| 219 | −sub check_pacman_missing($$) | |
| 220 | −{ | |
| 221 | − my @pkgs = @{$_[0]}; | |
| 222 | − my $is_optional = $_[1]; | |
| 223 | − | |
| 224 | − foreach my $prog(@pkgs) { | |
| 225 | − my $err = system("pacman -Q '$prog' 2>/dev/null >/dev/null"); | |
| 226 | − add_package($prog, $is_optional) if ($err); | |
| 227 | − } | |
| 228 | −} | |
| 229 | − | |
| 230 | −sub check_missing_tex($) | |
| 231 | −{ | |
| 232 | − my $is_optional = shift; | |
| 233 | − my $kpsewhich = findprog("kpsewhich"); | |
| 234 | − | |
| 235 | − foreach my $prog(keys %texlive) { | |
| 236 | − my $package = $texlive{$prog}; | |
| 237 | − if (!$kpsewhich) { | |
| 238 | − add_package($package, $is_optional); | |
| 239 | − next; | |
| 240 | − } | |
| 241 | − my $file = qx($kpsewhich $prog); | |
| 242 | − add_package($package, $is_optional) if ($file =~ /^\s*$/); | |
| 243 | − } | |
| 244 | −} | |
| 245 | − | |
| 246 | −sub get_sphinx_fname() | |
| 247 | −{ | |
| 248 | − if ($ENV{'SPHINXBUILD'}) { | |
| 249 | − return $ENV{'SPHINXBUILD'}; | |
| 250 | − } | |
| 251 | − | |
| 252 | − my $fname = "sphinx-build"; | |
| 253 | − return $fname if findprog($fname); | |
| 254 | − | |
| 255 | − $fname = "sphinx-build-3"; | |
| 256 | − if (findprog($fname)) { | |
| 257 | − $need_symlink = 1; | |
| 258 | − return $fname; | |
| 259 | − } | |
| 260 | − | |
| 261 | − return ""; | |
| 262 | −} | |
| 263 | − | |
| 264 | −sub get_sphinx_version($) | |
| 265 | −{ | |
| 266 | − my $cmd = shift; | |
| 267 | − my $ver; | |
| 268 | − | |
| 269 | − open IN, "$cmd --version 2>&1 |"; | |
| 270 | − while (<IN>) { | |
| 271 | − if (m/^\s*sphinx-build\s+([\d\.]+)((\+\/[\da-f]+)|(b\d+))?$/) { | |
| 272 | − $ver=$1; | |
| 273 | − last; | |
| 274 | − } | |
| 275 | − # Sphinx 1.2.x uses a different format | |
| 276 | − if (m/^\s*Sphinx.*\s+([\d\.]+)$/) { | |
| 277 | − $ver=$1; | |
| 278 | − last; | |
| 279 | − } | |
| 280 | − } | |
| 281 | − close IN; | |
| 282 | − return $ver; | |
| 283 | −} | |
| 284 | − | |
| 285 | −sub check_sphinx() | |
| 286 | −{ | |
| 287 | − open IN, $conf or die "Can't open $conf"; | |
| 288 | − while (<IN>) { | |
| 289 | − if (m/^\s*needs_sphinx\s*=\s*[\'\"]([\d\.]+)[\'\"]/) { | |
| 290 | − $min_version=$1; | |
| 291 | − last; | |
| 292 | − } | |
| 293 | − } | |
| 294 | − close IN; | |
| 295 | − | |
| 296 | − die "Can't get needs_sphinx version from $conf" if (!$min_version); | |
| 297 | − | |
| 298 | − $virtenv_dir = $virtenv_prefix . "latest"; | |
| 299 | − | |
| 300 | − my $sphinx = get_sphinx_fname(); | |
| 301 | − if ($sphinx eq "") { | |
| 302 | − $need_sphinx = 1; | |
| 303 | − return; | |
| 304 | − } | |
| 305 | − | |
| 306 | − $cur_version = get_sphinx_version($sphinx); | |
| 307 | − die "$sphinx didn't return its version" if (!$cur_version); | |
| 308 | − | |
| 309 | − if ($cur_version lt $min_version) { | |
| 310 | − printf "ERROR: Sphinx version is %s. It should be >= %s\n", | |
| 311 | − $cur_version, $min_version; | |
| 312 | − $need_sphinx = 1; | |
| 313 | − return; | |
| 314 | − } | |
| 315 | − | |
| 316 | − return if ($cur_version lt $rec_version); | |
| 317 | − | |
| 318 | − # On version check mode, just assume Sphinx has all mandatory deps | |
| 319 | − exit (0) if ($version_check); | |
| 320 | −} | |
| 321 | − | |
| 322 | −# | |
| 323 | −# Ancillary subroutines | |
| 324 | −# | |
| 325 | − | |
| 326 | −sub catcheck($) | |
| 327 | −{ | |
| 328 | − my $res = ""; | |
| 329 | − $res = qx(cat $_[0]) if (-r $_[0]); | |
| 330 | − return $res; | |
| 331 | −} | |
| 332 | − | |
| 333 | −sub which($) | |
| 334 | −{ | |
| 335 | − my $file = shift; | |
| 336 | − my @path = split ":", $ENV{PATH}; | |
| 337 | − | |
| 338 | − foreach my $dir(@path) { | |
| 339 | − my $name = $dir.'/'.$file; | |
| 340 | − return $name if (-x $name ); | |
| 341 | − } | |
| 342 | − return undef; | |
| 343 | −} | |
| 344 | − | |
| 345 | −# | |
| 346 | −# Subroutines that check distro-specific hints | |
| 347 | −# | |
| 348 | − | |
| 349 | −sub give_debian_hints() | |
| 350 | −{ | |
| 351 | − my %map = ( | |
| 352 | − "python-sphinx" => "python3-sphinx", | |
| 353 | − "yaml" => "python3-yaml", | |
| 354 | − "ensurepip" => "python3-venv", | |
| 355 | − "virtualenv" => "virtualenv", | |
| 356 | − "dot" => "graphviz", | |
| 357 | − "convert" => "imagemagick", | |
| 358 | − "Pod::Usage" => "perl-modules", | |
| 359 | − "xelatex" => "texlive-xetex", | |
| 360 | − "rsvg-convert" => "librsvg2-bin", | |
| 361 | − ); | |
| 362 | − | |
| 363 | − if ($pdf) { | |
| 364 | − check_missing_file(["/usr/share/texlive/texmf-dist/tex/latex/ctex/ctexhook.sty"], | |
| 365 | − "texlive-lang-chinese", 2); | |
| 366 | − | |
| 367 | − check_missing_file(["/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf"], | |
| 368 | − "fonts-dejavu", 2); | |
| 369 | − | |
| 370 | − check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc", | |
| 371 | − "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", | |
| 372 | − "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc"], | |
| 373 | − "fonts-noto-cjk", 2); | |
| 374 | − } | |
| 375 | − | |
| 376 | − check_program("dvipng", 2) if ($pdf); | |
| 377 | − check_missing(\%map); | |
| 378 | − | |
| 379 | − return if (!$need && !$optional); | |
| 380 | − printf("You should run:\n") if ($verbose_warn_install); | |
| 381 | − printf("\n\tsudo apt-get install $install\n"); | |
| 382 | −} | |
| 383 | − | |
| 384 | −sub give_redhat_hints() | |
| 385 | −{ | |
| 386 | − my %map = ( | |
| 387 | − "python-sphinx" => "python3-sphinx", | |
| 388 | − "yaml" => "python3-pyyaml", | |
| 389 | − "virtualenv" => "python3-virtualenv", | |
| 390 | − "dot" => "graphviz", | |
| 391 | − "convert" => "ImageMagick", | |
| 392 | − "Pod::Usage" => "perl-Pod-Usage", | |
| 393 | − "xelatex" => "texlive-xetex-bin", | |
| 394 | − "rsvg-convert" => "librsvg2-tools", | |
| 395 | − ); | |
| 396 | − | |
| 397 | − my @fedora26_opt_pkgs = ( | |
| 398 | − "graphviz-gd", # Fedora 26: needed for PDF support | |
| 399 | − ); | |
| 400 | − | |
| 401 | − my @fedora_tex_pkgs = ( | |
| 402 | − "texlive-collection-fontsrecommended", | |
| 403 | − "texlive-collection-latex", | |
| 404 | − "texlive-xecjk", | |
| 405 | − "dejavu-sans-fonts", | |
| 406 | − "dejavu-serif-fonts", | |
| 407 | − "dejavu-sans-mono-fonts", | |
| 408 | − ); | |
| 409 | − | |
| 410 | − # | |
| 411 | − # Checks valid for RHEL/CentOS version 7.x. | |
| 412 | − # | |
| 413 | − my $old = 0; | |
| 414 | − my $rel; | |
| 415 | − my $noto_sans_redhat = "google-noto-sans-cjk-ttc-fonts"; | |
| 416 | − $rel = $1 if ($system_release =~ /(release|Linux)\s+(\d+)/); | |
| 417 | − | |
| 418 | − if (!($system_release =~ /Fedora/)) { | |
| 419 | − $map{"virtualenv"} = "python-virtualenv"; | |
| 420 | − | |
| 421 | − if ($rel && $rel < 8) { | |
| 422 | − $old = 1; | |
| 423 | − $pdf = 0; | |
| 424 | − | |
| 425 | − printf("Note: texlive packages on RHEL/CENTOS <= 7 are incomplete. Can't support PDF output\n"); | |
| 426 | − printf("If you want to build PDF, please read:\n"); | |
| 427 | − printf("\thttps://www.systutorials.com/241660/how-to-install-tex-live-on-centos-7-linux/\n"); | |
| 428 | − } | |
| 429 | − } else { | |
| 430 | − if ($rel && $rel < 26) { | |
| 431 | − $old = 1; | |
| 432 | − } | |
| 433 | − if ($rel && $rel >= 38) { | |
| 434 | − $noto_sans_redhat = "google-noto-sans-cjk-fonts"; | |
| 435 | − } | |
| 436 | − } | |
| 437 | − if (!$rel) { | |
| 438 | − printf("Couldn't identify release number\n"); | |
| 439 | − $old = 1; | |
| 440 | − $pdf = 0; | |
| 441 | − } | |
| 442 | − | |
| 443 | − if ($pdf) { | |
| 444 | − check_missing_file(["/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", | |
| 445 | − "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc"], | |
| 446 | − $noto_sans_redhat, 2); | |
| 447 | − } | |
| 448 | − | |
| 449 | − check_rpm_missing(\@fedora26_opt_pkgs, 2) if ($pdf && !$old); | |
| 450 | − check_rpm_missing(\@fedora_tex_pkgs, 2) if ($pdf); | |
| 451 | − check_missing_tex(2) if ($pdf); | |
| 452 | − check_missing(\%map); | |
| 453 | − | |
| 454 | − return if (!$need && !$optional); | |
| 455 | − | |
| 456 | − if (!$old) { | |
| 457 | − # dnf, for Fedora 18+ | |
| 458 | − printf("You should run:\n") if ($verbose_warn_install); | |
| 459 | − printf("\n\tsudo dnf install -y $install\n"); | |
| 460 | − } else { | |
| 461 | − # yum, for RHEL (and clones) or Fedora version < 18 | |
| 462 | − printf("You should run:\n") if ($verbose_warn_install); | |
| 463 | − printf("\n\tsudo yum install -y $install\n"); | |
| 464 | − } | |
| 465 | −} | |
| 466 | − | |
| 467 | −sub give_opensuse_hints() | |
| 468 | −{ | |
| 469 | − my %map = ( | |
| 470 | − "python-sphinx" => "python3-sphinx", | |
| 471 | − "yaml" => "python3-pyyaml", | |
| 472 | − "virtualenv" => "python3-virtualenv", | |
| 473 | − "dot" => "graphviz", | |
| 474 | − "convert" => "ImageMagick", | |
| 475 | − "Pod::Usage" => "perl-Pod-Usage", | |
| 476 | − "xelatex" => "texlive-xetex-bin", | |
| 477 | − ); | |
| 478 | − | |
| 479 | − # On Tumbleweed, this package is also named rsvg-convert | |
| 480 | − $map{"rsvg-convert"} = "rsvg-view" if (!($system_release =~ /Tumbleweed/)); | |
| 481 | − | |
| 482 | − my @suse_tex_pkgs = ( | |
| 483 | − "texlive-babel-english", | |
| 484 | − "texlive-caption", | |
| 485 | − "texlive-colortbl", | |
| 486 | − "texlive-courier", | |
| 487 | − "texlive-dvips", | |
| 488 | − "texlive-helvetic", | |
| 489 | − "texlive-makeindex", | |
| 490 | − "texlive-metafont", | |
| 491 | − "texlive-metapost", | |
| 492 | − "texlive-palatino", | |
| 493 | − "texlive-preview", | |
| 494 | − "texlive-times", | |
| 495 | − "texlive-zapfchan", | |
| 496 | − "texlive-zapfding", | |
| 497 | − ); | |
| 498 | − | |
| 499 | − $map{"latexmk"} = "texlive-latexmk-bin"; | |
| 500 | − | |
| 501 | − # FIXME: add support for installing CJK fonts | |
| 502 | − # | |
| 503 | − # I tried hard, but was unable to find a way to install | |
| 504 | − # "Noto Sans CJK SC" on openSUSE | |
| 505 | − | |
| 506 | − check_rpm_missing(\@suse_tex_pkgs, 2) if ($pdf); | |
| 507 | − check_missing_tex(2) if ($pdf); | |
| 508 | − check_missing(\%map); | |
| 509 | − | |
| 510 | − return if (!$need && !$optional); | |
| 511 | − printf("You should run:\n") if ($verbose_warn_install); | |
| 512 | − printf("\n\tsudo zypper install --no-recommends $install\n"); | |
| 513 | −} | |
| 514 | − | |
| 515 | −sub give_mageia_hints() | |
| 516 | −{ | |
| 517 | − my %map = ( | |
| 518 | − "python-sphinx" => "python3-sphinx", | |
| 519 | − "yaml" => "python3-yaml", | |
| 520 | − "virtualenv" => "python3-virtualenv", | |
| 521 | − "dot" => "graphviz", | |
| 522 | − "convert" => "ImageMagick", | |
| 523 | − "Pod::Usage" => "perl-Pod-Usage", | |
| 524 | − "xelatex" => "texlive", | |
| 525 | − "rsvg-convert" => "librsvg2", | |
| 526 | − ); | |
| 527 | − | |
| 528 | − my @tex_pkgs = ( | |
| 529 | − "texlive-fontsextra", | |
| 530 | − ); | |
| 531 | − | |
| 532 | − $map{"latexmk"} = "texlive-collection-basic"; | |
| 533 | − | |
| 534 | − my $packager_cmd; | |
| 535 | − my $noto_sans; | |
| 536 | − if ($system_release =~ /OpenMandriva/) { | |
| 537 | − $packager_cmd = "dnf install"; | |
| 538 | − $noto_sans = "noto-sans-cjk-fonts"; | |
| 539 | − @tex_pkgs = ( "texlive-collection-fontsextra" ); | |
| 540 | − } else { | |
| 541 | − $packager_cmd = "urpmi"; | |
| 542 | − $noto_sans = "google-noto-sans-cjk-ttc-fonts"; | |
| 543 | − } | |
| 544 | − | |
| 545 | − | |
| 546 | − if ($pdf) { | |
| 547 | − check_missing_file(["/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", | |
| 548 | − "/usr/share/fonts/TTF/NotoSans-Regular.ttf"], | |
| 549 | − $noto_sans, 2); | |
| 550 | − } | |
| 551 | − | |
| 552 | − check_rpm_missing(\@tex_pkgs, 2) if ($pdf); | |
| 553 | − check_missing(\%map); | |
| 554 | − | |
| 555 | − return if (!$need && !$optional); | |
| 556 | − printf("You should run:\n") if ($verbose_warn_install); | |
| 557 | − printf("\n\tsudo $packager_cmd $install\n"); | |
| 558 | −} | |
| 559 | − | |
| 560 | −sub give_arch_linux_hints() | |
| 561 | −{ | |
| 562 | − my %map = ( | |
| 563 | − "yaml" => "python-yaml", | |
| 564 | − "virtualenv" => "python-virtualenv", | |
| 565 | − "dot" => "graphviz", | |
| 566 | − "convert" => "imagemagick", | |
| 567 | − "xelatex" => "texlive-xetex", | |
| 568 | − "latexmk" => "texlive-core", | |
| 569 | − "rsvg-convert" => "extra/librsvg", | |
| 570 | − ); | |
| 571 | − | |
| 572 | − my @archlinux_tex_pkgs = ( | |
| 573 | − "texlive-core", | |
| 574 | − "texlive-latexextra", | |
| 575 | − "ttf-dejavu", | |
| 576 | − ); | |
| 577 | − check_pacman_missing(\@archlinux_tex_pkgs, 2) if ($pdf); | |
| 578 | − | |
| 579 | − if ($pdf) { | |
| 580 | − check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"], | |
| 581 | − "noto-fonts-cjk", 2); | |
| 582 | − } | |
| 583 | − | |
| 584 | − check_missing(\%map); | |
| 585 | − | |
| 586 | − return if (!$need && !$optional); | |
| 587 | − printf("You should run:\n") if ($verbose_warn_install); | |
| 588 | − printf("\n\tsudo pacman -S $install\n"); | |
| 589 | −} | |
| 590 | − | |
| 591 | −sub give_gentoo_hints() | |
| 592 | −{ | |
| 593 | − my %map = ( | |
| 594 | − "yaml" => "dev-python/pyyaml", | |
| 595 | − "virtualenv" => "dev-python/virtualenv", | |
| 596 | − "dot" => "media-gfx/graphviz", | |
| 597 | − "convert" => "media-gfx/imagemagick", | |
| 598 | − "xelatex" => "dev-texlive/texlive-xetex media-fonts/dejavu", | |
| 599 | − "rsvg-convert" => "gnome-base/librsvg", | |
| 600 | − ); | |
| 601 | − | |
| 602 | − check_missing_file(["/usr/share/fonts/dejavu/DejaVuSans.ttf"], | |
| 603 | − "media-fonts/dejavu", 2) if ($pdf); | |
| 604 | − | |
| 605 | − if ($pdf) { | |
| 606 | − check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJKsc-Regular.otf", | |
| 607 | − "/usr/share/fonts/noto-cjk/NotoSerifCJK-Regular.ttc"], | |
| 608 | − "media-fonts/noto-cjk", 2); | |
| 609 | − } | |
| 610 | − | |
| 611 | − check_missing(\%map); | |
| 612 | − | |
| 613 | − return if (!$need && !$optional); | |
| 614 | − | |
| 615 | − printf("You should run:\n") if ($verbose_warn_install); | |
| 616 | − printf("\n"); | |
| 617 | − | |
| 618 | − my $imagemagick = "media-gfx/imagemagick svg png"; | |
| 619 | − my $cairo = "media-gfx/graphviz cairo pdf"; | |
| 620 | − my $portage_imagemagick = "/etc/portage/package.use/imagemagick"; | |
| 621 | − my $portage_cairo = "/etc/portage/package.use/graphviz"; | |
| 622 | − | |
| 623 | − if (qx(grep imagemagick $portage_imagemagick 2>/dev/null) eq "") { | |
| 624 | − printf("\tsudo su -c 'echo \"$imagemagick\" > $portage_imagemagick'\n") | |
| 625 | − } | |
| 626 | − if (qx(grep graphviz $portage_cairo 2>/dev/null) eq "") { | |
| 627 | − printf("\tsudo su -c 'echo \"$cairo\" > $portage_cairo'\n"); | |
| 628 | − } | |
| 629 | − | |
| 630 | − printf("\tsudo emerge --ask $install\n"); | |
| 631 | − | |
| 632 | −} | |
| 633 | − | |
| 634 | −sub check_distros() | |
| 635 | −{ | |
| 636 | − # Distro-specific hints | |
| 637 | − if ($system_release =~ /Red Hat Enterprise Linux/) { | |
| 638 | − give_redhat_hints; | |
| 639 | − return; | |
| 640 | − } | |
| 641 | − if ($system_release =~ /CentOS/) { | |
| 642 | − give_redhat_hints; | |
| 643 | − return; | |
| 644 | − } | |
| 645 | − if ($system_release =~ /Scientific Linux/) { | |
| 646 | − give_redhat_hints; | |
| 647 | − return; | |
| 648 | − } | |
| 649 | − if ($system_release =~ /Oracle Linux Server/) { | |
| 650 | − give_redhat_hints; | |
| 651 | − return; | |
| 652 | − } | |
| 653 | − if ($system_release =~ /Fedora/) { | |
| 654 | − give_redhat_hints; | |
| 655 | − return; | |
| 656 | − } | |
| 657 | − if ($system_release =~ /Ubuntu/) { | |
| 658 | − give_debian_hints; | |
| 659 | − return; | |
| 660 | − } | |
| 661 | − if ($system_release =~ /Debian/) { | |
| 662 | − give_debian_hints; | |
| 663 | − return; | |
| 664 | − } | |
| 665 | − if ($system_release =~ /openSUSE/) { | |
| 666 | − give_opensuse_hints; | |
| 667 | − return; | |
| 668 | − } | |
| 669 | − if ($system_release =~ /Mageia/) { | |
| 670 | − give_mageia_hints; | |
| 671 | − return; | |
| 672 | − } | |
| 673 | − if ($system_release =~ /OpenMandriva/) { | |
| 674 | − give_mageia_hints; | |
| 675 | − return; | |
| 676 | − } | |
| 677 | − if ($system_release =~ /Arch Linux/) { | |
| 678 | − give_arch_linux_hints; | |
| 679 | − return; | |
| 680 | − } | |
| 681 | − if ($system_release =~ /Gentoo/) { | |
| 682 | − give_gentoo_hints; | |
| 683 | − return; | |
| 684 | − } | |
| 685 | − | |
| 686 | − # | |
| 687 | − # Fall-back to generic hint code for other distros | |
| 688 | − # That's far from ideal, specially for LaTeX dependencies. | |
| 689 | − # | |
| 690 | − my %map = ( | |
| 691 | − "sphinx-build" => "sphinx" | |
| 692 | − ); | |
| 693 | − check_missing_tex(2) if ($pdf); | |
| 694 | − check_missing(\%map); | |
| 695 | − print "I don't know distro $system_release.\n"; | |
| 696 | − print "So, I can't provide you a hint with the install procedure.\n"; | |
| 697 | − print "There are likely missing dependencies.\n"; | |
| 698 | −} | |
| 699 | − | |
| 700 | −# | |
| 701 | −# Common dependencies | |
| 3 | +# Copyright (c) 2017-2025 Mauro Carvalho Chehab <mchehab+huawei@kernel.org> | |
| 702 | 4 | # |
| 5 | +# pylint: disable=C0103,C0114,C0115,C0116,C0301,C0302 | |
| 6 | +# pylint: disable=R0902,R0904,R0911,R0912,R0914,R0915,R1705,R1710,E1121 | |
| 7 | + | |
| 8 | +# Note: this script requires at least Python 3.6 to run. | |
| 9 | +# Don't add changes not compatible with it, it is meant to report | |
| 10 | +# incompatible python versions. | |
| 11 | + | |
| 12 | +""" | |
| 13 | +Dependency checker for Sphinx documentation Kernel build. | |
| 14 | + | |
| 15 | +This module provides tools to check for all required dependencies needed to | |
| 16 | +build documentation using Sphinx, including system packages, Python modules | |
| 17 | +and LaTeX packages for PDF generation. | |
| 18 | + | |
| 19 | +It detect packages for a subset of Linux distributions used by Kernel | |
| 20 | +maintainers, showing hints and missing dependencies. | |
| 21 | + | |
| 22 | +The main class SphinxDependencyChecker handles the dependency checking logic | |
| 23 | +and provides recommendations for installing missing packages. It supports both | |
| 24 | +system package installations and Python virtual environments. By default, | |
| 25 | +system pacage install is recommended. | |
| 26 | +""" | |
| 27 | + | |
| 28 | +import argparse | |
| 29 | +import os | |
| 30 | +import re | |
| 31 | +import subprocess | |
| 32 | +import sys | |
| 33 | +from glob import glob | |
| 34 | + | |
| 35 | + | |
| 36 | +def parse_version(version): | |
| 37 | + """Convert a major.minor.patch version into a tuple""" | |
| 38 | + return tuple(int(x) for x in version.split(".")) | |
| 39 | + | |
| 40 | + | |
| 41 | +def ver_str(version): | |
| 42 | + """Returns a version tuple as major.minor.patch""" | |
| 43 | + | |
| 44 | + return ".".join([str(x) for x in version]) | |
| 45 | + | |
| 46 | + | |
| 47 | +RECOMMENDED_VERSION = parse_version("3.4.3") | |
| 48 | +MIN_PYTHON_VERSION = parse_version("3.7") | |
| 49 | + | |
| 50 | + | |
| 51 | +class DepManager: | |
| 52 | + """ | |
| 53 | + Manage package dependencies. There are three types of dependencies: | |
| 54 | + | |
| 55 | + - System: dependencies required for docs build; | |
| 56 | + - Python: python dependencies for a native distro Sphinx install; | |
| 57 | + - PDF: dependencies needed by PDF builds. | |
| 58 | + | |
| 59 | + Each dependency can be mandatory or optional. Not installing an optional | |
| 60 | + dependency won't break the build, but will cause degradation at the | |
| 61 | + docs output. | |
| 62 | + """ | |
| 63 | + | |
| 64 | + # Internal types of dependencies. Don't use them outside DepManager class. | |
| 65 | + _SYS_TYPE = 0 | |
| 66 | + _PHY_TYPE = 1 | |
| 67 | + _PDF_TYPE = 2 | |
| 68 | + | |
| 69 | + # Dependencies visible outside the class. | |
| 70 | + # The keys are tuple with: (type, is_mandatory flag). | |
| 71 | + # | |
| 72 | + # Currently we're not using all optional dep types. Yet, we'll keep all | |
| 73 | + # possible combinations here. They're not many, and that makes easier | |
| 74 | + # if later needed and for the name() method below | |
| 75 | + | |
| 76 | + SYSTEM_MANDATORY = (_SYS_TYPE, True) | |
| 77 | + PYTHON_MANDATORY = (_PHY_TYPE, True) | |
| 78 | + PDF_MANDATORY = (_PDF_TYPE, True) | |
| 79 | + | |
| 80 | + SYSTEM_OPTIONAL = (_SYS_TYPE, False) | |
| 81 | + PYTHON_OPTIONAL = (_PHY_TYPE, False) | |
| 82 | + PDF_OPTIONAL = (_PDF_TYPE, True) | |
| 83 | + | |
| 84 | + def __init__(self, pdf): | |
| 85 | + """ | |
| 86 | + Initialize internal vars: | |
| 87 | + | |
| 88 | + - missing: missing dependencies list, containing a distro-independent | |
| 89 | + name for a missing dependency and its type. | |
| 90 | + - missing_pkg: ancillary dict containing missing dependencies in | |
| 91 | + distro namespace, organized by type. | |
| 92 | + - need: total number of needed dependencies. Never cleaned. | |
| 93 | + - optional: total number of optional dependencies. Never cleaned. | |
| 94 | + - pdf: Is PDF support enabled? | |
| 95 | + """ | |
| 96 | + self.missing = {} | |
| 97 | + self.missing_pkg = {} | |
| 98 | + self.need = 0 | |
| 99 | + self.optional = 0 | |
| 100 | + self.pdf = pdf | |
| 101 | + | |
| 102 | + @staticmethod | |
| 103 | + def name(dtype): | |
| 104 | + """ | |
| 105 | + Ancillary routine to output a warn/error message reporting | |
| 106 | + missing dependencies. | |
| 107 | + """ | |
| 108 | + if dtype[0] == DepManager._SYS_TYPE: | |
| 109 | + msg = "build" | |
| 110 | + elif dtype[0] == DepManager._PHY_TYPE: | |
| 111 | + msg = "Python" | |
| 112 | + else: | |
| 113 | + msg = "PDF" | |
| 114 | + | |
| 115 | + if dtype[1]: | |
| 116 | + return f"ERROR: {msg} mandatory deps missing" | |
| 117 | + else: | |
| 118 | + return f"Warning: {msg} optional deps missing" | |
| 119 | + | |
| 120 | + @staticmethod | |
| 121 | + def is_optional(dtype): | |
| 122 | + """Ancillary routine to report if a dependency is optional""" | |
| 123 | + return not dtype[1] | |
| 124 | + | |
| 125 | + @staticmethod | |
| 126 | + def is_pdf(dtype): | |
| 127 | + """Ancillary routine to report if a dependency is for PDF generation""" | |
| 128 | + if dtype[0] == DepManager._PDF_TYPE: | |
| 129 | + return True | |
| 130 | + | |
| 131 | + return False | |
| 132 | + | |
| 133 | + def add_package(self, package, dtype): | |
| 134 | + """ | |
| 135 | + Add a package at the self.missing() dictionary. | |
| 136 | + Doesn't update missing_pkg. | |
| 137 | + """ | |
| 138 | + is_optional = DepManager.is_optional(dtype) | |
| 139 | + self.missing[package] = dtype | |
| 140 | + if is_optional: | |
| 141 | + self.optional += 1 | |
| 142 | + else: | |
| 143 | + self.need += 1 | |
| 144 | + | |
| 145 | + def del_package(self, package): | |
| 146 | + """ | |
| 147 | + Remove a package at the self.missing() dictionary. | |
| 148 | + Doesn't update missing_pkg. | |
| 149 | + """ | |
| 150 | + if package in self.missing: | |
| 151 | + del self.missing[package] | |
| 152 | + | |
| 153 | + def clear_deps(self): | |
| 154 | + """ | |
| 155 | + Clear dependencies without changing needed/optional. | |
| 156 | + | |
| 157 | + This is an ackward way to have a separate section to recommend | |
| 158 | + a package after system main dependencies. | |
| 159 | + | |
| 160 | + TODO: rework the logic to prevent needing it. | |
| 161 | + """ | |
| 162 | + | |
| 163 | + self.missing = {} | |
| 164 | + self.missing_pkg = {} | |
| 165 | + | |
| 166 | + def check_missing(self, progs): | |
| 167 | + """ | |
| 168 | + Update self.missing_pkg, using progs dict to convert from the | |
| 169 | + agnostic package name to distro-specific one. | |
| 170 | + | |
| 171 | + Returns an string with the packages to be installed, sorted and | |
| 172 | + with eventual duplicates removed. | |
| 173 | + """ | |
| 174 | + | |
| 175 | + self.missing_pkg = {} | |
| 176 | + | |
| 177 | + for prog, dtype in sorted(self.missing.items()): | |
| 178 | + # At least on some LTS distros like CentOS 7, texlive doesn't | |
| 179 | + # provide all packages we need. When such distros are | |
| 180 | + # detected, we have to disable PDF output. | |
| 181 | + # | |
| 182 | + # So, we need to ignore the packages that distros would | |
| 183 | + # need for LaTeX to work | |
| 184 | + if DepManager.is_pdf(dtype) and not self.pdf: | |
| 185 | + self.optional -= 1 | |
| 186 | + continue | |
| 187 | + | |
| 188 | + if not dtype in self.missing_pkg: | |
| 189 | + self.missing_pkg[dtype] = [] | |
| 190 | + | |
| 191 | + self.missing_pkg[dtype].append(progs.get(prog, prog)) | |
| 192 | + | |
| 193 | + install = [] | |
| 194 | + for dtype, pkgs in self.missing_pkg.items(): | |
| 195 | + install += pkgs | |
| 196 | + | |
| 197 | + return " ".join(sorted(set(install))) | |
| 198 | + | |
| 199 | + def warn_install(self): | |
| 200 | + """ | |
| 201 | + Emit warnings/errors related to missing packages. | |
| 202 | + """ | |
| 203 | + | |
| 204 | + output_msg = "" | |
| 205 | + | |
| 206 | + for dtype in sorted(self.missing_pkg.keys()): | |
| 207 | + progs = " ".join(sorted(set(self.missing_pkg[dtype]))) | |
| 208 | + | |
| 209 | + try: | |
| 210 | + name = DepManager.name(dtype) | |
| 211 | + output_msg += f'{name}:\t{progs}\n' | |
| 212 | + except KeyError: | |
| 213 | + raise KeyError(f"ERROR!!!: invalid dtype for {progs}: {dtype}") | |
| 214 | + | |
| 215 | + if output_msg: | |
| 216 | + print(f"\n{output_msg}") | |
| 217 | + | |
| 218 | +class AncillaryMethods: | |
| 219 | + """ | |
| 220 | + Ancillary methods that checks for missing dependencies for different | |
| 221 | + types of types, like binaries, python modules, rpm deps, etc. | |
| 222 | + """ | |
| 223 | + | |
| 224 | + @staticmethod | |
| 225 | + def which(prog): | |
| 226 | + """ | |
| 227 | + Our own implementation of which(). We could instead use | |
| 228 | + shutil.which(), but this function is simple enough. | |
| 229 | + Probably faster to use this implementation than to import shutil. | |
| 230 | + """ | |
| 231 | + for path in os.environ.get("PATH", "").split(":"): | |
| 232 | + full_path = os.path.join(path, prog) | |
| 233 | + if os.access(full_path, os.X_OK): | |
| 234 | + return full_path | |
| 235 | + | |
| 236 | + return None | |
| 237 | + | |
| 238 | + @staticmethod | |
| 239 | + def get_python_version(cmd): | |
| 240 | + """ | |
| 241 | + Get python version from a Python binary. As we need to detect if | |
| 242 | + are out there newer python binaries, we can't rely on sys.release here. | |
| 243 | + """ | |
| 244 | + | |
| 245 | + result = SphinxDependencyChecker.run([cmd, "--version"], | |
| 246 | + capture_output=True, text=True) | |
| 247 | + version = result.stdout.strip() | |
| 248 | + | |
| 249 | + match = re.search(r"(\d+\.\d+\.\d+)", version) | |
| 250 | + if match: | |
| 251 | + return parse_version(match.group(1)) | |
| 252 | + | |
| 253 | + print(f"Can't parse version {version}") | |
| 254 | + return (0, 0, 0) | |
| 255 | + | |
| 256 | + @staticmethod | |
| 257 | + def find_python(): | |
| 258 | + """ | |
| 259 | + Detect if are out there any python 3.xy version newer than the | |
| 260 | + current one. | |
| 261 | + | |
| 262 | + Note: this routine is limited to up to 2 digits for python3. We | |
| 263 | + may need to update it one day, hopefully on a distant future. | |
| 264 | + """ | |
| 265 | + patterns = [ | |
| 266 | + "python3.[0-9]", | |
| 267 | + "python3.[0-9][0-9]", | |
| 268 | + ] | |
| 269 | + | |
| 270 | + # Seek for a python binary newer than MIN_PYTHON_VERSION | |
| 271 | + for path in os.getenv("PATH", "").split(":"): | |
| 272 | + for pattern in patterns: | |
| 273 | + for cmd in glob(os.path.join(path, pattern)): | |
| 274 | + if os.path.isfile(cmd) and os.access(cmd, os.X_OK): | |
| 275 | + version = SphinxDependencyChecker.get_python_version(cmd) | |
| 276 | + if version >= MIN_PYTHON_VERSION: | |
| 277 | + return cmd | |
| 278 | + | |
| 279 | + @staticmethod | |
| 280 | + def check_python(): | |
| 281 | + """ | |
| 282 | + Check if the current python binary satisfies our minimal requirement | |
| 283 | + for Sphinx build. If not, re-run with a newer version if found. | |
| 284 | + """ | |
| 285 | + cur_ver = sys.version_info[:3] | |
| 286 | + if cur_ver >= MIN_PYTHON_VERSION: | |
| 287 | + ver = ver_str(cur_ver) | |
| 288 | + print(f"Python version: {ver}") | |
| 289 | + | |
| 290 | + # This could be useful for debugging purposes | |
| 291 | + if SphinxDependencyChecker.which("docutils"): | |
| 292 | + result = SphinxDependencyChecker.run(["docutils", "--version"], | |
| 293 | + capture_output=True, text=True) | |
| 294 | + ver = result.stdout.strip() | |
| 295 | + match = re.search(r"(\d+\.\d+\.\d+)", ver) | |
| 296 | + if match: | |
| 297 | + ver = match.group(1) | |
| 298 | + | |
| 299 | + print(f"Docutils version: {ver}") | |
| 300 | + | |
| 301 | + return | |
| 302 | + | |
| 303 | + python_ver = ver_str(cur_ver) | |
| 304 | + | |
| 305 | + new_python_cmd = SphinxDependencyChecker.find_python() | |
| 306 | + if not new_python_cmd: | |
| 307 | + print(f"ERROR: Python version {python_ver} is not spported anymore\n") | |
| 308 | + print(" Can't find a new version. This script may fail") | |
| 309 | + return | |
| 310 | + | |
| 311 | + # Restart script using the newer version | |
| 312 | + script_path = os.path.abspath(sys.argv[0]) | |
| 313 | + args = [new_python_cmd, script_path] + sys.argv[1:] | |
| 314 | + | |
| 315 | + print(f"Python {python_ver} not supported. Changing to {new_python_cmd}") | |
| 316 | + | |
| 317 | + try: | |
| 318 | + os.execv(new_python_cmd, args) | |
| 319 | + except OSError as e: | |
| 320 | + sys.exit(f"Failed to restart with {new_python_cmd}: {e}") | |
| 321 | + | |
| 322 | + @staticmethod | |
| 323 | + def run(*args, **kwargs): | |
| 324 | + """ | |
| 325 | + Excecute a command, hiding its output by default. | |
| 326 | + Preserve comatibility with older Python versions. | |
| 327 | + """ | |
| 328 | + | |
| 329 | + capture_output = kwargs.pop('capture_output', False) | |
| 330 | + | |
| 331 | + if capture_output: | |
| 332 | + if 'stdout' not in kwargs: | |
| 333 | + kwargs['stdout'] = subprocess.PIPE | |
| 334 | + if 'stderr' not in kwargs: | |
| 335 | + kwargs['stderr'] = subprocess.PIPE | |
| 336 | + else: | |
| 337 | + if 'stdout' not in kwargs: | |
| 338 | + kwargs['stdout'] = subprocess.DEVNULL | |
| 339 | + if 'stderr' not in kwargs: | |
| 340 | + kwargs['stderr'] = subprocess.DEVNULL | |
| 341 | + | |
| 342 | + # Don't break with older Python versions | |
| 343 | + if 'text' in kwargs and sys.version_info < (3, 7): | |
| 344 | + kwargs['universal_newlines'] = kwargs.pop('text') | |
| 345 | + | |
| 346 | + return subprocess.run(*args, **kwargs) | |
| 347 | + | |
| 348 | +class MissingCheckers(AncillaryMethods): | |
| 349 | + """ | |
| 350 | + Contains some ancillary checkers for different types of binaries and | |
| 351 | + package managers. | |
| 352 | + """ | |
| 353 | + | |
| 354 | + def __init__(self, args, texlive): | |
| 355 | + """ | |
| 356 | + Initialize its internal variables | |
| 357 | + """ | |
| 358 | + self.pdf = args.pdf | |
| 359 | + self.virtualenv = args.virtualenv | |
| 360 | + self.version_check = args.version_check | |
| 361 | + self.texlive = texlive | |
| 362 | + | |
| 363 | + self.min_version = (0, 0, 0) | |
| 364 | + self.cur_version = (0, 0, 0) | |
| 365 | + | |
| 366 | + self.deps = DepManager(self.pdf) | |
| 367 | + | |
| 368 | + self.need_symlink = 0 | |
| 369 | + self.need_sphinx = 0 | |
| 370 | + | |
| 371 | + self.verbose_warn_install = 1 | |
| 372 | + | |
| 373 | + self.virtenv_dir = "" | |
| 374 | + self.install = "" | |
| 375 | + self.python_cmd = "" | |
| 376 | + | |
| 377 | + self.virtenv_prefix = ["sphinx_", "Sphinx_" ] | |
| 378 | + | |
| 379 | + def check_missing_file(self, files, package, dtype): | |
| 380 | + """ | |
| 381 | + Does the file exists? If not, add it to missing dependencies. | |
| 382 | + """ | |
| 383 | + for f in files: | |
| 384 | + if os.path.exists(f): | |
| 385 | + return | |
| 386 | + self.deps.add_package(package, dtype) | |
| 387 | + | |
| 388 | + def check_program(self, prog, dtype): | |
| 389 | + """ | |
| 390 | + Does the program exists and it is at the PATH? | |
| 391 | + If not, add it to missing dependencies. | |
| 392 | + """ | |
| 393 | + found = self.which(prog) | |
| 394 | + if found: | |
| 395 | + return found | |
| 396 | + | |
| 397 | + self.deps.add_package(prog, dtype) | |
| 398 | + | |
| 399 | + return None | |
| 400 | + | |
| 401 | + def check_perl_module(self, prog, dtype): | |
| 402 | + """ | |
| 403 | + Does perl have a dependency? Is it available? | |
| 404 | + If not, add it to missing dependencies. | |
| 405 | + | |
| 406 | + Right now, we still need Perl for doc build, as it is required | |
| 407 | + by some tools called at docs or kernel build time, like: | |
| 408 | + | |
| 409 | + scripts/documentation-file-ref-check | |
| 410 | + | |
| 411 | + Also, checkpatch is on Perl. | |
| 412 | + """ | |
| 413 | + | |
| 414 | + # While testing with lxc download template, one of the | |
| 415 | + # distros (Oracle) didn't have perl - nor even an option to install | |
| 416 | + # before installing oraclelinux-release-el9 package. | |
| 417 | + # | |
| 418 | + # Check it before running an error. If perl is not there, | |
| 419 | + # add it as a mandatory package, as some parts of the doc builder | |
| 420 | + # needs it. | |
| 421 | + if not self.which("perl"): | |
| 422 | + self.deps.add_package("perl", DepManager.SYSTEM_MANDATORY) | |
| 423 | + self.deps.add_package(prog, dtype) | |
| 424 | + return | |
| 425 | + | |
| 426 | + try: | |
| 427 | + self.run(["perl", f"-M{prog}", "-e", "1"], check=True) | |
| 428 | + except subprocess.CalledProcessError: | |
| 429 | + self.deps.add_package(prog, dtype) | |
| 430 | + | |
| 431 | + def check_python_module(self, module, is_optional=False): | |
| 432 | + """ | |
| 433 | + Does a python module exists outside venv? If not, add it to missing | |
| 434 | + dependencies. | |
| 435 | + """ | |
| 436 | + if is_optional: | |
| 437 | + dtype = DepManager.PYTHON_OPTIONAL | |
| 438 | + else: | |
| 439 | + dtype = DepManager.PYTHON_MANDATORY | |
| 440 | + | |
| 441 | + try: | |
| 442 | + self.run([self.python_cmd, "-c", f"import {module}"], check=True) | |
| 443 | + except subprocess.CalledProcessError: | |
| 444 | + self.deps.add_package(module, dtype) | |
| 445 | + | |
| 446 | + def check_rpm_missing(self, pkgs, dtype): | |
| 447 | + """ | |
| 448 | + Does a rpm package exists? If not, add it to missing dependencies. | |
| 449 | + """ | |
| 450 | + for prog in pkgs: | |
| 451 | + try: | |
| 452 | + self.run(["rpm", "-q", prog], check=True) | |
| 453 | + except subprocess.CalledProcessError: | |
| 454 | + self.deps.add_package(prog, dtype) | |
| 455 | + | |
| 456 | + def check_pacman_missing(self, pkgs, dtype): | |
| 457 | + """ | |
| 458 | + Does a pacman package exists? If not, add it to missing dependencies. | |
| 459 | + """ | |
| 460 | + for prog in pkgs: | |
| 461 | + try: | |
| 462 | + self.run(["pacman", "-Q", prog], check=True) | |
| 463 | + except subprocess.CalledProcessError: | |
| 464 | + self.deps.add_package(prog, dtype) | |
| 465 | + | |
| 466 | + def check_missing_tex(self, is_optional=False): | |
| 467 | + """ | |
| 468 | + Does a LaTeX package exists? If not, add it to missing dependencies. | |
| 469 | + """ | |
| 470 | + if is_optional: | |
| 471 | + dtype = DepManager.PDF_OPTIONAL | |
| 472 | + else: | |
| 473 | + dtype = DepManager.PDF_MANDATORY | |
| 474 | + | |
| 475 | + kpsewhich = self.which("kpsewhich") | |
| 476 | + for prog, package in self.texlive.items(): | |
| 477 | + | |
| 478 | + # If kpsewhich is not there, just add it to deps | |
| 479 | + if not kpsewhich: | |
| 480 | + self.deps.add_package(package, dtype) | |
| 481 | + continue | |
| 482 | + | |
| 483 | + # Check if the package is needed | |
| 484 | + try: | |
| 485 | + result = self.run( | |
| 486 | + [kpsewhich, prog], stdout=subprocess.PIPE, text=True, check=True | |
| 487 | + ) | |
| 488 | + | |
| 489 | + # Didn't find. Add it | |
| 490 | + if not result.stdout.strip(): | |
| 491 | + self.deps.add_package(package, dtype) | |
| 492 | + | |
| 493 | + except subprocess.CalledProcessError: | |
| 494 | + # kpsewhich returned an error. Add it, just in case | |
| 495 | + self.deps.add_package(package, dtype) | |
| 496 | + | |
| 497 | + def get_sphinx_fname(self): | |
| 498 | + """ | |
| 499 | + Gets the binary filename for sphinx-build. | |
| 500 | + """ | |
| 501 | + if "SPHINXBUILD" in os.environ: | |
| 502 | + return os.environ["SPHINXBUILD"] | |
| 503 | + | |
| 504 | + fname = "sphinx-build" | |
| 505 | + if self.which(fname): | |
| 506 | + return fname | |
| 507 | + | |
| 508 | + fname = "sphinx-build-3" | |
| 509 | + if self.which(fname): | |
| 510 | + self.need_symlink = 1 | |
| 511 | + return fname | |
| 512 | + | |
| 513 | + return "" | |
| 514 | + | |
| 515 | + def get_sphinx_version(self, cmd): | |
| 516 | + """ | |
| 517 | + Gets sphinx-build version. | |
| 518 | + """ | |
| 519 | + try: | |
| 520 | + result = self.run([cmd, "--version"], | |
| 521 | + stdout=subprocess.PIPE, | |
| 522 | + stderr=subprocess.STDOUT, | |
| 523 | + text=True, check=True) | |
| 524 | + except (subprocess.CalledProcessError, FileNotFoundError): | |
| 525 | + return None | |
| 526 | + | |
| 527 | + for line in result.stdout.split("\n"): | |
| 528 | + match = re.match(r"^sphinx-build\s+([\d\.]+)(?:\+(?:/[\da-f]+)|b\d+)?\s*$", line) | |
| 529 | + if match: | |
| 530 | + return parse_version(match.group(1)) | |
| 531 | + | |
| 532 | + match = re.match(r"^Sphinx.*\s+([\d\.]+)\s*$", line) | |
| 533 | + if match: | |
| 534 | + return parse_version(match.group(1)) | |
| 535 | + | |
| 536 | + def check_sphinx(self, conf): | |
| 537 | + """ | |
| 538 | + Checks Sphinx minimal requirements | |
| 539 | + """ | |
| 540 | + try: | |
| 541 | + with open(conf, "r", encoding="utf-8") as f: | |
| 542 | + for line in f: | |
| 543 | + match = re.match(r"^\s*needs_sphinx\s*=\s*[\'\"]([\d\.]+)[\'\"]", line) | |
| 544 | + if match: | |
| 545 | + self.min_version = parse_version(match.group(1)) | |
| 546 | + break | |
| 547 | + except IOError: | |
| 548 | + sys.exit(f"Can't open {conf}") | |
| 549 | + | |
| 550 | + if not self.min_version: | |
| 551 | + sys.exit(f"Can't get needs_sphinx version from {conf}") | |
| 552 | + | |
| 553 | + self.virtenv_dir = self.virtenv_prefix[0] + "latest" | |
| 554 | + | |
| 555 | + sphinx = self.get_sphinx_fname() | |
| 556 | + if not sphinx: | |
| 557 | + self.need_sphinx = 1 | |
| 558 | + return | |
| 559 | + | |
| 560 | + self.cur_version = self.get_sphinx_version(sphinx) | |
| 561 | + if not self.cur_version: | |
| 562 | + sys.exit(f"{sphinx} didn't return its version") | |
| 563 | + | |
| 564 | + if self.cur_version < self.min_version: | |
| 565 | + curver = ver_str(self.cur_version) | |
| 566 | + minver = ver_str(self.min_version) | |
| 567 | + | |
| 568 | + print(f"ERROR: Sphinx version is {curver}. It should be >= {minver}") | |
| 569 | + self.need_sphinx = 1 | |
| 570 | + return | |
| 571 | + | |
| 572 | + # On version check mode, just assume Sphinx has all mandatory deps | |
| 573 | + if self.version_check and self.cur_version >= RECOMMENDED_VERSION: | |
| 574 | + sys.exit(0) | |
| 575 | + | |
| 576 | + def catcheck(self, filename): | |
| 577 | + """ | |
| 578 | + Reads a file if it exists, returning as string. | |
| 579 | + If not found, returns an empty string. | |
| 580 | + """ | |
| 581 | + if os.path.exists(filename): | |
| 582 | + with open(filename, "r", encoding="utf-8") as f: | |
| 583 | + return f.read().strip() | |
| 584 | + return "" | |
| 585 | + | |
| 586 | + def get_system_release(self): | |
| 587 | + """ | |
| 588 | + Determine the system type. There's no unique way that would work | |
| 589 | + with all distros with a minimal package install. So, several | |
| 590 | + methods are used here. | |
| 591 | + | |
| 592 | + By default, it will use lsb_release function. If not available, it will | |
| 593 | + fail back to reading the known different places where the distro name | |
| 594 | + is stored. | |
| 595 | + | |
| 596 | + Several modern distros now have /etc/os-release, which usually have | |
| 597 | + a decent coverage. | |
| 598 | + """ | |
| 599 | + | |
| 600 | + system_release = "" | |
| 601 | + | |
| 602 | + if self.which("lsb_release"): | |
| 603 | + result = self.run(["lsb_release", "-d"], capture_output=True, text=True) | |
| 604 | + system_release = result.stdout.replace("Description:", "").strip() | |
| 605 | + | |
| 606 | + release_files = [ | |
| 607 | + "/etc/system-release", | |
| 608 | + "/etc/redhat-release", | |
| 609 | + "/etc/lsb-release", | |
| 610 | + "/etc/gentoo-release", | |
| 611 | + ] | |
| 612 | + | |
| 613 | + if not system_release: | |
| 614 | + for f in release_files: | |
| 615 | + system_release = self.catcheck(f) | |
| 616 | + if system_release: | |
| 617 | + break | |
| 618 | + | |
| 619 | + # This seems more common than LSB these days | |
| 620 | + if not system_release: | |
| 621 | + os_var = {} | |
| 622 | + try: | |
| 623 | + with open("/etc/os-release", "r", encoding="utf-8") as f: | |
| 624 | + for line in f: | |
| 625 | + match = re.match(r"^([\w\d\_]+)=\"?([^\"]*)\"?\n", line) | |
| 626 | + if match: | |
| 627 | + os_var[match.group(1)] = match.group(2) | |
| 628 | + | |
| 629 | + system_release = os_var.get("NAME", "") | |
| 630 | + if "VERSION_ID" in os_var: | |
| 631 | + system_release += " " + os_var["VERSION_ID"] | |
| 632 | + elif "VERSION" in os_var: | |
| 633 | + system_release += " " + os_var["VERSION"] | |
| 634 | + except IOError: | |
| 635 | + pass | |
| 636 | + | |
| 637 | + if not system_release: | |
| 638 | + system_release = self.catcheck("/etc/issue") | |
| 639 | + | |
| 640 | + system_release = system_release.strip() | |
| 641 | + | |
| 642 | + return system_release | |
| 643 | + | |
| 644 | +class SphinxDependencyChecker(MissingCheckers): | |
| 645 | + """ | |
| 646 | + Main class for checking Sphinx documentation build dependencies. | |
| 647 | + | |
| 648 | + - Check for missing system packages; | |
| 649 | + - Check for missing Python modules; | |
| 650 | + - Check for missing LaTeX packages needed by PDF generation; | |
| 651 | + - Propose Sphinx install via Python Virtual environment; | |
| 652 | + - Propose Sphinx install via distro-specific package install. | |
| 653 | + """ | |
| 654 | + def __init__(self, args): | |
| 655 | + """Initialize checker variables""" | |
| 656 | + | |
| 657 | + # List of required texlive packages on Fedora and OpenSuse | |
| 658 | + texlive = { | |
| 659 | + "amsfonts.sty": "texlive-amsfonts", | |
| 660 | + "amsmath.sty": "texlive-amsmath", | |
| 661 | + "amssymb.sty": "texlive-amsfonts", | |
| 662 | + "amsthm.sty": "texlive-amscls", | |
| 663 | + "anyfontsize.sty": "texlive-anyfontsize", | |
| 664 | + "atbegshi.sty": "texlive-oberdiek", | |
| 665 | + "bm.sty": "texlive-tools", | |
| 666 | + "capt-of.sty": "texlive-capt-of", | |
| 667 | + "cmap.sty": "texlive-cmap", | |
| 668 | + "ctexhook.sty": "texlive-ctex", | |
| 669 | + "ecrm1000.tfm": "texlive-ec", | |
| 670 | + "eqparbox.sty": "texlive-eqparbox", | |
| 671 | + "eu1enc.def": "texlive-euenc", | |
| 672 | + "fancybox.sty": "texlive-fancybox", | |
| 673 | + "fancyvrb.sty": "texlive-fancyvrb", | |
| 674 | + "float.sty": "texlive-float", | |
| 675 | + "fncychap.sty": "texlive-fncychap", | |
| 676 | + "footnote.sty": "texlive-mdwtools", | |
| 677 | + "framed.sty": "texlive-framed", | |
| 678 | + "luatex85.sty": "texlive-luatex85", | |
| 679 | + "multirow.sty": "texlive-multirow", | |
| 680 | + "needspace.sty": "texlive-needspace", | |
| 681 | + "palatino.sty": "texlive-psnfss", | |
| 682 | + "parskip.sty": "texlive-parskip", | |
| 683 | + "polyglossia.sty": "texlive-polyglossia", | |
| 684 | + "tabulary.sty": "texlive-tabulary", | |
| 685 | + "threeparttable.sty": "texlive-threeparttable", | |
| 686 | + "titlesec.sty": "texlive-titlesec", | |
| 687 | + "ucs.sty": "texlive-ucs", | |
| 688 | + "upquote.sty": "texlive-upquote", | |
| 689 | + "wrapfig.sty": "texlive-wrapfig", | |
| 690 | + } | |
| 691 | + | |
| 692 | + super().__init__(args, texlive) | |
| 693 | + | |
| 694 | + self.need_pip = False | |
| 695 | + self.rec_sphinx_upgrade = 0 | |
| 696 | + | |
| 697 | + self.system_release = self.get_system_release() | |
| 698 | + self.activate_cmd = "" | |
| 699 | + | |
| 700 | + # Some distros may not have a Sphinx shipped package compatible with | |
| 701 | + # our minimal requirements | |
| 702 | + self.package_supported = True | |
| 703 | + | |
| 704 | + # Recommend a new python version | |
| 705 | + self.recommend_python = None | |
| 706 | + | |
| 707 | + # Certain hints are meant to be shown only once | |
| 708 | + self.distro_msg = None | |
| 709 | + | |
| 710 | + self.latest_avail_ver = (0, 0, 0) | |
| 711 | + self.venv_ver = (0, 0, 0) | |
| 712 | + | |
| 713 | + prefix = os.environ.get("srctree", ".") + "/" | |
| 714 | + | |
| 715 | + self.conf = prefix + "Documentation/conf.py" | |
| 716 | + self.requirement_file = prefix + "Documentation/sphinx/requirements.txt" | |
| 717 | + | |
| 718 | + def get_install_progs(self, progs, cmd, extra=None): | |
| 719 | + """ | |
| 720 | + Check for missing dependencies using the provided program mapping. | |
| 721 | + | |
| 722 | + The actual distro-specific programs are mapped via progs argument. | |
| 723 | + """ | |
| 724 | + install = self.deps.check_missing(progs) | |
| 725 | + | |
| 726 | + if self.verbose_warn_install: | |
| 727 | + self.deps.warn_install() | |
| 728 | + | |
| 729 | + if not install: | |
| 730 | + return | |
| 731 | + | |
| 732 | + if cmd: | |
| 733 | + if self.verbose_warn_install: | |
| 734 | + msg = "You should run:" | |
| 735 | + else: | |
| 736 | + msg = "" | |
| 737 | + | |
| 738 | + if extra: | |
| 739 | + msg += "\n\t" + extra.replace("\n", "\n\t") | |
| 740 | + | |
| 741 | + return(msg + "\n\tsudo " + cmd + " " + install) | |
| 742 | + | |
| 743 | + return None | |
| 744 | + | |
| 745 | + # | |
| 746 | + # Distro-specific hints methods | |
| 747 | + # | |
| 748 | + | |
| 749 | + def give_debian_hints(self): | |
| 750 | + """ | |
| 751 | + Provide package installation hints for Debian-based distros. | |
| 752 | + """ | |
| 753 | + progs = { | |
| 754 | + "Pod::Usage": "perl-modules", | |
| 755 | + "convert": "imagemagick", | |
| 756 | + "dot": "graphviz", | |
| 757 | + "ensurepip": "python3-venv", | |
| 758 | + "python-sphinx": "python3-sphinx", | |
| 759 | + "rsvg-convert": "librsvg2-bin", | |
| 760 | + "virtualenv": "virtualenv", | |
| 761 | + "xelatex": "texlive-xetex", | |
| 762 | + "yaml": "python3-yaml", | |
| 763 | + } | |
| 764 | + | |
| 765 | + if self.pdf: | |
| 766 | + pdf_pkgs = { | |
| 767 | + "fonts-dejavu": [ | |
| 768 | + "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", | |
| 769 | + ], | |
| 770 | + "fonts-noto-cjk": [ | |
| 771 | + "/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc", | |
| 772 | + "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc", | |
| 773 | + "/usr/share/fonts/opentype/noto/NotoSerifCJK-Regular.ttc", | |
| 774 | + ], | |
| 775 | + "tex-gyre": [ | |
| 776 | + "/usr/share/texmf/tex/latex/tex-gyre/tgtermes.sty" | |
| 777 | + ], | |
| 778 | + "texlive-fonts-recommended": [ | |
| 779 | + "/usr/share/texlive/texmf-dist/fonts/tfm/adobe/zapfding/pzdr.tfm", | |
| 780 | + ], | |
| 781 | + "texlive-lang-chinese": [ | |
| 782 | + "/usr/share/texlive/texmf-dist/tex/latex/ctex/ctexhook.sty", | |
| 783 | + ], | |
| 784 | + } | |
| 785 | + | |
| 786 | + for package, files in pdf_pkgs.items(): | |
| 787 | + self.check_missing_file(files, package, DepManager.PDF_MANDATORY) | |
| 788 | + | |
| 789 | + self.check_program("dvipng", DepManager.PDF_MANDATORY) | |
| 790 | + | |
| 791 | + if not self.distro_msg: | |
| 792 | + self.distro_msg = \ | |
| 793 | + "Note: ImageMagick is broken on some distros, affecting PDF output. For more details:\n" \ | |
| 794 | + "\thttps://askubuntu.com/questions/1158894/imagemagick-still-broken-using-with-usr-bin-convert" | |
| 795 | + | |
| 796 | + return self.get_install_progs(progs, "apt-get install") | |
| 797 | + | |
| 798 | + def give_redhat_hints(self): | |
| 799 | + """ | |
| 800 | + Provide package installation hints for RedHat-based distros | |
| 801 | + (Fedora, RHEL and RHEL-based variants). | |
| 802 | + """ | |
| 803 | + progs = { | |
| 804 | + "Pod::Usage": "perl-Pod-Usage", | |
| 805 | + "convert": "ImageMagick", | |
| 806 | + "dot": "graphviz", | |
| 807 | + "python-sphinx": "python3-sphinx", | |
| 808 | + "rsvg-convert": "librsvg2-tools", | |
| 809 | + "virtualenv": "python3-virtualenv", | |
| 810 | + "xelatex": "texlive-xetex-bin", | |
| 811 | + "yaml": "python3-pyyaml", | |
| 812 | + } | |
| 813 | + | |
| 814 | + fedora_tex_pkgs = [ | |
| 815 | + "dejavu-sans-fonts", | |
| 816 | + "dejavu-sans-mono-fonts", | |
| 817 | + "dejavu-serif-fonts", | |
| 818 | + "texlive-collection-fontsrecommended", | |
| 819 | + "texlive-collection-latex", | |
| 820 | + "texlive-xecjk", | |
| 821 | + ] | |
| 822 | + | |
| 823 | + fedora = False | |
| 824 | + rel = None | |
| 825 | + | |
| 826 | + match = re.search(r"(release|Linux)\s+(\d+)", self.system_release) | |
| 827 | + if match: | |
| 828 | + rel = int(match.group(2)) | |
| 829 | + | |
| 830 | + if not rel: | |
| 831 | + print("Couldn't identify release number") | |
| 832 | + noto_sans_redhat = None | |
| 833 | + self.pdf = False | |
| 834 | + elif re.search("Fedora", self.system_release): | |
| 835 | + # Fedora 38 and upper use this CJK font | |
| 836 | + | |
| 837 | + noto_sans_redhat = "google-noto-sans-cjk-fonts" | |
| 838 | + fedora = True | |
| 839 | + else: | |
| 840 | + # Almalinux, CentOS, RHEL, ... | |
| 841 | + | |
| 842 | + # at least up to version 9 (and Fedora < 38), that's the CJK font | |
| 843 | + noto_sans_redhat = "google-noto-sans-cjk-ttc-fonts" | |
| 844 | + | |
| 845 | + progs["virtualenv"] = "python-virtualenv" | |
| 846 | + | |
| 847 | + if not rel or rel < 8: | |
| 848 | + print("ERROR: Distro not supported. Too old?") | |
| 849 | + return | |
| 850 | + | |
| 851 | + # RHEL 8 uses Python 3.6, which is not compatible with | |
| 852 | + # the build system anymore. Suggest Python 3.11 | |
| 853 | + if rel == 8: | |
| 854 | + self.check_program("python3.9", DepManager.SYSTEM_MANDATORY) | |
| 855 | + progs["python3.9"] = "python39" | |
| 856 | + progs["yaml"] = "python39-pyyaml" | |
| 857 | + | |
| 858 | + self.recommend_python = True | |
| 859 | + | |
| 860 | + # There's no python39-sphinx package. Only pip is supported | |
| 861 | + self.package_supported = False | |
| 862 | + | |
| 863 | + if not self.distro_msg: | |
| 864 | + self.distro_msg = \ | |
| 865 | + "Note: RHEL-based distros typically require extra repositories.\n" \ | |
| 866 | + "For most, enabling epel and crb are enough:\n" \ | |
| 867 | + "\tsudo dnf install -y epel-release\n" \ | |
| 868 | + "\tsudo dnf config-manager --set-enabled crb\n" \ | |
| 869 | + "Yet, some may have other required repositories. Those commands could be useful:\n" \ | |
| 870 | + "\tsudo dnf repolist all\n" \ | |
| 871 | + "\tsudo dnf repoquery --available --info <pkgs>\n" \ | |
| 872 | + "\tsudo dnf config-manager --set-enabled '*' # enable all - probably not what you want" | |
| 873 | + | |
| 874 | + if self.pdf: | |
| 875 | + pdf_pkgs = [ | |
| 876 | + "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", | |
| 877 | + "/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc", | |
| 878 | + ] | |
| 879 | + | |
| 880 | + self.check_missing_file(pdf_pkgs, noto_sans_redhat, DepManager.PDF_MANDATORY) | |
| 881 | + | |
| 882 | + self.check_rpm_missing(fedora_tex_pkgs, DepManager.PDF_MANDATORY) | |
| 883 | + | |
| 884 | + self.check_missing_tex(DepManager.PDF_MANDATORY) | |
| 885 | + | |
| 886 | + # There's no texlive-ctex on RHEL 8 repositories. This will | |
| 887 | + # likely affect CJK pdf build only. | |
| 888 | + if not fedora and rel == 8: | |
| 889 | + self.deps.del_package("texlive-ctex") | |
| 890 | + | |
| 891 | + return self.get_install_progs(progs, "dnf install") | |
| 892 | + | |
| 893 | + def give_opensuse_hints(self): | |
| 894 | + """ | |
| 895 | + Provide package installation hints for openSUSE-based distros | |
| 896 | + (Leap and Tumbleweed). | |
| 897 | + """ | |
| 898 | + progs = { | |
| 899 | + "Pod::Usage": "perl-Pod-Usage", | |
| 900 | + "convert": "ImageMagick", | |
| 901 | + "dot": "graphviz", | |
| 902 | + "python-sphinx": "python3-sphinx", | |
| 903 | + "virtualenv": "python3-virtualenv", | |
| 904 | + "xelatex": "texlive-xetex-bin texlive-dejavu", | |
| 905 | + "yaml": "python3-pyyaml", | |
| 906 | + } | |
| 907 | + | |
| 908 | + suse_tex_pkgs = [ | |
| 909 | + "texlive-babel-english", | |
| 910 | + "texlive-caption", | |
| 911 | + "texlive-colortbl", | |
| 912 | + "texlive-courier", | |
| 913 | + "texlive-dvips", | |
| 914 | + "texlive-helvetic", | |
| 915 | + "texlive-makeindex", | |
| 916 | + "texlive-metafont", | |
| 917 | + "texlive-metapost", | |
| 918 | + "texlive-palatino", | |
| 919 | + "texlive-preview", | |
| 920 | + "texlive-times", | |
| 921 | + "texlive-zapfchan", | |
| 922 | + "texlive-zapfding", | |
| 923 | + ] | |
| 924 | + | |
| 925 | + progs["latexmk"] = "texlive-latexmk-bin" | |
| 926 | + | |
| 927 | + match = re.search(r"(Leap)\s+(\d+).(\d)", self.system_release) | |
| 928 | + if match: | |
| 929 | + rel = int(match.group(2)) | |
| 930 | + | |
| 931 | + # Leap 15.x uses Python 3.6, which is not compatible with | |
| 932 | + # the build system anymore. Suggest Python 3.11 | |
| 933 | + if rel == 15: | |
| 934 | + if not self.which(self.python_cmd): | |
| 935 | + self.check_program("python3.11", DepManager.SYSTEM_MANDATORY) | |
| 936 | + progs["python3.11"] = "python311" | |
| 937 | + self.recommend_python = True | |
| 938 | + | |
| 939 | + progs.update({ | |
| 940 | + "python-sphinx": "python311-Sphinx python311-Sphinx-latex", | |
| 941 | + "virtualenv": "python311-virtualenv", | |
| 942 | + "yaml": "python311-PyYAML", | |
| 943 | + }) | |
| 944 | + else: | |
| 945 | + # Tumbleweed defaults to Python 3.11 | |
| 946 | + | |
| 947 | + progs.update({ | |
| 948 | + "python-sphinx": "python313-Sphinx python313-Sphinx-latex", | |
| 949 | + "virtualenv": "python313-virtualenv", | |
| 950 | + "yaml": "python313-PyYAML", | |
| 951 | + }) | |
| 952 | + | |
| 953 | + # FIXME: add support for installing CJK fonts | |
| 954 | + # | |
| 955 | + # I tried hard, but was unable to find a way to install | |
| 956 | + # "Noto Sans CJK SC" on openSUSE | |
| 957 | + | |
| 958 | + if self.pdf: | |
| 959 | + self.check_rpm_missing(suse_tex_pkgs, DepManager.PDF_MANDATORY) | |
| 960 | + if self.pdf: | |
| 961 | + self.check_missing_tex() | |
| 962 | + | |
| 963 | + return self.get_install_progs(progs, "zypper install --no-recommends") | |
| 964 | + | |
| 965 | + def give_mageia_hints(self): | |
| 966 | + """ | |
| 967 | + Provide package installation hints for Mageia and OpenMandriva. | |
| 968 | + """ | |
| 969 | + progs = { | |
| 970 | + "Pod::Usage": "perl-Pod-Usage", | |
| 971 | + "convert": "ImageMagick", | |
| 972 | + "dot": "graphviz", | |
| 973 | + "python-sphinx": "python3-sphinx", | |
| 974 | + "rsvg-convert": "librsvg2", | |
| 975 | + "virtualenv": "python3-virtualenv", | |
| 976 | + "xelatex": "texlive", | |
| 977 | + "yaml": "python3-yaml", | |
| 978 | + } | |
| 979 | + | |
| 980 | + tex_pkgs = [ | |
| 981 | + "texlive-fontsextra", | |
| 982 | + "texlive-fonts-asian", | |
| 983 | + "fonts-ttf-dejavu", | |
| 984 | + ] | |
| 985 | + | |
| 986 | + if re.search(r"OpenMandriva", self.system_release): | |
| 987 | + packager_cmd = "dnf install" | |
| 988 | + noto_sans = "noto-sans-cjk-fonts" | |
| 989 | + tex_pkgs = [ | |
| 990 | + "texlive-collection-basic", | |
| 991 | + "texlive-collection-langcjk", | |
| 992 | + "texlive-collection-fontsextra", | |
| 993 | + "texlive-collection-fontsrecommended" | |
| 994 | + ] | |
| 995 | + | |
| 996 | + # Tested on OpenMandriva Lx 4.3 | |
| 997 | + progs["convert"] = "imagemagick" | |
| 998 | + progs["yaml"] = "python-pyyaml" | |
| 999 | + progs["python-virtualenv"] = "python-virtualenv" | |
| 1000 | + progs["python-sphinx"] = "python-sphinx" | |
| 1001 | + progs["xelatex"] = "texlive" | |
| 1002 | + | |
| 1003 | + self.check_program("python-virtualenv", DepManager.PYTHON_MANDATORY) | |
| 1004 | + | |
| 1005 | + # On my tests with openMandriva LX 4.0 docker image, upgraded | |
| 1006 | + # to 4.3, python-virtualenv package is broken: it is missing | |
| 1007 | + # ensurepip. Without it, the alternative would be to run: | |
| 1008 | + # python3 -m venv --without-pip ~/sphinx_latest, but running | |
| 1009 | + # pip there won't install sphinx at venv. | |
| 1010 | + # | |
| 1011 | + # Add a note about that. | |
| 1012 | + | |
| 1013 | + if not self.distro_msg: | |
| 1014 | + self.distro_msg = \ | |
| 1015 | + "Notes:\n"\ | |
| 1016 | + "1. for venv, ensurepip could be broken, preventing its install method.\n" \ | |
| 1017 | + "2. at least on OpenMandriva LX 4.3, texlive packages seem broken" | |
| 1018 | + | |
| 1019 | + else: | |
| 1020 | + packager_cmd = "urpmi" | |
| 1021 | + noto_sans = "google-noto-sans-cjk-ttc-fonts" | |
| 1022 | + | |
| 1023 | + progs["latexmk"] = "texlive-collection-basic" | |
| 1024 | + | |
| 1025 | + if self.pdf: | |
| 1026 | + pdf_pkgs = [ | |
| 1027 | + "/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc", | |
| 1028 | + "/usr/share/fonts/TTF/NotoSans-Regular.ttf", | |
| 1029 | + ] | |
| 1030 | + | |
| 1031 | + self.check_missing_file(pdf_pkgs, noto_sans, DepManager.PDF_MANDATORY) | |
| 1032 | + self.check_rpm_missing(tex_pkgs, DepManager.PDF_MANDATORY) | |
| 1033 | + | |
| 1034 | + return self.get_install_progs(progs, packager_cmd) | |
| 1035 | + | |
| 1036 | + def give_arch_linux_hints(self): | |
| 1037 | + """ | |
| 1038 | + Provide package installation hints for ArchLinux. | |
| 1039 | + """ | |
| 1040 | + progs = { | |
| 1041 | + "convert": "imagemagick", | |
| 1042 | + "dot": "graphviz", | |
| 1043 | + "latexmk": "texlive-core", | |
| 1044 | + "rsvg-convert": "extra/librsvg", | |
| 1045 | + "virtualenv": "python-virtualenv", | |
| 1046 | + "xelatex": "texlive-xetex", | |
| 1047 | + "yaml": "python-yaml", | |
| 1048 | + } | |
| 1049 | + | |
| 1050 | + archlinux_tex_pkgs = [ | |
| 1051 | + "texlive-basic", | |
| 1052 | + "texlive-binextra", | |
| 1053 | + "texlive-core", | |
| 1054 | + "texlive-fontsrecommended", | |
| 1055 | + "texlive-langchinese", | |
| 1056 | + "texlive-langcjk", | |
| 1057 | + "texlive-latexextra", | |
| 1058 | + "ttf-dejavu", | |
| 1059 | + ] | |
| 1060 | + | |
| 1061 | + if self.pdf: | |
| 1062 | + self.check_pacman_missing(archlinux_tex_pkgs, | |
| 1063 | + DepManager.PDF_MANDATORY) | |
| 1064 | + | |
| 1065 | + self.check_missing_file(["/usr/share/fonts/noto-cjk/NotoSansCJK-Regular.ttc"], | |
| 1066 | + "noto-fonts-cjk", | |
| 1067 | + DepManager.PDF_MANDATORY) | |
| 1068 | + | |
| 1069 | + | |
| 1070 | + return self.get_install_progs(progs, "pacman -S") | |
| 1071 | + | |
| 1072 | + def give_gentoo_hints(self): | |
| 1073 | + """ | |
| 1074 | + Provide package installation hints for Gentoo. | |
| 1075 | + """ | |
| 1076 | + texlive_deps = [ | |
| 1077 | + "dev-texlive/texlive-fontsrecommended", | |
| 1078 | + "dev-texlive/texlive-latexextra", | |
| 1079 | + "dev-texlive/texlive-xetex", | |
| 1080 | + "media-fonts/dejavu", | |
| 1081 | + ] | |
| 1082 | + | |
| 1083 | + progs = { | |
| 1084 | + "convert": "media-gfx/imagemagick", | |
| 1085 | + "dot": "media-gfx/graphviz", | |
| 1086 | + "rsvg-convert": "gnome-base/librsvg", | |
| 1087 | + "virtualenv": "dev-python/virtualenv", | |
| 1088 | + "xelatex": " ".join(texlive_deps), | |
| 1089 | + "yaml": "dev-python/pyyaml", | |
| 1090 | + "python-sphinx": "dev-python/sphinx", | |
| 1091 | + } | |
| 1092 | + | |
| 1093 | + if self.pdf: | |
| 1094 | + pdf_pkgs = { | |
| 1095 | + "media-fonts/dejavu": [ | |
| 1096 | + "/usr/share/fonts/dejavu/DejaVuSans.ttf", | |
| 1097 | + ], | |
| 1098 | + "media-fonts/noto-cjk": [ | |
| 1099 | + "/usr/share/fonts/noto-cjk/NotoSansCJKsc-Regular.otf", | |
| 1100 | + "/usr/share/fonts/noto-cjk/NotoSerifCJK-Regular.ttc", | |
| 1101 | + ], | |
| 1102 | + } | |
| 1103 | + for package, files in pdf_pkgs.items(): | |
| 1104 | + self.check_missing_file(files, package, DepManager.PDF_MANDATORY) | |
| 1105 | + | |
| 1106 | + # Handling dependencies is a nightmare, as Gentoo refuses to emerge | |
| 1107 | + # some packages if there's no package.use file describing them. | |
| 1108 | + # To make it worse, compilation flags shall also be present there | |
| 1109 | + # for some packages. If USE is not perfect, error/warning messages | |
| 1110 | + # like those are shown: | |
| 1111 | + # | |
| 1112 | + # !!! The following binary packages have been ignored due to non matching USE: | |
| 1113 | + # | |
| 1114 | + # =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_13 qt6 svg | |
| 1115 | + # =media-gfx/graphviz-12.2.1-r1 X pdf python_single_target_python3_12 -python_single_target_python3_13 qt6 svg | |
| 1116 | + # =media-gfx/graphviz-12.2.1-r1 X pdf qt6 svg | |
| 1117 | + # =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_10 qt6 svg | |
| 1118 | + # =media-gfx/graphviz-12.2.1-r1 X pdf -python_single_target_python3_10 python_single_target_python3_12 -python_single_target_python3_13 qt6 svg | |
| 1119 | + # =media-fonts/noto-cjk-20190416 X | |
| 1120 | + # =app-text/texlive-core-2024-r1 X cjk -xetex | |
| 1121 | + # =app-text/texlive-core-2024-r1 X -xetex | |
| 1122 | + # =app-text/texlive-core-2024-r1 -xetex | |
| 1123 | + # =dev-libs/zziplib-0.13.79-r1 sdl | |
| 1124 | + # | |
| 1125 | + # And will ignore such packages, installing the remaining ones. That | |
| 1126 | + # affects mostly the image extension and PDF generation. | |
| 1127 | + | |
| 1128 | + # Package dependencies and the minimal needed args: | |
| 1129 | + portages = { | |
| 1130 | + "graphviz": "media-gfx/graphviz", | |
| 1131 | + "imagemagick": "media-gfx/imagemagick", | |
| 1132 | + "media-libs": "media-libs/harfbuzz icu", | |
| 1133 | + "media-fonts": "media-fonts/noto-cjk", | |
| 1134 | + "texlive": "app-text/texlive-core xetex", | |
| 1135 | + "zziblib": "dev-libs/zziplib sdl", | |
| 1136 | + } | |
| 1137 | + | |
| 1138 | + extra_cmds = "" | |
| 1139 | + if not self.distro_msg: | |
| 1140 | + self.distro_msg = "Note: Gentoo requires package.use to be adjusted before emerging packages" | |
| 1141 | + | |
| 1142 | + use_base = "/etc/portage/package.use" | |
| 1143 | + files = glob(f"{use_base}/*") | |
| 1144 | + | |
| 1145 | + for fname, portage in portages.items(): | |
| 1146 | + install = False | |
| 1147 | + | |
| 1148 | + while install is False: | |
| 1149 | + if not files: | |
| 1150 | + # No files under package.usage. Install all | |
| 1151 | + install = True | |
| 1152 | + break | |
| 1153 | + | |
| 1154 | + args = portage.split(" ") | |
| 1155 | + | |
| 1156 | + name = args.pop(0) | |
| 1157 | + | |
| 1158 | + cmd = ["grep", "-l", "-E", rf"^{name}\b" ] + files | |
| 1159 | + result = self.run(cmd, stdout=subprocess.PIPE, text=True) | |
| 1160 | + if result.returncode or not result.stdout.strip(): | |
| 1161 | + # File containing portage name not found | |
| 1162 | + install = True | |
| 1163 | + break | |
| 1164 | + | |
| 1165 | + # Ensure that needed USE flags are present | |
| 1166 | + if args: | |
| 1167 | + match_fname = result.stdout.strip() | |
| 1168 | + with open(match_fname, 'r', encoding='utf8', | |
| 1169 | + errors='backslashreplace') as fp: | |
| 1170 | + for line in fp: | |
| 1171 | + for arg in args: | |
| 1172 | + if arg.startswith("-"): | |
| 1173 | + continue | |
| 1174 | + | |
| 1175 | + if not re.search(rf"\s*{arg}\b", line): | |
| 1176 | + # Needed file argument not found | |
| 1177 | + install = True | |
| 1178 | + break | |
| 1179 | + | |
| 1180 | + # Everything looks ok, don't install | |
| 1181 | + break | |
| 1182 | + | |
| 1183 | + # emit a code to setup missing USE | |
| 1184 | + if install: | |
| 1185 | + extra_cmds += (f"sudo su -c 'echo \"{portage}\" > {use_base}/{fname}'\n") | |
| 1186 | + | |
| 1187 | + # Now, we can use emerge and let it respect USE | |
| 1188 | + return self.get_install_progs(progs, | |
| 1189 | + "emerge --ask --changed-use --binpkg-respect-use=y", | |
| 1190 | + extra_cmds) | |
| 1191 | + | |
| 1192 | + def get_install(self): | |
| 1193 | + """ | |
| 1194 | + OS-specific hints logic. Seeks for a hinter. If found, use it to | |
| 1195 | + provide package-manager specific install commands. | |
| 1196 | + | |
| 1197 | + Otherwise, outputs install instructions for the meta-packages. | |
| 1198 | + | |
| 1199 | + Returns a string with the command to be executed to install the | |
| 1200 | + the needed packages, if distro found. Otherwise, return just a | |
| 1201 | + list of packages that require installation. | |
| 1202 | + """ | |
| 1203 | + os_hints = { | |
| 1204 | + re.compile("Red Hat Enterprise Linux"): self.give_redhat_hints, | |
| 1205 | + re.compile("Fedora"): self.give_redhat_hints, | |
| 1206 | + re.compile("AlmaLinux"): self.give_redhat_hints, | |
| 1207 | + re.compile("Amazon Linux"): self.give_redhat_hints, | |
| 1208 | + re.compile("CentOS"): self.give_redhat_hints, | |
| 1209 | + re.compile("openEuler"): self.give_redhat_hints, | |
| 1210 | + re.compile("Oracle Linux Server"): self.give_redhat_hints, | |
| 1211 | + re.compile("Rocky Linux"): self.give_redhat_hints, | |
| 1212 | + re.compile("Springdale Open Enterprise"): self.give_redhat_hints, | |
| 1213 | + | |
| 1214 | + re.compile("Ubuntu"): self.give_debian_hints, | |
| 1215 | + re.compile("Debian"): self.give_debian_hints, | |
| 1216 | + re.compile("Devuan"): self.give_debian_hints, | |
| 1217 | + re.compile("Kali"): self.give_debian_hints, | |
| 1218 | + re.compile("Mint"): self.give_debian_hints, | |
| 1219 | + | |
| 1220 | + re.compile("openSUSE"): self.give_opensuse_hints, | |
| 1221 | + | |
| 1222 | + re.compile("Mageia"): self.give_mageia_hints, | |
| 1223 | + re.compile("OpenMandriva"): self.give_mageia_hints, | |
| 1224 | + | |
| 1225 | + re.compile("Arch Linux"): self.give_arch_linux_hints, | |
| 1226 | + re.compile("Gentoo"): self.give_gentoo_hints, | |
| 1227 | + } | |
| 1228 | + | |
| 1229 | + # If the OS is detected, use per-OS hint logic | |
| 1230 | + for regex, os_hint in os_hints.items(): | |
| 1231 | + if regex.search(self.system_release): | |
| 1232 | + return os_hint() | |
| 1233 | + | |
| 1234 | + # | |
| 1235 | + # Fall-back to generic hint code for other distros | |
| 1236 | + # That's far from ideal, specially for LaTeX dependencies. | |
| 1237 | + # | |
| 1238 | + progs = {"sphinx-build": "sphinx"} | |
| 1239 | + if self.pdf: | |
| 1240 | + self.check_missing_tex() | |
| 1241 | + | |
| 1242 | + self.distro_msg = \ | |
| 1243 | + f"I don't know distro {self.system_release}.\n" \ | |
| 1244 | + "So, I can't provide you a hint with the install procedure.\n" \ | |
| 1245 | + "There are likely missing dependencies." | |
| 1246 | + | |
| 1247 | + return self.get_install_progs(progs, None) | |
| 1248 | + | |
| 1249 | + # | |
| 1250 | + # Common dependencies | |
| 1251 | + # | |
| 1252 | + def deactivate_help(self): | |
| 1253 | + """ | |
| 1254 | + Print a helper message to disable a virtual environment. | |
| 1255 | + """ | |
| 1256 | + | |
| 1257 | + print("\n If you want to exit the virtualenv, you can use:") | |
| 1258 | + print("\tdeactivate") | |
| 1259 | + | |
| 1260 | + def get_virtenv(self): | |
| 1261 | + """ | |
| 1262 | + Give a hint about how to activate an already-existing virtual | |
| 1263 | + environment containing sphinx-build. | |
| 1264 | + | |
| 1265 | + Returns a tuble with (activate_cmd_path, sphinx_version) with | |
| 1266 | + the newest available virtual env. | |
| 1267 | + """ | |
| 1268 | + | |
| 1269 | + cwd = os.getcwd() | |
| 1270 | + | |
| 1271 | + activates = [] | |
| 1272 | + | |
| 1273 | + # Add all sphinx prefixes with possible version numbers | |
| 1274 | + for p in self.virtenv_prefix: | |
| 1275 | + activates += glob(f"{cwd}/{p}[0-9]*/bin/activate") | |
| 1276 | + | |
| 1277 | + activates.sort(reverse=True, key=str.lower) | |
| 1278 | + | |
| 1279 | + # Place sphinx_latest first, if it exists | |
| 1280 | + for p in self.virtenv_prefix: | |
| 1281 | + activates = glob(f"{cwd}/{p}*latest/bin/activate") + activates | |
| 1282 | + | |
| 1283 | + ver = (0, 0, 0) | |
| 1284 | + for f in activates: | |
| 1285 | + # Discard too old Sphinx virtual environments | |
| 1286 | + match = re.search(r"(\d+)\.(\d+)\.(\d+)", f) | |
| 1287 | + if match: | |
| 1288 | + ver = (int(match.group(1)), int(match.group(2)), int(match.group(3))) | |
| 1289 | + | |
| 1290 | + if ver < self.min_version: | |
| 1291 | + continue | |
| 1292 | + | |
| 1293 | + sphinx_cmd = f.replace("activate", "sphinx-build") | |
| 1294 | + if not os.path.isfile(sphinx_cmd): | |
| 1295 | + continue | |
| 1296 | + | |
| 1297 | + ver = self.get_sphinx_version(sphinx_cmd) | |
| 1298 | + | |
| 1299 | + if not ver: | |
| 1300 | + venv_dir = f.replace("/bin/activate", "") | |
| 1301 | + print(f"Warning: virtual environment {venv_dir} is not working.\n" \ | |
| 1302 | + "Python version upgrade? Remove it with:\n\n" \ | |
| 1303 | + "\trm -rf {venv_dir}\n\n") | |
| 1304 | + else: | |
| 1305 | + if self.need_sphinx and ver >= self.min_version: | |
| 1306 | + return (f, ver) | |
| 1307 | + elif parse_version(ver) > self.cur_version: | |
| 1308 | + return (f, ver) | |
| 1309 | + | |
| 1310 | + return ("", ver) | |
| 1311 | + | |
| 1312 | + def recommend_sphinx_upgrade(self): | |
| 1313 | + """ | |
| 1314 | + Check if Sphinx needs to be upgraded. | |
| 1315 | + | |
| 1316 | + Returns a tuple with the higest available Sphinx version if found. | |
| 1317 | + Otherwise, returns None to indicate either that no upgrade is needed | |
| 1318 | + or no venv was found. | |
| 1319 | + """ | |
| 1320 | + | |
| 1321 | + # Avoid running sphinx-builds from venv if cur_version is good | |
| 1322 | + if self.cur_version and self.cur_version >= RECOMMENDED_VERSION: | |
| 1323 | + self.latest_avail_ver = self.cur_version | |
| 1324 | + return None | |
| 1325 | + | |
| 1326 | + # Get the highest version from sphinx_*/bin/sphinx-build and the | |
| 1327 | + # corresponding command to activate the venv/virtenv | |
| 1328 | + self.activate_cmd, self.venv_ver = self.get_virtenv() | |
| 1329 | + | |
| 1330 | + # Store the highest version from Sphinx existing virtualenvs | |
| 1331 | + if self.activate_cmd and self.venv_ver > self.cur_version: | |
| 1332 | + self.latest_avail_ver = self.venv_ver | |
| 1333 | + else: | |
| 1334 | + if self.cur_version: | |
| 1335 | + self.latest_avail_ver = self.cur_version | |
| 1336 | + else: | |
| 1337 | + self.latest_avail_ver = (0, 0, 0) | |
| 1338 | + | |
| 1339 | + # As we don't know package version of Sphinx, and there's no | |
| 1340 | + # virtual environments, don't check if upgrades are needed | |
| 1341 | + if not self.virtualenv: | |
| 1342 | + if not self.latest_avail_ver: | |
| 1343 | + return None | |
| 1344 | + | |
| 1345 | + return self.latest_avail_ver | |
| 1346 | + | |
| 1347 | + # Either there are already a virtual env or a new one should be created | |
| 1348 | + self.need_pip = True | |
| 1349 | + | |
| 1350 | + if not self.latest_avail_ver: | |
| 1351 | + return None | |
| 1352 | + | |
| 1353 | + # Return if the reason is due to an upgrade or not | |
| 1354 | + if self.latest_avail_ver != (0, 0, 0): | |
| 1355 | + if self.latest_avail_ver < RECOMMENDED_VERSION: | |
| 1356 | + self.rec_sphinx_upgrade = 1 | |
| 1357 | + | |
| 1358 | + return self.latest_avail_ver | |
| 1359 | + | |
| 1360 | + def recommend_package(self): | |
| 1361 | + """ | |
| 1362 | + Recommend installing Sphinx as a distro-specific package. | |
| 1363 | + """ | |
| 1364 | + | |
| 1365 | + print("\n2) As a package with:") | |
| 1366 | + | |
| 1367 | + old_need = self.deps.need | |
| 1368 | + old_optional = self.deps.optional | |
| 1369 | + | |
| 1370 | + self.pdf = False | |
| 1371 | + self.deps.optional = 0 | |
| 1372 | + old_verbose = self.verbose_warn_install | |
| 1373 | + self.verbose_warn_install = 0 | |
| 1374 | + | |
| 1375 | + self.deps.clear_deps() | |
| 1376 | + | |
| 1377 | + self.deps.add_package("python-sphinx", DepManager.PYTHON_MANDATORY) | |
| 1378 | + | |
| 1379 | + cmd = self.get_install() | |
| 1380 | + if cmd: | |
| 1381 | + print(cmd) | |
| 1382 | + | |
| 1383 | + self.deps.need = old_need | |
| 1384 | + self.deps.optional = old_optional | |
| 1385 | + self.verbose_warn_install = old_verbose | |
| 1386 | + | |
| 1387 | + def recommend_sphinx_version(self, virtualenv_cmd): | |
| 1388 | + """ | |
| 1389 | + Provide recommendations for installing or upgrading Sphinx based | |
| 1390 | + on current version. | |
| 1391 | + | |
| 1392 | + The logic here is complex, as it have to deal with different versions: | |
| 1393 | + | |
| 1394 | + - minimal supported version; | |
| 1395 | + - minimal PDF version; | |
| 1396 | + - recommended version. | |
| 1397 | + | |
| 1398 | + It also needs to work fine with both distro's package and | |
| 1399 | + venv/virtualenv | |
| 1400 | + """ | |
| 1401 | + | |
| 1402 | + if self.recommend_python: | |
| 1403 | + cur_ver = sys.version_info[:3] | |
| 1404 | + if cur_ver < MIN_PYTHON_VERSION: | |
| 1405 | + print(f"\nPython version {cur_ver} is incompatible with doc build.\n" \ | |
| 1406 | + "Please upgrade it and re-run.\n") | |
| 1407 | + return | |
| 1408 | + | |
| 1409 | + # Version is OK. Nothing to do. | |
| 1410 | + if self.cur_version != (0, 0, 0) and self.cur_version >= RECOMMENDED_VERSION: | |
| 1411 | + return | |
| 1412 | + | |
| 1413 | + if self.latest_avail_ver: | |
| 1414 | + latest_avail_ver = ver_str(self.latest_avail_ver) | |
| 1415 | + | |
| 1416 | + if not self.need_sphinx: | |
| 1417 | + # sphinx-build is present and its version is >= $min_version | |
| 1418 | + | |
| 1419 | + # only recommend enabling a newer virtenv version if makes sense. | |
| 1420 | + if self.latest_avail_ver and self.latest_avail_ver > self.cur_version: | |
| 1421 | + print(f"\nYou may also use the newer Sphinx version {latest_avail_ver} with:") | |
| 1422 | + if f"{self.virtenv_prefix}" in os.getcwd(): | |
| 1423 | + print("\tdeactivate") | |
| 1424 | + print(f"\t. {self.activate_cmd}") | |
| 1425 | + self.deactivate_help() | |
| 1426 | + return | |
| 1427 | + | |
| 1428 | + if self.latest_avail_ver and self.latest_avail_ver >= RECOMMENDED_VERSION: | |
| 1429 | + return | |
| 1430 | + | |
| 1431 | + if not self.virtualenv: | |
| 1432 | + # No sphinx either via package or via virtenv. As we can't | |
| 1433 | + # Compare the versions here, just return, recommending the | |
| 1434 | + # user to install it from the package distro. | |
| 1435 | + if not self.latest_avail_ver or self.latest_avail_ver == (0, 0, 0): | |
| 1436 | + return | |
| 1437 | + | |
| 1438 | + # User doesn't want a virtenv recommendation, but he already | |
| 1439 | + # installed one via virtenv with a newer version. | |
| 1440 | + # So, print commands to enable it | |
| 1441 | + if self.latest_avail_ver > self.cur_version: | |
| 1442 | + print(f"\nYou may also use the Sphinx virtualenv version {latest_avail_ver} with:") | |
| 1443 | + if f"{self.virtenv_prefix}" in os.getcwd(): | |
| 1444 | + print("\tdeactivate") | |
| 1445 | + print(f"\t. {self.activate_cmd}") | |
| 1446 | + self.deactivate_help() | |
| 1447 | + return | |
| 1448 | + print("\n") | |
| 1449 | + else: | |
| 1450 | + if self.need_sphinx: | |
| 1451 | + self.deps.need += 1 | |
| 1452 | + | |
| 1453 | + # Suggest newer versions if current ones are too old | |
| 1454 | + if self.latest_avail_ver and self.latest_avail_ver >= self.min_version: | |
| 1455 | + if self.latest_avail_ver >= RECOMMENDED_VERSION: | |
| 1456 | + print(f"\nNeed to activate Sphinx (version {latest_avail_ver}) on virtualenv with:") | |
| 1457 | + print(f"\t. {self.activate_cmd}") | |
| 1458 | + self.deactivate_help() | |
| 1459 | + return | |
| 1460 | + | |
| 1461 | + # Version is above the minimal required one, but may be | |
| 1462 | + # below the recommended one. So, print warnings/notes | |
| 1463 | + if self.latest_avail_ver < RECOMMENDED_VERSION: | |
| 1464 | + print(f"Warning: It is recommended at least Sphinx version {RECOMMENDED_VERSION}.") | |
| 1465 | + | |
| 1466 | + # At this point, either it needs Sphinx or upgrade is recommended, | |
| 1467 | + # both via pip | |
| 1468 | + | |
| 1469 | + if self.rec_sphinx_upgrade: | |
| 1470 | + if not self.virtualenv: | |
| 1471 | + print("Instead of install/upgrade Python Sphinx pkg, you could use pip/pypi with:\n\n") | |
| 1472 | + else: | |
| 1473 | + print("To upgrade Sphinx, use:\n\n") | |
| 1474 | + else: | |
| 1475 | + print("\nSphinx needs to be installed either:\n1) via pip/pypi with:\n") | |
| 1476 | + | |
| 1477 | + if not virtualenv_cmd: | |
| 1478 | + print(" Currently not possible.\n") | |
| 1479 | + print(" Please upgrade Python to a newer version and run this script again") | |
| 1480 | + else: | |
| 1481 | + print(f"\t{virtualenv_cmd} {self.virtenv_dir}") | |
| 1482 | + print(f"\t. {self.virtenv_dir}/bin/activate") | |
| 1483 | + print(f"\tpip install -r {self.requirement_file}") | |
| 1484 | + self.deactivate_help() | |
| 1485 | + | |
| 1486 | + if self.package_supported: | |
| 1487 | + self.recommend_package() | |
| 1488 | + | |
| 1489 | + print("\n" \ | |
| 1490 | + " Please note that Sphinx currentlys produce false-positive\n" \ | |
| 1491 | + " warnings when the same name is used for more than one type (functions,\n" \ | |
| 1492 | + " structs, enums,...). This is known Sphinx bug. For more details, see:\n" \ | |
| 1493 | + "\thttps://github.com/sphinx-doc/sphinx/pull/8313") | |
| 1494 | + | |
| 1495 | + def check_needs(self): | |
| 1496 | + """ | |
| 1497 | + Main method that checks needed dependencies and provides | |
| 1498 | + recommendations. | |
| 1499 | + """ | |
| 1500 | + self.python_cmd = sys.executable | |
| 1501 | + | |
| 1502 | + # Check if Sphinx is already accessible from current environment | |
| 1503 | + self.check_sphinx(self.conf) | |
| 1504 | + | |
| 1505 | + if self.system_release: | |
| 1506 | + print(f"Detected OS: {self.system_release}.") | |
| 1507 | + else: | |
| 1508 | + print("Unknown OS") | |
| 1509 | + if self.cur_version != (0, 0, 0): | |
| 1510 | + ver = ver_str(self.cur_version) | |
| 1511 | + print(f"Sphinx version: {ver}\n") | |
| 1512 | + | |
| 1513 | + # Check the type of virtual env, depending on Python version | |
| 1514 | + virtualenv_cmd = None | |
| 1515 | + | |
| 1516 | + if sys.version_info < MIN_PYTHON_VERSION: | |
| 1517 | + min_ver = ver_str(MIN_PYTHON_VERSION) | |
| 1518 | + print(f"ERROR: at least python {min_ver} is required to build the kernel docs") | |
| 1519 | + self.need_sphinx = 1 | |
| 1520 | + | |
| 1521 | + self.venv_ver = self.recommend_sphinx_upgrade() | |
| 1522 | + | |
| 1523 | + if self.need_pip: | |
| 1524 | + if sys.version_info < MIN_PYTHON_VERSION: | |
| 1525 | + self.need_pip = False | |
| 1526 | + print("Warning: python version is not supported.") | |
| 1527 | + else: | |
| 1528 | + virtualenv_cmd = f"{self.python_cmd} -m venv" | |
| 1529 | + self.check_python_module("ensurepip") | |
| 1530 | + | |
| 1531 | + # Check for needed programs/tools | |
| 1532 | + self.check_perl_module("Pod::Usage", DepManager.SYSTEM_MANDATORY) | |
| 1533 | + | |
| 1534 | + self.check_program("make", DepManager.SYSTEM_MANDATORY) | |
| 1535 | + self.check_program("which", DepManager.SYSTEM_MANDATORY) | |
| 1536 | + | |
| 1537 | + self.check_program("dot", DepManager.SYSTEM_OPTIONAL) | |
| 1538 | + self.check_program("convert", DepManager.SYSTEM_OPTIONAL) | |
| 1539 | + | |
| 1540 | + self.check_python_module("yaml") | |
| 1541 | + | |
| 1542 | + if self.pdf: | |
| 1543 | + self.check_program("xelatex", DepManager.PDF_MANDATORY) | |
| 1544 | + self.check_program("rsvg-convert", DepManager.PDF_MANDATORY) | |
| 1545 | + self.check_program("latexmk", DepManager.PDF_MANDATORY) | |
| 1546 | + | |
| 1547 | + # Do distro-specific checks and output distro-install commands | |
| 1548 | + cmd = self.get_install() | |
| 1549 | + if cmd: | |
| 1550 | + print(cmd) | |
| 1551 | + | |
| 1552 | + # If distro requires some special instructions, print here. | |
| 1553 | + # Please notice that get_install() needs to be called first. | |
| 1554 | + if self.distro_msg: | |
| 1555 | + print("\n" + self.distro_msg) | |
| 1556 | + | |
| 1557 | + if not self.python_cmd: | |
| 1558 | + if self.need == 1: | |
| 1559 | + sys.exit("Can't build as 1 mandatory dependency is missing") | |
| 1560 | + elif self.need: | |
| 1561 | + sys.exit(f"Can't build as {self.need} mandatory dependencies are missing") | |
| 1562 | + | |
| 1563 | + # Check if sphinx-build is called sphinx-build-3 | |
| 1564 | + if self.need_symlink: | |
| 1565 | + sphinx_path = self.which("sphinx-build-3") | |
| 1566 | + if sphinx_path: | |
| 1567 | + print(f"\tsudo ln -sf {sphinx_path} /usr/bin/sphinx-build\n") | |
| 1568 | + | |
| 1569 | + self.recommend_sphinx_version(virtualenv_cmd) | |
| 1570 | + print("") | |
| 1571 | + | |
| 1572 | + if not self.deps.optional: | |
| 1573 | + print("All optional dependencies are met.") | |
| 1574 | + | |
| 1575 | + if self.deps.need == 1: | |
| 1576 | + sys.exit("Can't build as 1 mandatory dependency is missing") | |
| 1577 | + elif self.deps.need: | |
| 1578 | + sys.exit(f"Can't build as {self.deps.need} mandatory dependencies are missing") | |
| 1579 | + | |
| 1580 | + print("Needed package dependencies are met.") | |
| 1581 | + | |
| 1582 | +DESCRIPTION = """ | |
| 1583 | +Process some flags related to Sphinx installation and documentation build. | |
| 1584 | +""" | |
| 1585 | + | |
| 1586 | + | |
| 1587 | +def main(): | |
| 1588 | + """Main function""" | |
| 1589 | + parser = argparse.ArgumentParser(description=DESCRIPTION) | |
| 1590 | + | |
| 1591 | + parser.add_argument( | |
| 1592 | + "--no-virtualenv", | |
| 1593 | + action="store_false", | |
| 1594 | + dest="virtualenv", | |
| 1595 | + help="Recommend installing Sphinx instead of using a virtualenv", | |
| 1596 | + ) | |
| 1597 | + | |
| 1598 | + parser.add_argument( | |
| 1599 | + "--no-pdf", | |
| 1600 | + action="store_false", | |
| 1601 | + dest="pdf", | |
| 1602 | + help="Don't check for dependencies required to build PDF docs", | |
| 1603 | + ) | |
| 1604 | + | |
| 1605 | + parser.add_argument( | |
| 1606 | + "--version-check", | |
| 1607 | + action="store_true", | |
| 1608 | + dest="version_check", | |
| 1609 | + help="If version is compatible, don't check for missing dependencies", | |
| 1610 | + ) | |
| 703 | 1611 | |
| 704 | −sub deactivate_help() | |
| 705 | −{ | |
| 706 | − printf "\n If you want to exit the virtualenv, you can use:\n"; | |
| 707 | − printf "\tdeactivate\n"; | |
| 708 | −} | |
| 709 | − | |
| 710 | −sub get_virtenv() | |
| 711 | −{ | |
| 712 | − my $ver; | |
| 713 | − my $min_activate = "$ENV{'PWD'}/${virtenv_prefix}${min_version}/bin/activate"; | |
| 714 | − my @activates = glob "$ENV{'PWD'}/${virtenv_prefix}*/bin/activate"; | |
| 715 | − | |
| 716 | − @activates = sort {$b cmp $a} @activates; | |
| 717 | − | |
| 718 | − foreach my $f (@activates) { | |
| 719 | − next if ($f lt $min_activate); | |
| 720 | − | |
| 721 | − my $sphinx_cmd = $f; | |
| 722 | − $sphinx_cmd =~ s/activate/sphinx-build/; | |
| 723 | − next if (! -f $sphinx_cmd); | |
| 724 | − | |
| 725 | − my $ver = get_sphinx_version($sphinx_cmd); | |
| 726 | − | |
| 727 | − if (!$ver) { | |
| 728 | − $f =~ s#/bin/activate##; | |
| 729 | − print("Warning: virtual environment $f is not working.\nPython version upgrade? Remove it with:\n\n\trm -rf $f\n\n"); | |
| 730 | − } | |
| 731 | − | |
| 732 | − if ($need_sphinx && ($ver ge $min_version)) { | |
| 733 | − return ($f, $ver); | |
| 734 | − } elsif ($ver gt $cur_version) { | |
| 735 | − return ($f, $ver); | |
| 736 | − } | |
| 737 | − } | |
| 738 | − return ("", ""); | |
| 739 | −} | |
| 740 | − | |
| 741 | −sub recommend_sphinx_upgrade() | |
| 742 | −{ | |
| 743 | − my $venv_ver; | |
| 744 | − | |
| 745 | − # Avoid running sphinx-builds from venv if $cur_version is good | |
| 746 | − if ($cur_version && ($cur_version ge $rec_version)) { | |
| 747 | − $latest_avail_ver = $cur_version; | |
| 748 | − return; | |
| 749 | − } | |
| 750 | − | |
| 751 | − # Get the highest version from sphinx_*/bin/sphinx-build and the | |
| 752 | − # corresponding command to activate the venv/virtenv | |
| 753 | − ($activate_cmd, $venv_ver) = get_virtenv(); | |
| 754 | − | |
| 755 | − # Store the highest version from Sphinx existing virtualenvs | |
| 756 | − if (($activate_cmd ne "") && ($venv_ver gt $cur_version)) { | |
| 757 | − $latest_avail_ver = $venv_ver; | |
| 758 | − } else { | |
| 759 | − $latest_avail_ver = $cur_version if ($cur_version); | |
| 760 | − } | |
| 761 | − | |
| 762 | − # As we don't know package version of Sphinx, and there's no | |
| 763 | − # virtual environments, don't check if upgrades are needed | |
| 764 | − if (!$virtualenv) { | |
| 765 | − return if (!$latest_avail_ver); | |
| 766 | − } | |
| 767 | − | |
| 768 | − # Either there are already a virtual env or a new one should be created | |
| 769 | − $need_pip = 1; | |
| 770 | − | |
| 771 | − return if (!$latest_avail_ver); | |
| 772 | − | |
| 773 | − # Return if the reason is due to an upgrade or not | |
| 774 | − if ($latest_avail_ver lt $rec_version) { | |
| 775 | − $rec_sphinx_upgrade = 1; | |
| 776 | − } | |
| 777 | − | |
| 778 | − return $latest_avail_ver; | |
| 779 | −} | |
| 780 | − | |
| 781 | −# | |
| 782 | −# The logic here is complex, as it have to deal with different versions: | |
| 783 | −# - minimal supported version; | |
| 784 | −# - minimal PDF version; | |
| 785 | −# - recommended version. | |
| 786 | −# It also needs to work fine with both distro's package and venv/virtualenv | |
| 787 | −sub recommend_sphinx_version($) | |
| 788 | −{ | |
| 789 | − my $virtualenv_cmd = shift; | |
| 790 | − | |
| 791 | − # Version is OK. Nothing to do. | |
| 792 | − if ($cur_version && ($cur_version ge $rec_version)) { | |
| 793 | − return; | |
| 794 | − }; | |
| 795 | − | |
| 796 | − if (!$need_sphinx) { | |
| 797 | − # sphinx-build is present and its version is >= $min_version | |
| 798 | − | |
| 799 | − #only recommend enabling a newer virtenv version if makes sense. | |
| 800 | − if ($latest_avail_ver gt $cur_version) { | |
| 801 | − printf "\nYou may also use the newer Sphinx version $latest_avail_ver with:\n"; | |
| 802 | − printf "\tdeactivate\n" if ($ENV{'PWD'} =~ /${virtenv_prefix}/); | |
| 803 | − printf "\t. $activate_cmd\n"; | |
| 804 | − deactivate_help(); | |
| 805 | − | |
| 806 | − return; | |
| 807 | − } | |
| 808 | − return if ($latest_avail_ver ge $rec_version); | |
| 809 | − } | |
| 810 | − | |
| 811 | − if (!$virtualenv) { | |
| 812 | − # No sphinx either via package or via virtenv. As we can't | |
| 813 | − # Compare the versions here, just return, recommending the | |
| 814 | − # user to install it from the package distro. | |
| 815 | − return if (!$latest_avail_ver); | |
| 816 | − | |
| 817 | − # User doesn't want a virtenv recommendation, but he already | |
| 818 | − # installed one via virtenv with a newer version. | |
| 819 | − # So, print commands to enable it | |
| 820 | − if ($latest_avail_ver gt $cur_version) { | |
| 821 | − printf "\nYou may also use the Sphinx virtualenv version $latest_avail_ver with:\n"; | |
| 822 | − printf "\tdeactivate\n" if ($ENV{'PWD'} =~ /${virtenv_prefix}/); | |
| 823 | − printf "\t. $activate_cmd\n"; | |
| 824 | − deactivate_help(); | |
| 825 | − | |
| 826 | − return; | |
| 827 | − } | |
| 828 | − print "\n"; | |
| 829 | − } else { | |
| 830 | − $need++ if ($need_sphinx); | |
| 831 | − } | |
| 832 | − | |
| 833 | − # Suggest newer versions if current ones are too old | |
| 834 | − if ($latest_avail_ver && $latest_avail_ver ge $min_version) { | |
| 835 | − # If there's a good enough version, ask the user to enable it | |
| 836 | − if ($latest_avail_ver ge $rec_version) { | |
| 837 | − printf "\nNeed to activate Sphinx (version $latest_avail_ver) on virtualenv with:\n"; | |
| 838 | − printf "\t. $activate_cmd\n"; | |
| 839 | − deactivate_help(); | |
| 840 | − | |
| 841 | − return; | |
| 842 | − } | |
| 843 | − | |
| 844 | − # Version is above the minimal required one, but may be | |
| 845 | − # below the recommended one. So, print warnings/notes | |
| 846 | − | |
| 847 | − if ($latest_avail_ver lt $rec_version) { | |
| 848 | − print "Warning: It is recommended at least Sphinx version $rec_version.\n"; | |
| 849 | − } | |
| 850 | − } | |
| 851 | − | |
| 852 | − # At this point, either it needs Sphinx or upgrade is recommended, | |
| 853 | − # both via pip | |
| 854 | − | |
| 855 | − if ($rec_sphinx_upgrade) { | |
| 856 | − if (!$virtualenv) { | |
| 857 | − print "Instead of install/upgrade Python Sphinx pkg, you could use pip/pypi with:\n\n"; | |
| 858 | − } else { | |
| 859 | − print "To upgrade Sphinx, use:\n\n"; | |
| 860 | − } | |
| 861 | − } else { | |
| 862 | − print "\nSphinx needs to be installed either:\n1) via pip/pypi with:\n\n"; | |
| 863 | − } | |
| 864 | − | |
| 865 | − $python_cmd = find_python_no_venv(); | |
| 866 | − | |
| 867 | − printf "\t$virtualenv_cmd $virtenv_dir\n"; | |
| 868 | − | |
| 869 | − printf "\t. $virtenv_dir/bin/activate\n"; | |
| 870 | − printf "\tpip install -r $requirement_file\n"; | |
| 871 | − deactivate_help(); | |
| 872 | − | |
| 873 | − printf "\n2) As a package with:\n"; | |
| 874 | − | |
| 875 | − my $old_need = $need; | |
| 876 | − my $old_optional = $optional; | |
| 877 | − %missing = (); | |
| 878 | − $pdf = 0; | |
| 879 | − $optional = 0; | |
| 880 | − $install = ""; | |
| 881 | − $verbose_warn_install = 0; | |
| 882 | − | |
| 883 | − add_package("python-sphinx", 0); | |
| 884 | − | |
| 885 | − check_distros(); | |
| 886 | − | |
| 887 | − $need = $old_need; | |
| 888 | − $optional = $old_optional; | |
| 889 | − | |
| 890 | − printf "\n Please note that Sphinx >= 3.0 will currently produce false-positive\n"; | |
| 891 | − printf " warning when the same name is used for more than one type (functions,\n"; | |
| 892 | − printf " structs, enums,...). This is known Sphinx bug. For more details, see:\n"; | |
| 893 | − printf "\thttps://github.com/sphinx-doc/sphinx/pull/8313\n"; | |
| 894 | −} | |
| 895 | − | |
| 896 | −sub check_needs() | |
| 897 | −{ | |
| 898 | − # Check if Sphinx is already accessible from current environment | |
| 899 | − check_sphinx(); | |
| 900 | − | |
| 901 | − if ($system_release) { | |
| 902 | − print "Detected OS: $system_release.\n"; | |
| 903 | − } else { | |
| 904 | − print "Unknown OS\n"; | |
| 905 | − } | |
| 906 | − printf "Sphinx version: %s\n\n", $cur_version if ($cur_version); | |
| 907 | − | |
| 908 | − # Check python command line, trying first python3 | |
| 909 | − $python_cmd = findprog("python3"); | |
| 910 | − $python_cmd = check_program("python", 0) if (!$python_cmd); | |
| 911 | − | |
| 912 | − # Check the type of virtual env, depending on Python version | |
| 913 | − if ($python_cmd) { | |
| 914 | − if ($virtualenv) { | |
| 915 | − my $tmp = qx($python_cmd --version 2>&1); | |
| 916 | − if ($tmp =~ m/(\d+\.)(\d+\.)/) { | |
| 917 | − if ($1 < 3) { | |
| 918 | − # Fail if it finds python2 (or worse) | |
| 919 | − die "Python 3 is required to build the kernel docs\n"; | |
| 920 | − } | |
| 921 | − if ($1 == 3 && $2 < 3) { | |
| 922 | − # Need Python 3.3 or upper for venv | |
| 923 | − $need_virtualenv = 1; | |
| 924 | − } | |
| 925 | − } else { | |
| 926 | − die "Warning: couldn't identify $python_cmd version!"; | |
| 927 | − } | |
| 928 | − } else { | |
| 929 | − add_package("python-sphinx", 0); | |
| 930 | − } | |
| 931 | − } | |
| 932 | − | |
| 933 | − my $venv_ver = recommend_sphinx_upgrade(); | |
| 934 | − | |
| 935 | − my $virtualenv_cmd; | |
| 936 | − | |
| 937 | − if ($need_pip) { | |
| 938 | − # Set virtualenv command line, if python < 3.3 | |
| 939 | − if ($need_virtualenv) { | |
| 940 | − $virtualenv_cmd = findprog("virtualenv-3"); | |
| 941 | − $virtualenv_cmd = findprog("virtualenv-3.5") if (!$virtualenv_cmd); | |
| 942 | − if (!$virtualenv_cmd) { | |
| 943 | − check_program("virtualenv", 0); | |
| 944 | − $virtualenv_cmd = "virtualenv"; | |
| 945 | − } | |
| 946 | − } else { | |
| 947 | − $virtualenv_cmd = "$python_cmd -m venv"; | |
| 948 | − check_python_module("ensurepip", 0); | |
| 949 | − } | |
| 950 | − } | |
| 951 | − | |
| 952 | − # Check for needed programs/tools | |
| 953 | − check_perl_module("Pod::Usage", 0); | |
| 954 | − check_python_module("yaml", 0); | |
| 955 | − check_program("make", 0); | |
| 956 | − check_program("gcc", 0); | |
| 957 | − check_program("dot", 1); | |
| 958 | − check_program("convert", 1); | |
| 959 | − | |
| 960 | − # Extra PDF files - should use 2 for is_optional | |
| 961 | − check_program("xelatex", 2) if ($pdf); | |
| 962 | − check_program("rsvg-convert", 2) if ($pdf); | |
| 963 | − check_program("latexmk", 2) if ($pdf); | |
| 964 | − | |
| 965 | − # Do distro-specific checks and output distro-install commands | |
| 966 | − check_distros(); | |
| 967 | − | |
| 968 | − if (!$python_cmd) { | |
| 969 | − if ($need == 1) { | |
| 970 | − die "Can't build as $need mandatory dependency is missing"; | |
| 971 | − } elsif ($need) { | |
| 972 | − die "Can't build as $need mandatory dependencies are missing"; | |
| 973 | − } | |
| 974 | − } | |
| 975 | − | |
| 976 | − # Check if sphinx-build is called sphinx-build-3 | |
| 977 | − if ($need_symlink) { | |
| 978 | − printf "\tsudo ln -sf %s /usr/bin/sphinx-build\n\n", | |
| 979 | − which("sphinx-build-3"); | |
| 980 | − } | |
| 981 | − | |
| 982 | − recommend_sphinx_version($virtualenv_cmd); | |
| 983 | − printf "\n"; | |
| 984 | − | |
| 985 | − print "All optional dependencies are met.\n" if (!$optional); | |
| 986 | − | |
| 987 | − if ($need == 1) { | |
| 988 | − die "Can't build as $need mandatory dependency is missing"; | |
| 989 | − } elsif ($need) { | |
| 990 | − die "Can't build as $need mandatory dependencies are missing"; | |
| 991 | − } | |
| 992 | − | |
| 993 | − print "Needed package dependencies are met.\n"; | |
| 994 | −} | |
| 995 | − | |
| 996 | −# | |
| 997 | −# Main | |
| 998 | −# | |
| 999 | − | |
| 1000 | −while (@ARGV) { | |
| 1001 | − my $arg = shift(@ARGV); | |
| 1002 | − | |
| 1003 | − if ($arg eq "--no-virtualenv") { | |
| 1004 | − $virtualenv = 0; | |
| 1005 | − } elsif ($arg eq "--no-pdf"){ | |
| 1006 | − $pdf = 0; | |
| 1007 | − } elsif ($arg eq "--version-check"){ | |
| 1008 | − $version_check = 1; | |
| 1009 | − } else { | |
| 1010 | − print "Usage:\n\t$0 <--no-virtualenv> <--no-pdf> <--version-check>\n\n"; | |
| 1011 | − print "Where:\n"; | |
| 1012 | − print "\t--no-virtualenv\t- Recommend installing Sphinx instead of using a virtualenv\n"; | |
| 1013 | − print "\t--version-check\t- if version is compatible, don't check for missing dependencies\n"; | |
| 1014 | − print "\t--no-pdf\t- don't check for dependencies required to build PDF docs\n\n"; | |
| 1015 | − exit -1; | |
| 1016 | − } | |
| 1017 | −} | |
| 1018 | − | |
| 1019 | −# | |
| 1020 | −# Determine the system type. There's no standard unique way that would | |
| 1021 | −# work with all distros with a minimal package install. So, several | |
| 1022 | −# methods are used here. | |
| 1023 | −# | |
| 1024 | −# By default, it will use lsb_release function. If not available, it will | |
| 1025 | −# fail back to reading the known different places where the distro name | |
| 1026 | −# is stored | |
| 1027 | −# | |
| 1612 | + args = parser.parse_args() | |
| 1613 | + | |
| 1614 | + checker = SphinxDependencyChecker(args) | |
| 1028 | 1615 | |
| 1029 | −$system_release = qx(lsb_release -d) if which("lsb_release"); | |
| 1030 | −$system_release =~ s/Description:\s*// if ($system_release); | |
| 1031 | −$system_release = catcheck("/etc/system-release") if !$system_release; | |
| 1032 | −$system_release = catcheck("/etc/redhat-release") if !$system_release; | |
| 1033 | −$system_release = catcheck("/etc/lsb-release") if !$system_release; | |
| 1034 | −$system_release = catcheck("/etc/gentoo-release") if !$system_release; | |
| 1035 | − | |
| 1036 | −# This seems more common than LSB these days | |
| 1037 | −if (!$system_release) { | |
| 1038 | − my %os_var; | |
| 1039 | − if (open IN, "cat /etc/os-release|") { | |
| 1040 | − while (<IN>) { | |
| 1041 | − if (m/^([\w\d\_]+)=\"?([^\"]*)\"?\n/) { | |
| 1042 | − $os_var{$1}=$2; | |
| 1043 | − } | |
| 1044 | − } | |
| 1045 | − $system_release = $os_var{"NAME"}; | |
| 1046 | − if (defined($os_var{"VERSION_ID"})) { | |
| 1047 | − $system_release .= " " . $os_var{"VERSION_ID"} if (defined($os_var{"VERSION_ID"})); | |
| 1048 | − } else { | |
| 1049 | − $system_release .= " " . $os_var{"VERSION"}; | |
| 1050 | − } | |
| 1051 | − } | |
| 1052 | −} | |
| 1053 | −$system_release = catcheck("/etc/issue") if !$system_release; | |
| 1054 | −$system_release =~ s/\s+$//; | |
| 1055 | − | |
| 1056 | −check_needs; | |
| 1616 | + checker.check_python() | |
| 1617 | + checker.check_needs() | |
| 1618 | + | |
| 1619 | +# Call main if not used as module | |
| 1620 | +if __name__ == "__main__": | |
| 1621 | + main() | |
| @@ -0,0 +1,54 @@ | ||
| 1 | +#! /usr/bin/env python3 | |
| 2 | +# SPDX-License-Identifier: GPL-2.0 | |
| 3 | +# | |
| 4 | +# Copyright © 2025, Oracle and/or its affiliates. | |
| 5 | +# Author: Vegard Nossum <vegard.nossum@oracle.com> | |
| 6 | + | |
| 7 | +"""Generate HTML redirects for renamed Documentation/**.rst files using | |
| 8 | +the output of tools/docs/gen-renames.py. | |
| 9 | + | |
| 10 | +Example: | |
| 11 | + | |
| 12 | + tools/docs/gen-redirects.py --output Documentation/output/ < Documentation/.renames.txt | |
| 13 | +""" | |
| 14 | + | |
| 15 | +import argparse | |
| 16 | +import os | |
| 17 | +import sys | |
| 18 | + | |
| 19 | +parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) | |
| 20 | +parser.add_argument('-o', '--output', help='output directory') | |
| 21 | + | |
| 22 | +args = parser.parse_args() | |
| 23 | + | |
| 24 | +for line in sys.stdin: | |
| 25 | + line = line.rstrip('\n') | |
| 26 | + | |
| 27 | + old_name, new_name = line.split(' ', 2) | |
| 28 | + | |
| 29 | + old_html_path = os.path.join(args.output, old_name + '.html') | |
| 30 | + new_html_path = os.path.join(args.output, new_name + '.html') | |
| 31 | + | |
| 32 | + if not os.path.exists(new_html_path): | |
| 33 | + print(f"warning: target does not exist: {new_html_path} (redirect from {old_html_path})") | |
| 34 | + continue | |
| 35 | + | |
| 36 | + old_html_dir = os.path.dirname(old_html_path) | |
| 37 | + if not os.path.exists(old_html_dir): | |
| 38 | + os.makedirs(old_html_dir) | |
| 39 | + | |
| 40 | + relpath = os.path.relpath(new_name, os.path.dirname(old_name)) + '.html' | |
| 41 | + | |
| 42 | + with open(old_html_path, 'w') as f: | |
| 43 | + print(f"""\ | |
| 44 | +<!DOCTYPE html> | |
| 45 | + | |
| 46 | +<html lang="en"> | |
| 47 | +<head> | |
| 48 | + <title>This page has moved</title> | |
| 49 | + <meta http-equiv="refresh" content="0; url={relpath}"> | |
| 50 | +</head> | |
| 51 | +<body> | |
| 52 | +<p>This page has moved to <a href="{relpath}">{new_name}</a>.</p> | |
| 53 | +</body> | |
| 54 | +</html>""", file=f) | |
| @@ -0,0 +1,130 @@ | ||
| 1 | +#! /usr/bin/env python3 | |
| 2 | +# SPDX-License-Identifier: GPL-2.0 | |
| 3 | +# | |
| 4 | +# Copyright © 2025, Oracle and/or its affiliates. | |
| 5 | +# Author: Vegard Nossum <vegard.nossum@oracle.com> | |
| 6 | + | |
| 7 | +"""Trawl repository history for renames of Documentation/**.rst files. | |
| 8 | + | |
| 9 | +Example: | |
| 10 | + | |
| 11 | + tools/docs/gen-renames.py --rev HEAD > Documentation/.renames.txt | |
| 12 | +""" | |
| 13 | + | |
| 14 | +import argparse | |
| 15 | +import itertools | |
| 16 | +import os | |
| 17 | +import subprocess | |
| 18 | +import sys | |
| 19 | + | |
| 20 | +parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) | |
| 21 | +parser.add_argument('--rev', default='HEAD', help='generate renames up to this revision') | |
| 22 | + | |
| 23 | +args = parser.parse_args() | |
| 24 | + | |
| 25 | +def normalize(path): | |
| 26 | + prefix = 'Documentation/' | |
| 27 | + suffix = '.rst' | |
| 28 | + | |
| 29 | + assert path.startswith(prefix) | |
| 30 | + assert path.endswith(suffix) | |
| 31 | + | |
| 32 | + return path[len(prefix):-len(suffix)] | |
| 33 | + | |
| 34 | +class Name(object): | |
| 35 | + def __init__(self, name): | |
| 36 | + self.names = [name] | |
| 37 | + | |
| 38 | + def rename(self, new_name): | |
| 39 | + self.names.append(new_name) | |
| 40 | + | |
| 41 | +names = { | |
| 42 | +} | |
| 43 | + | |
| 44 | +for line in subprocess.check_output([ | |
| 45 | + 'git', 'log', | |
| 46 | + '--reverse', | |
| 47 | + '--oneline', | |
| 48 | + '--find-renames', | |
| 49 | + '--diff-filter=RD', | |
| 50 | + '--name-status', | |
| 51 | + '--format=commit %H', | |
| 52 | + # ~v4.8-ish is when Sphinx/.rst was added in the first place | |
| 53 | + f'v4.8..{args.rev}', | |
| 54 | + '--', | |
| 55 | + 'Documentation/' | |
| 56 | +], text=True).splitlines(): | |
| 57 | + # rename | |
| 58 | + if line.startswith('R'): | |
| 59 | + _, old, new = line[1:].split('\t', 2) | |
| 60 | + | |
| 61 | + if old.endswith('.rst') and new.endswith('.rst'): | |
| 62 | + old = normalize(old) | |
| 63 | + new = normalize(new) | |
| 64 | + | |
| 65 | + name = names.get(old) | |
| 66 | + if name is None: | |
| 67 | + name = Name(old) | |
| 68 | + else: | |
| 69 | + del names[old] | |
| 70 | + | |
| 71 | + name.rename(new) | |
| 72 | + names[new] = name | |
| 73 | + | |
| 74 | + continue | |
| 75 | + | |
| 76 | + # delete | |
| 77 | + if line.startswith('D'): | |
| 78 | + _, old = line.split('\t', 1) | |
| 79 | + | |
| 80 | + if old.endswith('.rst'): | |
| 81 | + old = normalize(old) | |
| 82 | + | |
| 83 | + # TODO: we could save added/modified files as well and propose | |
| 84 | + # them as alternatives | |
| 85 | + name = names.get(old) | |
| 86 | + if name is None: | |
| 87 | + pass | |
| 88 | + else: | |
| 89 | + del names[old] | |
| 90 | + | |
| 91 | + continue | |
| 92 | + | |
| 93 | +# | |
| 94 | +# Get the set of current files so we can sanity check that we aren't | |
| 95 | +# redirecting any of those | |
| 96 | +# | |
| 97 | + | |
| 98 | +current_files = set() | |
| 99 | +for line in subprocess.check_output([ | |
| 100 | + 'git', 'ls-tree', | |
| 101 | + '-r', | |
| 102 | + '--name-only', | |
| 103 | + args.rev, | |
| 104 | + 'Documentation/', | |
| 105 | +], text=True).splitlines(): | |
| 106 | + if line.endswith('.rst'): | |
| 107 | + current_files.add(normalize(line)) | |
| 108 | + | |
| 109 | +# | |
| 110 | +# Format/group/output result | |
| 111 | +# | |
| 112 | + | |
| 113 | +result = [] | |
| 114 | +for _, v in names.items(): | |
| 115 | + old_names = v.names[:-1] | |
| 116 | + new_name = v.names[-1] | |
| 117 | + | |
| 118 | + for old_name in old_names: | |
| 119 | + if old_name == new_name: | |
| 120 | + # A file was renamed to its new name twice; don't redirect that | |
| 121 | + continue | |
| 122 | + | |
| 123 | + if old_name in current_files: | |
| 124 | + # A file was recreated with a former name; don't redirect those | |
| 125 | + continue | |
| 126 | + | |
| 127 | + result.append((old_name, new_name)) | |
| 128 | + | |
| 129 | +for old_name, new_name in sorted(result): | |
| 130 | + print(f"{old_name} {new_name}") | |
Empty file added.
| @@ -0,0 +1,70 @@ | ||
| 1 | +#!/usr/bin/env python3 | |
| 2 | +# SPDX-License-Identifier: GPL-2.0 | |
| 3 | +# Copyright (c) 2025 by Mauro Carvalho Chehab <mchehab@kernel.org>. | |
| 4 | + | |
| 5 | +""" | |
| 6 | +Ancillary argparse HelpFormatter class that works on a similar way as | |
| 7 | +argparse.RawDescriptionHelpFormatter, e.g. description maintains line | |
| 8 | +breaks, but it also implement transformations to the help text. The | |
| 9 | +actual transformations ar given by enrich_text(), if the output is tty. | |
| 10 | + | |
| 11 | +Currently, the follow transformations are done: | |
| 12 | + | |
| 13 | + - Positional arguments are shown in upper cases; | |
| 14 | + - if output is TTY, ``var`` and positional arguments are shown prepended | |
| 15 | + by an ANSI SGR code. This is usually translated to bold. On some | |
| 16 | + terminals, like, konsole, this is translated into a colored bold text. | |
| 17 | +""" | |
| 18 | + | |
| 19 | +import argparse | |
| 20 | +import re | |
| 21 | +import sys | |
| 22 | + | |
| 23 | +class EnrichFormatter(argparse.HelpFormatter): | |
| 24 | + """ | |
| 25 | + Better format the output, making easier to identify the positional args | |
| 26 | + and how they're used at the __doc__ description. | |
| 27 | + """ | |
| 28 | + def __init__(self, *args, **kwargs): | |
| 29 | + """Initialize class and check if is TTY""" | |
| 30 | + super().__init__(*args, **kwargs) | |
| 31 | + self._tty = sys.stdout.isatty() | |
| 32 | + | |
| 33 | + def enrich_text(self, text): | |
| 34 | + """Handle ReST markups (currently, only ``foo``)""" | |
| 35 | + if self._tty and text: | |
| 36 | + # Replace ``text`` with ANSI SGR (bold) | |
| 37 | + return re.sub(r'\`\`(.+?)\`\`', | |
| 38 | + lambda m: f'\033[1m{m.group(1)}\033[0m', text) | |
| 39 | + return text | |
| 40 | + | |
| 41 | + def _fill_text(self, text, width, indent): | |
| 42 | + """Enrich descriptions with markups on it""" | |
| 43 | + enriched = self.enrich_text(text) | |
| 44 | + return "\n".join(indent + line for line in enriched.splitlines()) | |
| 45 | + | |
| 46 | + def _format_usage(self, usage, actions, groups, prefix): | |
| 47 | + """Enrich positional arguments at usage: line""" | |
| 48 | + | |
| 49 | + prog = self._prog | |
| 50 | + parts = [] | |
| 51 | + | |
| 52 | + for action in actions: | |
| 53 | + if action.option_strings: | |
| 54 | + opt = action.option_strings[0] | |
| 55 | + if action.nargs != 0: | |
| 56 | + opt += f" {action.dest.upper()}" | |
| 57 | + parts.append(f"[{opt}]") | |
| 58 | + else: | |
| 59 | + # Positional argument | |
| 60 | + parts.append(self.enrich_text(f"``{action.dest.upper()}``")) | |
| 61 | + | |
| 62 | + usage_text = f"{prefix or 'usage: '} {prog} {' '.join(parts)}\n" | |
| 63 | + return usage_text | |
| 64 | + | |
| 65 | + def _format_action_invocation(self, action): | |
| 66 | + """Enrich argument names""" | |
| 67 | + if not action.option_strings: | |
| 68 | + return self.enrich_text(f"``{action.dest.upper()}``") | |
| 69 | + | |
| 70 | + return ", ".join(action.option_strings) | |
| @@ -0,0 +1,452 @@ | ||
| 1 | +#!/usr/bin/env python3 | |
| 2 | +# SPDX-License-Identifier: GPL-2.0 | |
| 3 | +# Copyright (c) 2016-2025 by Mauro Carvalho Chehab <mchehab@kernel.org>. | |
| 4 | +# pylint: disable=R0912,R0915 | |
| 5 | + | |
| 6 | +""" | |
| 7 | +Parse a source file or header, creating ReStructured Text cross references. | |
| 8 | + | |
| 9 | +It accepts an optional file to change the default symbol reference or to | |
| 10 | +suppress symbols from the output. | |
| 11 | + | |
| 12 | +It is capable of identifying defines, functions, structs, typedefs, | |
| 13 | +enums and enum symbols and create cross-references for all of them. | |
| 14 | +It is also capable of distinguish #define used for specifying a Linux | |
| 15 | +ioctl. | |
| 16 | + | |
| 17 | +The optional rules file contains a set of rules like: | |
| 18 | + | |
| 19 | + ignore ioctl VIDIOC_ENUM_FMT | |
| 20 | + replace ioctl VIDIOC_DQBUF vidioc_qbuf | |
| 21 | + replace define V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ :c:type:`v4l2_event_motion_det` | |
| 22 | +""" | |
| 23 | + | |
| 24 | +import os | |
| 25 | +import re | |
| 26 | +import sys | |
| 27 | + | |
| 28 | + | |
| 29 | +class ParseDataStructs: | |
| 30 | + """ | |
| 31 | + Creates an enriched version of a Kernel header file with cross-links | |
| 32 | + to each C data structure type. | |
| 33 | + | |
| 34 | + It is meant to allow having a more comprehensive documentation, where | |
| 35 | + uAPI headers will create cross-reference links to the code. | |
| 36 | + | |
| 37 | + It is capable of identifying defines, functions, structs, typedefs, | |
| 38 | + enums and enum symbols and create cross-references for all of them. | |
| 39 | + It is also capable of distinguish #define used for specifying a Linux | |
| 40 | + ioctl. | |
| 41 | + | |
| 42 | + By default, it create rules for all symbols and defines, but it also | |
| 43 | + allows parsing an exception file. Such file contains a set of rules | |
| 44 | + using the syntax below: | |
| 45 | + | |
| 46 | + 1. Ignore rules: | |
| 47 | + | |
| 48 | + ignore <type> <symbol>` | |
| 49 | + | |
| 50 | + Removes the symbol from reference generation. | |
| 51 | + | |
| 52 | + 2. Replace rules: | |
| 53 | + | |
| 54 | + replace <type> <old_symbol> <new_reference> | |
| 55 | + | |
| 56 | + Replaces how old_symbol with a new reference. The new_reference can be: | |
| 57 | + - A simple symbol name; | |
| 58 | + - A full Sphinx reference. | |
| 59 | + | |
| 60 | + On both cases, <type> can be: | |
| 61 | + - ioctl: for defines that end with _IO*, e.g. ioctl definitions | |
| 62 | + - define: for other defines | |
| 63 | + - symbol: for symbols defined within enums; | |
| 64 | + - typedef: for typedefs; | |
| 65 | + - enum: for the name of a non-anonymous enum; | |
| 66 | + - struct: for structs. | |
| 67 | + | |
| 68 | + Examples: | |
| 69 | + | |
| 70 | + ignore define __LINUX_MEDIA_H | |
| 71 | + ignore ioctl VIDIOC_ENUM_FMT | |
| 72 | + replace ioctl VIDIOC_DQBUF vidioc_qbuf | |
| 73 | + replace define V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ :c:type:`v4l2_event_motion_det` | |
| 74 | + """ | |
| 75 | + | |
| 76 | + # Parser regexes with multiple ways to capture enums and structs | |
| 77 | + RE_ENUMS = [ | |
| 78 | + re.compile(r"^\s*enum\s+([\w_]+)\s*\{"), | |
| 79 | + re.compile(r"^\s*enum\s+([\w_]+)\s*$"), | |
| 80 | + re.compile(r"^\s*typedef\s*enum\s+([\w_]+)\s*\{"), | |
| 81 | + re.compile(r"^\s*typedef\s*enum\s+([\w_]+)\s*$"), | |
| 82 | + ] | |
| 83 | + RE_STRUCTS = [ | |
| 84 | + re.compile(r"^\s*struct\s+([_\w][\w\d_]+)\s*\{"), | |
| 85 | + re.compile(r"^\s*struct\s+([_\w][\w\d_]+)$"), | |
| 86 | + re.compile(r"^\s*typedef\s*struct\s+([_\w][\w\d_]+)\s*\{"), | |
| 87 | + re.compile(r"^\s*typedef\s*struct\s+([_\w][\w\d_]+)$"), | |
| 88 | + ] | |
| 89 | + | |
| 90 | + # FIXME: the original code was written a long time before Sphinx C | |
| 91 | + # domain to have multiple namespaces. To avoid to much turn at the | |
| 92 | + # existing hyperlinks, the code kept using "c:type" instead of the | |
| 93 | + # right types. To change that, we need to change the types not only | |
| 94 | + # here, but also at the uAPI media documentation. | |
| 95 | + DEF_SYMBOL_TYPES = { | |
| 96 | + "ioctl": { | |
| 97 | + "prefix": "\\ ", | |
| 98 | + "suffix": "\\ ", | |
| 99 | + "ref_type": ":ref", | |
| 100 | + "description": "IOCTL Commands", | |
| 101 | + }, | |
| 102 | + "define": { | |
| 103 | + "prefix": "\\ ", | |
| 104 | + "suffix": "\\ ", | |
| 105 | + "ref_type": ":ref", | |
| 106 | + "description": "Macros and Definitions", | |
| 107 | + }, | |
| 108 | + # We're calling each definition inside an enum as "symbol" | |
| 109 | + "symbol": { | |
| 110 | + "prefix": "\\ ", | |
| 111 | + "suffix": "\\ ", | |
| 112 | + "ref_type": ":ref", | |
| 113 | + "description": "Enumeration values", | |
| 114 | + }, | |
| 115 | + "typedef": { | |
| 116 | + "prefix": "\\ ", | |
| 117 | + "suffix": "\\ ", | |
| 118 | + "ref_type": ":c:type", | |
| 119 | + "description": "Type Definitions", | |
| 120 | + }, | |
| 121 | + # This is the description of the enum itself | |
| 122 | + "enum": { | |
| 123 | + "prefix": "\\ ", | |
| 124 | + "suffix": "\\ ", | |
| 125 | + "ref_type": ":c:type", | |
| 126 | + "description": "Enumerations", | |
| 127 | + }, | |
| 128 | + "struct": { | |
| 129 | + "prefix": "\\ ", | |
| 130 | + "suffix": "\\ ", | |
| 131 | + "ref_type": ":c:type", | |
| 132 | + "description": "Structures", | |
| 133 | + }, | |
| 134 | + } | |
| 135 | + | |
| 136 | + def __init__(self, debug: bool = False): | |
| 137 | + """Initialize internal vars""" | |
| 138 | + self.debug = debug | |
| 139 | + self.data = "" | |
| 140 | + | |
| 141 | + self.symbols = {} | |
| 142 | + | |
| 143 | + for symbol_type in self.DEF_SYMBOL_TYPES: | |
| 144 | + self.symbols[symbol_type] = {} | |
| 145 | + | |
| 146 | + def store_type(self, symbol_type: str, symbol: str, | |
| 147 | + ref_name: str = None, replace_underscores: bool = True): | |
| 148 | + """ | |
| 149 | + Stores a new symbol at self.symbols under symbol_type. | |
| 150 | + | |
| 151 | + By default, underscores are replaced by "-" | |
| 152 | + """ | |
| 153 | + defs = self.DEF_SYMBOL_TYPES[symbol_type] | |
| 154 | + | |
| 155 | + prefix = defs.get("prefix", "") | |
| 156 | + suffix = defs.get("suffix", "") | |
| 157 | + ref_type = defs.get("ref_type") | |
| 158 | + | |
| 159 | + # Determine ref_link based on symbol type | |
| 160 | + if ref_type: | |
| 161 | + if symbol_type == "enum": | |
| 162 | + ref_link = f"{ref_type}:`{symbol}`" | |
| 163 | + else: | |
| 164 | + if not ref_name: | |
| 165 | + ref_name = symbol.lower() | |
| 166 | + | |
| 167 | + # c-type references don't support hash | |
| 168 | + if ref_type == ":ref" and replace_underscores: | |
| 169 | + ref_name = ref_name.replace("_", "-") | |
| 170 | + | |
| 171 | + ref_link = f"{ref_type}:`{symbol} <{ref_name}>`" | |
| 172 | + else: | |
| 173 | + ref_link = symbol | |
| 174 | + | |
| 175 | + self.symbols[symbol_type][symbol] = f"{prefix}{ref_link}{suffix}" | |
| 176 | + | |
| 177 | + def store_line(self, line): | |
| 178 | + """Stores a line at self.data, properly indented""" | |
| 179 | + line = " " + line.expandtabs() | |
| 180 | + self.data += line.rstrip(" ") | |
| 181 | + | |
| 182 | + def parse_file(self, file_in: str): | |
| 183 | + """Reads a C source file and get identifiers""" | |
| 184 | + self.data = "" | |
| 185 | + is_enum = False | |
| 186 | + is_comment = False | |
| 187 | + multiline = "" | |
| 188 | + | |
| 189 | + with open(file_in, "r", | |
| 190 | + encoding="utf-8", errors="backslashreplace") as f: | |
| 191 | + for line_no, line in enumerate(f): | |
| 192 | + self.store_line(line) | |
| 193 | + line = line.strip("\n") | |
| 194 | + | |
| 195 | + # Handle continuation lines | |
| 196 | + if line.endswith(r"\\"): | |
| 197 | + multiline += line[-1] | |
| 198 | + continue | |
| 199 | + | |
| 200 | + if multiline: | |
| 201 | + line = multiline + line | |
| 202 | + multiline = "" | |
| 203 | + | |
| 204 | + # Handle comments. They can be multilined | |
| 205 | + if not is_comment: | |
| 206 | + if re.search(r"/\*.*", line): | |
| 207 | + is_comment = True | |
| 208 | + else: | |
| 209 | + # Strip C99-style comments | |
| 210 | + line = re.sub(r"(//.*)", "", line) | |
| 211 | + | |
| 212 | + if is_comment: | |
| 213 | + if re.search(r".*\*/", line): | |
| 214 | + is_comment = False | |
| 215 | + else: | |
| 216 | + multiline = line | |
| 217 | + continue | |
| 218 | + | |
| 219 | + # At this point, line variable may be a multilined statement, | |
| 220 | + # if lines end with \ or if they have multi-line comments | |
| 221 | + # With that, it can safely remove the entire comments, | |
| 222 | + # and there's no need to use re.DOTALL for the logic below | |
| 223 | + | |
| 224 | + line = re.sub(r"(/\*.*\*/)", "", line) | |
| 225 | + if not line.strip(): | |
| 226 | + continue | |
| 227 | + | |
| 228 | + # It can be useful for debug purposes to print the file after | |
| 229 | + # having comments stripped and multi-lines grouped. | |
| 230 | + if self.debug > 1: | |
| 231 | + print(f"line {line_no + 1}: {line}") | |
| 232 | + | |
| 233 | + # Now the fun begins: parse each type and store it. | |
| 234 | + | |
| 235 | + # We opted for a two parsing logic here due to: | |
| 236 | + # 1. it makes easier to debug issues not-parsed symbols; | |
| 237 | + # 2. we want symbol replacement at the entire content, not | |
| 238 | + # just when the symbol is detected. | |
| 239 | + | |
| 240 | + if is_enum: | |
| 241 | + match = re.match(r"^\s*([_\w][\w\d_]+)\s*[\,=]?", line) | |
| 242 | + if match: | |
| 243 | + self.store_type("symbol", match.group(1)) | |
| 244 | + if "}" in line: | |
| 245 | + is_enum = False | |
| 246 | + continue | |
| 247 | + | |
| 248 | + match = re.match(r"^\s*#\s*define\s+([\w_]+)\s+_IO", line) | |
| 249 | + if match: | |
| 250 | + self.store_type("ioctl", match.group(1), | |
| 251 | + replace_underscores=False) | |
| 252 | + continue | |
| 253 | + | |
| 254 | + match = re.match(r"^\s*#\s*define\s+([\w_]+)(\s+|$)", line) | |
| 255 | + if match: | |
| 256 | + self.store_type("define", match.group(1)) | |
| 257 | + continue | |
| 258 | + | |
| 259 | + match = re.match(r"^\s*typedef\s+([_\w][\w\d_]+)\s+(.*)\s+([_\w][\w\d_]+);", | |
| 260 | + line) | |
| 261 | + if match: | |
| 262 | + name = match.group(2).strip() | |
| 263 | + symbol = match.group(3) | |
| 264 | + self.store_type("typedef", symbol, ref_name=name) | |
| 265 | + continue | |
| 266 | + | |
| 267 | + for re_enum in self.RE_ENUMS: | |
| 268 | + match = re_enum.match(line) | |
| 269 | + if match: | |
| 270 | + self.store_type("enum", match.group(1)) | |
| 271 | + is_enum = True | |
| 272 | + break | |
| 273 | + | |
| 274 | + for re_struct in self.RE_STRUCTS: | |
| 275 | + match = re_struct.match(line) | |
| 276 | + if match: | |
| 277 | + self.store_type("struct", match.group(1)) | |
| 278 | + break | |
| 279 | + | |
| 280 | + def process_exceptions(self, fname: str): | |
| 281 | + """ | |
| 282 | + Process exceptions file with rules to ignore or replace references. | |
| 283 | + """ | |
| 284 | + if not fname: | |
| 285 | + return | |
| 286 | + | |
| 287 | + name = os.path.basename(fname) | |
| 288 | + | |
| 289 | + with open(fname, "r", encoding="utf-8", errors="backslashreplace") as f: | |
| 290 | + for ln, line in enumerate(f): | |
| 291 | + ln += 1 | |
| 292 | + line = line.strip() | |
| 293 | + if not line or line.startswith("#"): | |
| 294 | + continue | |
| 295 | + | |
| 296 | + # Handle ignore rules | |
| 297 | + match = re.match(r"^ignore\s+(\w+)\s+(\S+)", line) | |
| 298 | + if match: | |
| 299 | + c_type = match.group(1) | |
| 300 | + symbol = match.group(2) | |
| 301 | + | |
| 302 | + if c_type not in self.DEF_SYMBOL_TYPES: | |
| 303 | + sys.exit(f"{name}:{ln}: {c_type} is invalid") | |
| 304 | + | |
| 305 | + d = self.symbols[c_type] | |
| 306 | + if symbol in d: | |
| 307 | + del d[symbol] | |
| 308 | + | |
| 309 | + continue | |
| 310 | + | |
| 311 | + # Handle replace rules | |
| 312 | + match = re.match(r"^replace\s+(\S+)\s+(\S+)\s+(\S+)", line) | |
| 313 | + if not match: | |
| 314 | + sys.exit(f"{name}:{ln}: invalid line: {line}") | |
| 315 | + | |
| 316 | + c_type, old, new = match.groups() | |
| 317 | + | |
| 318 | + if c_type not in self.DEF_SYMBOL_TYPES: | |
| 319 | + sys.exit(f"{name}:{ln}: {c_type} is invalid") | |
| 320 | + | |
| 321 | + reftype = None | |
| 322 | + | |
| 323 | + # Parse reference type when the type is specified | |
| 324 | + | |
| 325 | + match = re.match(r"^\:c\:(data|func|macro|type)\:\`(.+)\`", new) | |
| 326 | + if match: | |
| 327 | + reftype = f":c:{match.group(1)}" | |
| 328 | + new = match.group(2) | |
| 329 | + else: | |
| 330 | + match = re.search(r"(\:ref)\:\`(.+)\`", new) | |
| 331 | + if match: | |
| 332 | + reftype = match.group(1) | |
| 333 | + new = match.group(2) | |
| 334 | + | |
| 335 | + # If the replacement rule doesn't have a type, get default | |
| 336 | + if not reftype: | |
| 337 | + reftype = self.DEF_SYMBOL_TYPES[c_type].get("ref_type") | |
| 338 | + if not reftype: | |
| 339 | + reftype = self.DEF_SYMBOL_TYPES[c_type].get("real_type") | |
| 340 | + | |
| 341 | + new_ref = f"{reftype}:`{old} <{new}>`" | |
| 342 | + | |
| 343 | + # Change self.symbols to use the replacement rule | |
| 344 | + if old in self.symbols[c_type]: | |
| 345 | + self.symbols[c_type][old] = new_ref | |
| 346 | + else: | |
| 347 | + print(f"{name}:{ln}: Warning: can't find {old} {c_type}") | |
| 348 | + | |
| 349 | + def debug_print(self): | |
| 350 | + """ | |
| 351 | + Print debug information containing the replacement rules per symbol. | |
| 352 | + To make easier to check, group them per type. | |
| 353 | + """ | |
| 354 | + if not self.debug: | |
| 355 | + return | |
| 356 | + | |
| 357 | + for c_type, refs in self.symbols.items(): | |
| 358 | + if not refs: # Skip empty dictionaries | |
| 359 | + continue | |
| 360 | + | |
| 361 | + print(f"{c_type}:") | |
| 362 | + | |
| 363 | + for symbol, ref in sorted(refs.items()): | |
| 364 | + print(f" {symbol} -> {ref}") | |
| 365 | + | |
| 366 | + print() | |
| 367 | + | |
| 368 | + def gen_output(self): | |
| 369 | + """Write the formatted output to a file.""" | |
| 370 | + | |
| 371 | + # Avoid extra blank lines | |
| 372 | + text = re.sub(r"\s+$", "", self.data) + "\n" | |
| 373 | + text = re.sub(r"\n\s+\n", "\n\n", text) | |
| 374 | + | |
| 375 | + # Escape Sphinx special characters | |
| 376 | + text = re.sub(r"([\_\`\*\<\>\&\\\\:\/\|\%\$\#\{\}\~\^])", r"\\\1", text) | |
| 377 | + | |
| 378 | + # Source uAPI files may have special notes. Use bold font for them | |
| 379 | + text = re.sub(r"DEPRECATED", "**DEPRECATED**", text) | |
| 380 | + | |
| 381 | + # Delimiters to catch the entire symbol after escaped | |
| 382 | + start_delim = r"([ \n\t\(=\*\@])" | |
| 383 | + end_delim = r"(\s|,|\\=|\\:|\;|\)|\}|\{)" | |
| 384 | + | |
| 385 | + # Process all reference types | |
| 386 | + for ref_dict in self.symbols.values(): | |
| 387 | + for symbol, replacement in ref_dict.items(): | |
| 388 | + symbol = re.escape(re.sub(r"([\_\`\*\<\>\&\\\\:\/])", r"\\\1", symbol)) | |
| 389 | + text = re.sub(fr'{start_delim}{symbol}{end_delim}', | |
| 390 | + fr'\1{replacement}\2', text) | |
| 391 | + | |
| 392 | + # Remove "\ " where not needed: before spaces and at the end of lines | |
| 393 | + text = re.sub(r"\\ ([\n ])", r"\1", text) | |
| 394 | + text = re.sub(r" \\ ", " ", text) | |
| 395 | + | |
| 396 | + return text | |
| 397 | + | |
| 398 | + def gen_toc(self): | |
| 399 | + """ | |
| 400 | + Create a TOC table pointing to each symbol from the header | |
| 401 | + """ | |
| 402 | + text = [] | |
| 403 | + | |
| 404 | + # Add header | |
| 405 | + text.append(".. contents:: Table of Contents") | |
| 406 | + text.append(" :depth: 2") | |
| 407 | + text.append(" :local:") | |
| 408 | + text.append("") | |
| 409 | + | |
| 410 | + # Sort symbol types per description | |
| 411 | + symbol_descriptions = [] | |
| 412 | + for k, v in self.DEF_SYMBOL_TYPES.items(): | |
| 413 | + symbol_descriptions.append((v['description'], k)) | |
| 414 | + | |
| 415 | + symbol_descriptions.sort() | |
| 416 | + | |
| 417 | + # Process each category | |
| 418 | + for description, c_type in symbol_descriptions: | |
| 419 | + | |
| 420 | + refs = self.symbols[c_type] | |
| 421 | + if not refs: # Skip empty categories | |
| 422 | + continue | |
| 423 | + | |
| 424 | + text.append(f"{description}") | |
| 425 | + text.append("-" * len(description)) | |
| 426 | + text.append("") | |
| 427 | + | |
| 428 | + # Sort symbols alphabetically | |
| 429 | + for symbol, ref in sorted(refs.items()): | |
| 430 | + text.append(f"* :{ref}:") | |
| 431 | + | |
| 432 | + text.append("") # Add empty line between categories | |
| 433 | + | |
| 434 | + return "\n".join(text) | |
| 435 | + | |
| 436 | + def write_output(self, file_in: str, file_out: str, toc: bool): | |
| 437 | + title = os.path.basename(file_in) | |
| 438 | + | |
| 439 | + if toc: | |
| 440 | + text = self.gen_toc() | |
| 441 | + else: | |
| 442 | + text = self.gen_output() | |
| 443 | + | |
| 444 | + with open(file_out, "w", encoding="utf-8", errors="backslashreplace") as f: | |
| 445 | + f.write(".. -*- coding: utf-8; mode: rst -*-\n\n") | |
| 446 | + f.write(f"{title}\n") | |
| 447 | + f.write("=" * len(title) + "\n\n") | |
| 448 | + | |
| 449 | + if not toc: | |
| 450 | + f.write(".. parsed-literal::\n\n") | |
| 451 | + | |
| 452 | + f.write(text) | |
| @@ -0,0 +1,60 @@ | ||
| 1 | +#!/usr/bin/env python3 | |
| 2 | +# SPDX-License-Identifier: GPL-2.0 | |
| 3 | +# Copyright (c) 2016, 2025 by Mauro Carvalho Chehab <mchehab@kernel.org>. | |
| 4 | +# pylint: disable=C0103 | |
| 5 | + | |
| 6 | +""" | |
| 7 | +Convert a C header or source file ``FILE_IN``, into a ReStructured Text | |
| 8 | +included via ..parsed-literal block with cross-references for the | |
| 9 | +documentation files that describe the API. It accepts an optional | |
| 10 | +``FILE_RULES`` file to describes what elements will be either ignored or | |
| 11 | +be pointed to a non-default reference type/name. | |
| 12 | + | |
| 13 | +The output is written at ``FILE_OUT``. | |
| 14 | + | |
| 15 | +It is capable of identifying defines, functions, structs, typedefs, | |
| 16 | +enums and enum symbols and create cross-references for all of them. | |
| 17 | +It is also capable of distinguish #define used for specifying a Linux | |
| 18 | +ioctl. | |
| 19 | + | |
| 20 | +The optional ``FILE_RULES`` contains a set of rules like: | |
| 21 | + | |
| 22 | + ignore ioctl VIDIOC_ENUM_FMT | |
| 23 | + replace ioctl VIDIOC_DQBUF vidioc_qbuf | |
| 24 | + replace define V4L2_EVENT_MD_FL_HAVE_FRAME_SEQ :c:type:`v4l2_event_motion_det` | |
| 25 | +""" | |
| 26 | + | |
| 27 | +import argparse | |
| 28 | + | |
| 29 | +from lib.parse_data_structs import ParseDataStructs | |
| 30 | +from lib.enrich_formatter import EnrichFormatter | |
| 31 | + | |
| 32 | +def main(): | |
| 33 | + """Main function""" | |
| 34 | + parser = argparse.ArgumentParser(description=__doc__, | |
| 35 | + formatter_class=EnrichFormatter) | |
| 36 | + | |
| 37 | + parser.add_argument("-d", "--debug", action="count", default=0, | |
| 38 | + help="Increase debug level. Can be used multiple times") | |
| 39 | + parser.add_argument("-t", "--toc", action="store_true", | |
| 40 | + help="instead of a literal block, outputs a TOC table at the RST file") | |
| 41 | + | |
| 42 | + parser.add_argument("file_in", help="Input C file") | |
| 43 | + parser.add_argument("file_out", help="Output RST file") | |
| 44 | + parser.add_argument("file_rules", nargs="?", | |
| 45 | + help="Exceptions file (optional)") | |
| 46 | + | |
| 47 | + args = parser.parse_args() | |
| 48 | + | |
| 49 | + parser = ParseDataStructs(debug=args.debug) | |
| 50 | + parser.parse_file(args.file_in) | |
| 51 | + | |
| 52 | + if args.file_rules: | |
| 53 | + parser.process_exceptions(args.file_rules) | |
| 54 | + | |
| 55 | + parser.debug_print() | |
| 56 | + parser.write_output(args.file_in, args.file_out, args.toc) | |
| 57 | + | |
| 58 | + | |
| 59 | +if __name__ == "__main__": | |
| 60 | + main() | |