A moderatly busy cycle for development this time around.
- Some cleanup of the main index page for easier navigation - Rework some of the other top-level pages for better readability and, with luck, fewer merge conflicts in the future. - Submit-checklist improvements, hopefully the first of many. - New Italian translations - A fair number of kernel-doc fixes and improvements. We have also dropped the recommendation to use an old version of Sphinx. - A new document from Thorsten on bisection ...and lots of fixes and updates. -----BEGIN PGP SIGNATURE----- iQEzBAABCAAdFiEEIw+MvkEiF49krdp9F0NaE2wMflgFAmXvKVIACgkQF0NaE2wM flik1gf/ZFS1mHwDdmHA/vpx8UxdUlFEo0Pms8V24iPSW5aEIqkZ406c9DSyMTtp CXTzW+RSCfB1Q3ciYtakHBgv0RzZ5+RyaEZ1l7zVmMyw4nYvK6giYKmg8Y0EVPKI fAVuPWo5iE7io0sNVbKBKJJkj9Z8QEScM48hv/CV1FblMvHYn0lie6muJrF9G6Ez HND+hlYZtWkbRd5M86CDBiFeGMLVPx17T+psQyQIcbUYm9b+RUqZRHIVRLYbad7r 18r9+83DsOhXTVJCBBSfCSZwzF8yAm+eD1w47sxnSItF8OiIjqCzQgXs3BZe9TXH h2YyeWbMN3xByA4mEgpmOPP44RW7Pg== =SC60 -----END PGP SIGNATURE----- Merge tag 'docs-6.9' of git://git.lwn.net/linux Pull documentation updates from Jonathan Corbet: "A moderatly busy cycle for development this time around. - Some cleanup of the main index page for easier navigation - Rework some of the other top-level pages for better readability and, with luck, fewer merge conflicts in the future. - Submit-checklist improvements, hopefully the first of many. - New Italian translations - A fair number of kernel-doc fixes and improvements. We have also dropped the recommendation to use an old version of Sphinx. - A new document from Thorsten on bisection ... and lots of fixes and updates" * tag 'docs-6.9' of git://git.lwn.net/linux: (54 commits) docs: verify/bisect: fixes, finetuning, and support for Arch docs: Makefile: Add dependency to $(YNL_INDEX) for targets other than htmldocs docs: Move ja_JP/howto.rst to ja_JP/process/howto.rst docs: submit-checklist: use subheadings docs: submit-checklist: structure by category docs: new text on bisecting which also covers bug validation docs: drop the version constraints for sphinx and dependencies docs: kerneldoc-preamble.sty: Remove code for Sphinx <2.4 docs: Restore "smart quotes" for quotes docs/zh_CN: accurate translation of "function" docs: Include simplified link titles in main index docs: Correct formatting of title in admin-guide/index.rst docs: kernel_feat.py: fix build error for missing files MAINTAINERS: Set the field name for subsystem profile section kasan: Add documentation for CONFIG_KASAN_EXTRA_INFO Fixed case issue with 'fault-injection' in documentation kernel-doc: handle #if in enums as well Documentation: update mailing list addresses doc: kerneldoc.py: fix indentation scripts/kernel-doc: simplify signature printing ...
This commit is contained in:
commit
1f44039766
@ -1,6 +1,6 @@
|
||||
What: /sys/bus/vdpa/drivers_autoprobe
|
||||
Date: March 2020
|
||||
Contact: virtualization@lists.linux-foundation.org
|
||||
Contact: virtualization@lists.linux.dev
|
||||
Description:
|
||||
This file determines whether new devices are immediately bound
|
||||
to a driver after the creation. It initially contains 1, which
|
||||
@ -12,7 +12,7 @@ Description:
|
||||
|
||||
What: /sys/bus/vdpa/driver_probe
|
||||
Date: March 2020
|
||||
Contact: virtualization@lists.linux-foundation.org
|
||||
Contact: virtualization@lists.linux.dev
|
||||
Description:
|
||||
Writing a device name to this file will cause the kernel binds
|
||||
devices to a compatible driver.
|
||||
@ -22,7 +22,7 @@ Description:
|
||||
|
||||
What: /sys/bus/vdpa/drivers/.../bind
|
||||
Date: March 2020
|
||||
Contact: virtualization@lists.linux-foundation.org
|
||||
Contact: virtualization@lists.linux.dev
|
||||
Description:
|
||||
Writing a device name to this file will cause the driver to
|
||||
attempt to bind to the device. This is useful for overriding
|
||||
@ -30,7 +30,7 @@ Description:
|
||||
|
||||
What: /sys/bus/vdpa/drivers/.../unbind
|
||||
Date: March 2020
|
||||
Contact: virtualization@lists.linux-foundation.org
|
||||
Contact: virtualization@lists.linux.dev
|
||||
Description:
|
||||
Writing a device name to this file will cause the driver to
|
||||
attempt to unbind from the device. This may be useful when
|
||||
@ -38,7 +38,7 @@ Description:
|
||||
|
||||
What: /sys/bus/vdpa/devices/.../driver_override
|
||||
Date: November 2021
|
||||
Contact: virtualization@lists.linux-foundation.org
|
||||
Contact: virtualization@lists.linux.dev
|
||||
Description:
|
||||
This file allows the driver for a device to be specified.
|
||||
When specified, only a driver with a name matching the value
|
||||
|
@ -111,7 +111,9 @@ $(YNL_INDEX): $(YNL_RST_FILES)
|
||||
$(YNL_RST_DIR)/%.rst: $(YNL_YAML_DIR)/%.yaml $(YNL_TOOL)
|
||||
$(Q)$(YNL_TOOL) -i $< -o $@
|
||||
|
||||
htmldocs: $(YNL_INDEX)
|
||||
htmldocs texinfodocs latexdocs epubdocs xmldocs: $(YNL_INDEX)
|
||||
|
||||
htmldocs:
|
||||
@$(srctree)/scripts/sphinx-pre-install --version-check
|
||||
@+$(foreach var,$(SPHINXDIRS),$(call loop_cmd,sphinx,html,$(var),,$(var)))
|
||||
|
||||
@ -176,6 +178,7 @@ refcheckdocs:
|
||||
$(Q)cd $(srctree);scripts/documentation-file-ref-check
|
||||
|
||||
cleandocs:
|
||||
$(Q)rm -f $(YNL_INDEX) $(YNL_RST_FILES)
|
||||
$(Q)rm -rf $(BUILDDIR)
|
||||
$(Q)$(MAKE) BUILDDIR=$(abspath $(BUILDDIR)) $(build)=Documentation/userspace-api/media clean
|
||||
|
||||
|
@ -318,7 +318,7 @@ Suppose that a previous kvm.sh run left its output in this directory::
|
||||
|
||||
tools/testing/selftests/rcutorture/res/2022.11.03-11.26.28
|
||||
|
||||
Then this run can be re-run without rebuilding as follow:
|
||||
Then this run can be re-run without rebuilding as follow::
|
||||
|
||||
kvm-again.sh tools/testing/selftests/rcutorture/res/2022.11.03-11.26.28
|
||||
|
||||
|
@ -262,9 +262,11 @@ Compiling the kernel
|
||||
- Make sure you have at least gcc 5.1 available.
|
||||
For more information, refer to :ref:`Documentation/process/changes.rst <changes>`.
|
||||
|
||||
- Do a ``make`` to create a compressed kernel image. It is also
|
||||
possible to do ``make install`` if you have lilo installed to suit the
|
||||
kernel makefiles, but you may want to check your particular lilo setup first.
|
||||
- Do a ``make`` to create a compressed kernel image. It is also possible to do
|
||||
``make install`` if you have lilo installed or if your distribution has an
|
||||
install script recognised by the kernel's installer. Most popular
|
||||
distributions will have a recognized install script. You may want to
|
||||
check your distribution's setup first.
|
||||
|
||||
To do the actual install, you have to be root, but none of the normal
|
||||
build should require that. Don't take the name of root in vain.
|
||||
@ -301,32 +303,51 @@ Compiling the kernel
|
||||
image (e.g. .../linux/arch/x86/boot/bzImage after compilation)
|
||||
to the place where your regular bootable kernel is found.
|
||||
|
||||
- Booting a kernel directly from a floppy without the assistance of a
|
||||
bootloader such as LILO, is no longer supported.
|
||||
- Booting a kernel directly from a storage device without the assistance
|
||||
of a bootloader such as LILO or GRUB, is no longer supported in BIOS
|
||||
(non-EFI systems). On UEFI/EFI systems, however, you can use EFISTUB
|
||||
which allows the motherboard to boot directly to the kernel.
|
||||
On modern workstations and desktops, it's generally recommended to use a
|
||||
bootloader as difficulties can arise with multiple kernels and secure boot.
|
||||
For more details on EFISTUB,
|
||||
see "Documentation/admin-guide/efi-stub.rst".
|
||||
|
||||
If you boot Linux from the hard drive, chances are you use LILO, which
|
||||
uses the kernel image as specified in the file /etc/lilo.conf. The
|
||||
kernel image file is usually /vmlinuz, /boot/vmlinuz, /bzImage or
|
||||
/boot/bzImage. To use the new kernel, save a copy of the old image
|
||||
and copy the new image over the old one. Then, you MUST RERUN LILO
|
||||
to update the loading map! If you don't, you won't be able to boot
|
||||
the new kernel image.
|
||||
- It's important to note that as of 2016 LILO (LInux LOader) is no longer in
|
||||
active development, though as it was extremely popular, it often comes up
|
||||
in documentation. Popular alternatives include GRUB2, rEFInd, Syslinux,
|
||||
systemd-boot, or EFISTUB. For various reasons, it's not recommended to use
|
||||
software that's no longer in active development.
|
||||
|
||||
Reinstalling LILO is usually a matter of running /sbin/lilo.
|
||||
You may wish to edit /etc/lilo.conf to specify an entry for your
|
||||
old kernel image (say, /vmlinux.old) in case the new one does not
|
||||
work. See the LILO docs for more information.
|
||||
- Chances are your distribution includes an install script and running
|
||||
``make install`` will be all that's needed. Should that not be the case
|
||||
you'll have to identify your bootloader and reference its documentation or
|
||||
configure your EFI.
|
||||
|
||||
After reinstalling LILO, you should be all set. Shutdown the system,
|
||||
Legacy LILO Instructions
|
||||
------------------------
|
||||
|
||||
|
||||
- If you use LILO the kernel images are specified in the file /etc/lilo.conf.
|
||||
The kernel image file is usually /vmlinuz, /boot/vmlinuz, /bzImage or
|
||||
/boot/bzImage. To use the new kernel, save a copy of the old image and copy
|
||||
the new image over the old one. Then, you MUST RERUN LILO to update the
|
||||
loading map! If you don't, you won't be able to boot the new kernel image.
|
||||
|
||||
- Reinstalling LILO is usually a matter of running /sbin/lilo. You may wish
|
||||
to edit /etc/lilo.conf to specify an entry for your old kernel image
|
||||
(say, /vmlinux.old) in case the new one does not work. See the LILO docs
|
||||
for more information.
|
||||
|
||||
- After reinstalling LILO, you should be all set. Shutdown the system,
|
||||
reboot, and enjoy!
|
||||
|
||||
If you ever need to change the default root device, video mode,
|
||||
etc. in the kernel image, use your bootloader's boot options
|
||||
where appropriate. No need to recompile the kernel to change
|
||||
these parameters.
|
||||
- If you ever need to change the default root device, video mode, etc. in the
|
||||
kernel image, use your bootloader's boot options where appropriate. No need
|
||||
to recompile the kernel to change these parameters.
|
||||
|
||||
- Reboot with the new kernel and enjoy.
|
||||
|
||||
|
||||
If something goes wrong
|
||||
-----------------------
|
||||
|
||||
|
@ -1,3 +1,4 @@
|
||||
=================================================
|
||||
The Linux kernel user's and administrator's guide
|
||||
=================================================
|
||||
|
||||
@ -37,6 +38,7 @@ problems and bugs in particular.
|
||||
reporting-issues
|
||||
reporting-regressions
|
||||
quickly-build-trimmed-linux
|
||||
verify-bugs-and-bisect-regressions
|
||||
bug-hunting
|
||||
bug-bisect
|
||||
tainted-kernels
|
||||
|
@ -4668,6 +4668,11 @@
|
||||
may be specified.
|
||||
Format: <port>,<port>....
|
||||
|
||||
possible_cpus= [SMP,S390,X86]
|
||||
Format: <unsigned int>
|
||||
Set the number of possible CPUs, overriding the
|
||||
regular discovery mechanisms (such as ACPI/FW, etc).
|
||||
|
||||
powersave=off [PPC] This option disables power saving features.
|
||||
It specifically disables cpuidle and sets the
|
||||
platform machine description specific power_save
|
||||
|
@ -34,7 +34,7 @@ name of the command ('Comm:') that triggered the event::
|
||||
|
||||
You'll find a 'Not tainted: ' there if the kernel was not tainted at the
|
||||
time of the event; if it was, then it will print 'Tainted: ' and characters
|
||||
either letters or blanks. In above example it looks like this::
|
||||
either letters or blanks. In the example above it looks like this::
|
||||
|
||||
Tainted: P W O
|
||||
|
||||
@ -52,7 +52,7 @@ At runtime, you can query the tainted state by reading
|
||||
tainted; any other number indicates the reasons why it is. The easiest way to
|
||||
decode that number is the script ``tools/debugging/kernel-chktaint``, which your
|
||||
distribution might ship as part of a package called ``linux-tools`` or
|
||||
``kernel-tools``; if it doesn't you can download the script from
|
||||
``kernel-tools``; if it doesn't, you can download the script from
|
||||
`git.kernel.org <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/tools/debugging/kernel-chktaint>`_
|
||||
and execute it with ``sh kernel-chktaint``, which would print something like
|
||||
this on the machine that had the statements in the logs that were quoted earlier::
|
||||
|
1952
Documentation/admin-guide/verify-bugs-and-bisect-regressions.rst
Normal file
1952
Documentation/admin-guide/verify-bugs-and-bisect-regressions.rst
Normal file
File diff suppressed because it is too large
Load Diff
@ -346,9 +346,9 @@ sys.stderr.write("Using %s theme\n" % html_theme)
|
||||
html_static_path = ['sphinx-static']
|
||||
|
||||
# If true, Docutils "smart quotes" will be used to convert quotes and dashes
|
||||
# to typographically correct entities. This will convert "--" to "—",
|
||||
# which is not always what we want, so disable it.
|
||||
smartquotes = False
|
||||
# to typographically correct entities. However, conversion of "--" to "—"
|
||||
# is not always what we want, so enable only quotes.
|
||||
smartquotes_action = 'q'
|
||||
|
||||
# Custom sidebar templates, maps document names to template names.
|
||||
# Note that the RTD theme ignores this
|
||||
|
@ -168,7 +168,7 @@ Available options:
|
||||
|
||||
- --fix
|
||||
|
||||
This is an EXPERIMENTAL feature. If correctable errors exists, a file
|
||||
This is an EXPERIMENTAL feature. If correctable errors exist, a file
|
||||
<inputfile>.EXPERIMENTAL-checkpatch-fixes is created which has the
|
||||
automatically fixable errors corrected.
|
||||
|
||||
@ -181,7 +181,7 @@ Available options:
|
||||
|
||||
- --ignore-perl-version
|
||||
|
||||
Override checking of perl version. Runtime errors maybe encountered after
|
||||
Override checking of perl version. Runtime errors may be encountered after
|
||||
enabling this flag if the perl version does not meet the minimum specified.
|
||||
|
||||
- --codespell
|
||||
|
@ -277,6 +277,27 @@ traces point to places in code that interacted with the object but that are not
|
||||
directly present in the bad access stack trace. Currently, this includes
|
||||
call_rcu() and workqueue queuing.
|
||||
|
||||
CONFIG_KASAN_EXTRA_INFO
|
||||
~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
Enabling CONFIG_KASAN_EXTRA_INFO allows KASAN to record and report more
|
||||
information. The extra information currently supported is the CPU number and
|
||||
timestamp at allocation and free. More information can help find the cause of
|
||||
the bug and correlate the error with other system events, at the cost of using
|
||||
extra memory to record more information (more cost details in the help text of
|
||||
CONFIG_KASAN_EXTRA_INFO).
|
||||
|
||||
Here is the report with CONFIG_KASAN_EXTRA_INFO enabled (only the
|
||||
different parts are shown)::
|
||||
|
||||
==================================================================
|
||||
...
|
||||
Allocated by task 134 on cpu 5 at 229.133855s:
|
||||
...
|
||||
Freed by task 136 on cpu 3 at 230.199335s:
|
||||
...
|
||||
==================================================================
|
||||
|
||||
Implementation details
|
||||
----------------------
|
||||
|
||||
|
@ -341,6 +341,51 @@ Typedefs with function prototypes can also be documented::
|
||||
*/
|
||||
typedef void (*type_name)(struct v4l2_ctrl *arg1, void *arg2);
|
||||
|
||||
Object-like macro documentation
|
||||
-------------------------------
|
||||
|
||||
Object-like macros are distinct from function-like macros. They are
|
||||
differentiated by whether the macro name is immediately followed by a
|
||||
left parenthesis ('(') for function-like macros or not followed by one
|
||||
for object-like macros.
|
||||
|
||||
Function-like macros are handled like functions by ``scripts/kernel-doc``.
|
||||
They may have a parameter list. Object-like macros have do not have a
|
||||
parameter list.
|
||||
|
||||
The general format of an object-like macro kernel-doc comment is::
|
||||
|
||||
/**
|
||||
* define object_name - Brief description.
|
||||
*
|
||||
* Description of the object.
|
||||
*/
|
||||
|
||||
Example::
|
||||
|
||||
/**
|
||||
* define MAX_ERRNO - maximum errno value that is supported
|
||||
*
|
||||
* Kernel pointers have redundant information, so we can use a
|
||||
* scheme where we can return either an error code or a normal
|
||||
* pointer with the same return value.
|
||||
*/
|
||||
#define MAX_ERRNO 4095
|
||||
|
||||
Example::
|
||||
|
||||
/**
|
||||
* define DRM_GEM_VRAM_PLANE_HELPER_FUNCS - \
|
||||
* Initializes struct drm_plane_helper_funcs for VRAM handling
|
||||
*
|
||||
* This macro initializes struct drm_plane_helper_funcs to use the
|
||||
* respective helper functions.
|
||||
*/
|
||||
#define DRM_GEM_VRAM_PLANE_HELPER_FUNCS \
|
||||
.prepare_fb = drm_gem_vram_plane_helper_prepare_fb, \
|
||||
.cleanup_fb = drm_gem_vram_plane_helper_cleanup_fb
|
||||
|
||||
|
||||
Highlights and cross-references
|
||||
-------------------------------
|
||||
|
||||
|
@ -27,6 +27,13 @@ documentation and ensure that no new errors or warnings have been
|
||||
introduced. Generating HTML documents and looking at the result will help
|
||||
to avoid unsightly misunderstandings about how things will be rendered.
|
||||
|
||||
All new documentation (including additions to existing documents) should
|
||||
ideally justify who the intended target audience is somewhere in the
|
||||
changelog; this way, we ensure that the documentation ends up in the correct
|
||||
place. Some possible categories are: kernel developers (experts or
|
||||
beginners), userspace programmers, end users and/or system administrators,
|
||||
and distributors.
|
||||
|
||||
Key cycle dates
|
||||
---------------
|
||||
|
||||
|
@ -48,13 +48,14 @@ or ``virtualenv``, depending on how your distribution packaged Python 3.
|
||||
on the Sphinx version, it should be installed separately,
|
||||
with ``pip install sphinx_rtd_theme``.
|
||||
|
||||
In summary, if you want to install Sphinx version 2.4.4, you should do::
|
||||
In summary, if you want to install the latest version of Sphinx, you
|
||||
should do::
|
||||
|
||||
$ virtualenv sphinx_2.4.4
|
||||
$ . sphinx_2.4.4/bin/activate
|
||||
(sphinx_2.4.4) $ pip install -r Documentation/sphinx/requirements.txt
|
||||
$ virtualenv sphinx_latest
|
||||
$ . sphinx_latest/bin/activate
|
||||
(sphinx_latest) $ pip install -r Documentation/sphinx/requirements.txt
|
||||
|
||||
After running ``. sphinx_2.4.4/bin/activate``, the prompt will change,
|
||||
After running ``. sphinx_latest/bin/activate``, the prompt will change,
|
||||
in order to indicate that you're using the new environment. If you
|
||||
open a new shell, you need to rerun this command to enter again at
|
||||
the virtual environment before building the documentation.
|
||||
@ -63,8 +64,7 @@ Image output
|
||||
------------
|
||||
|
||||
The kernel documentation build system contains an extension that
|
||||
handles images on both GraphViz and SVG formats (see
|
||||
:ref:`sphinx_kfigure`).
|
||||
handles images in both GraphViz and SVG formats (see :ref:`sphinx_kfigure`).
|
||||
|
||||
For it to work, you need to install both GraphViz and ImageMagick
|
||||
packages. If those packages are not installed, the build system will
|
||||
@ -108,7 +108,7 @@ further info.
|
||||
Checking for Sphinx dependencies
|
||||
--------------------------------
|
||||
|
||||
There's a script that automatically check for Sphinx dependencies. If it can
|
||||
There's a script that automatically checks for Sphinx dependencies. If it can
|
||||
recognize your distribution, it will also give a hint about the install
|
||||
command line options for your distro::
|
||||
|
||||
@ -283,7 +283,7 @@ Here are some specific guidelines for the kernel documentation:
|
||||
from highlighting. For a short snippet of code embedded in the text, use \`\`.
|
||||
|
||||
|
||||
the C domain
|
||||
The C domain
|
||||
------------
|
||||
|
||||
The **Sphinx C Domain** (name c) is suited for documentation of C API. E.g. a
|
||||
|
@ -9,110 +9,141 @@ of device drivers. This document is an only somewhat organized collection
|
||||
of some of those interfaces — it will hopefully get better over time! The
|
||||
available subsections can be seen below.
|
||||
|
||||
.. toctree::
|
||||
:caption: Table of contents
|
||||
:maxdepth: 2
|
||||
|
||||
driver-model/index
|
||||
General information for driver authors
|
||||
======================================
|
||||
|
||||
This section contains documentation that should, at some point or other, be
|
||||
of interest to most developers working on device drivers.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
basics
|
||||
driver-model/index
|
||||
device_link
|
||||
infrastructure
|
||||
ioctl
|
||||
early-userspace/index
|
||||
pm/index
|
||||
clk
|
||||
device-io
|
||||
dma-buf
|
||||
device_link
|
||||
component
|
||||
message-based
|
||||
infiniband
|
||||
aperture
|
||||
frame-buffer
|
||||
regulator
|
||||
reset
|
||||
iio/index
|
||||
input
|
||||
usb/index
|
||||
firewire
|
||||
pci/index
|
||||
cxl/index
|
||||
spi
|
||||
i2c
|
||||
ipmb
|
||||
ipmi
|
||||
i3c/index
|
||||
interconnect
|
||||
devfreq
|
||||
hsi
|
||||
edac
|
||||
scsi
|
||||
libata
|
||||
target
|
||||
mailbox
|
||||
mtdnand
|
||||
miscellaneous
|
||||
mei/index
|
||||
mtd/index
|
||||
mmc/index
|
||||
nvdimm/index
|
||||
w1
|
||||
rapidio/index
|
||||
s390-drivers
|
||||
vme
|
||||
80211/index
|
||||
uio-howto
|
||||
firmware/index
|
||||
pin-control
|
||||
gpio/index
|
||||
md/index
|
||||
media/index
|
||||
misc_devices
|
||||
nfc/index
|
||||
dmaengine/index
|
||||
slimbus
|
||||
soundwire/index
|
||||
thermal/index
|
||||
fpga/index
|
||||
acpi/index
|
||||
auxiliary_bus
|
||||
backlight/lp855x-driver.rst
|
||||
|
||||
Useful support libraries
|
||||
========================
|
||||
|
||||
This section contains documentation that should, at some point or other, be
|
||||
of interest to most developers working on device drivers.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
early-userspace/index
|
||||
connector
|
||||
console
|
||||
eisa
|
||||
isa
|
||||
device-io
|
||||
devfreq
|
||||
dma-buf
|
||||
component
|
||||
io-mapping
|
||||
io_ordering
|
||||
generic-counter
|
||||
memory-devices/index
|
||||
men-chameleon-bus
|
||||
ntb
|
||||
nvmem
|
||||
parport-lowlevel
|
||||
pps
|
||||
ptp
|
||||
phy/index
|
||||
pwm
|
||||
pldmfw/index
|
||||
rfkill
|
||||
serial/index
|
||||
sm501
|
||||
surface_aggregator/index
|
||||
switchtec
|
||||
sync_file
|
||||
tty/index
|
||||
uio-howto
|
||||
vfio-mediated-device
|
||||
vfio
|
||||
vfio-pci-device-specific-driver-acceptance
|
||||
|
||||
Bus-level documentation
|
||||
=======================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
auxiliary_bus
|
||||
cxl/index
|
||||
eisa
|
||||
firewire
|
||||
i3c/index
|
||||
isa
|
||||
men-chameleon-bus
|
||||
pci/index
|
||||
rapidio/index
|
||||
slimbus
|
||||
usb/index
|
||||
virtio/index
|
||||
xilinx/index
|
||||
vme
|
||||
w1
|
||||
xillybus
|
||||
zorro
|
||||
hte/index
|
||||
wmi
|
||||
dpll
|
||||
wbrf
|
||||
|
||||
|
||||
Subsystem-specific APIs
|
||||
=======================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
80211/index
|
||||
acpi/index
|
||||
backlight/lp855x-driver.rst
|
||||
clk
|
||||
console
|
||||
crypto/index
|
||||
dmaengine/index
|
||||
dpll
|
||||
edac
|
||||
firmware/index
|
||||
fpga/index
|
||||
frame-buffer
|
||||
aperture
|
||||
generic-counter
|
||||
gpio/index
|
||||
hsi
|
||||
hte/index
|
||||
i2c
|
||||
iio/index
|
||||
infiniband
|
||||
input
|
||||
interconnect
|
||||
ipmb
|
||||
ipmi
|
||||
libata
|
||||
mailbox
|
||||
md/index
|
||||
media/index
|
||||
mei/index
|
||||
memory-devices/index
|
||||
message-based
|
||||
misc_devices
|
||||
miscellaneous
|
||||
mmc/index
|
||||
mtd/index
|
||||
mtdnand
|
||||
nfc/index
|
||||
ntb
|
||||
nvdimm/index
|
||||
nvmem
|
||||
parport-lowlevel
|
||||
phy/index
|
||||
pin-control
|
||||
pldmfw/index
|
||||
pps
|
||||
ptp
|
||||
pwm
|
||||
regulator
|
||||
reset
|
||||
rfkill
|
||||
s390-drivers
|
||||
scsi
|
||||
serial/index
|
||||
sm501
|
||||
soundwire/index
|
||||
spi
|
||||
surface_aggregator/index
|
||||
switchtec
|
||||
sync_file
|
||||
target
|
||||
tee
|
||||
thermal/index
|
||||
tty/index
|
||||
wbrf
|
||||
wmi
|
||||
xilinx/index
|
||||
zorro
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
===============
|
||||
fault-injection
|
||||
Fault-injection
|
||||
===============
|
||||
|
||||
.. toctree::
|
||||
|
@ -1899,8 +1899,8 @@ For more information on mount propagation see:
|
||||
These files provide a method to access a task's comm value. It also allows for
|
||||
a task to set its own or one of its thread siblings comm value. The comm value
|
||||
is limited in size compared to the cmdline value, so writing anything longer
|
||||
then the kernel's TASK_COMM_LEN (currently 16 chars) will result in a truncated
|
||||
comm value.
|
||||
then the kernel's TASK_COMM_LEN (currently 16 chars, including the NUL
|
||||
terminator) will result in a truncated comm value.
|
||||
|
||||
|
||||
3.7 /proc/<pid>/task/<tid>/children - Information about task children
|
||||
|
@ -22,10 +22,10 @@ community and getting your work upstream.
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
process/development-process
|
||||
process/submitting-patches
|
||||
Development process <process/development-process>
|
||||
Submitting patches <process/submitting-patches>
|
||||
Code of conduct <process/code-of-conduct>
|
||||
maintainer/index
|
||||
Maintainer handbook <maintainer/index>
|
||||
All development-process docs <process/index>
|
||||
|
||||
|
||||
@ -38,10 +38,10 @@ kernel.
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
core-api/index
|
||||
driver-api/index
|
||||
subsystem-apis
|
||||
Locking in the kernel <locking/index>
|
||||
Core API <core-api/index>
|
||||
Driver APIs <driver-api/index>
|
||||
Subsystems <subsystem-apis>
|
||||
Locking <locking/index>
|
||||
|
||||
Development tools and processes
|
||||
===============================
|
||||
@ -51,15 +51,15 @@ Various other manuals with useful information for all kernel developers.
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
process/license-rules
|
||||
doc-guide/index
|
||||
dev-tools/index
|
||||
dev-tools/testing-overview
|
||||
kernel-hacking/index
|
||||
trace/index
|
||||
fault-injection/index
|
||||
livepatch/index
|
||||
rust/index
|
||||
Licensing rules <process/license-rules>
|
||||
Writing documentation <doc-guide/index>
|
||||
Development tools <dev-tools/index>
|
||||
Testing guide <dev-tools/testing-overview>
|
||||
Hacking guide <kernel-hacking/index>
|
||||
Tracing <trace/index>
|
||||
Fault injection <fault-injection/index>
|
||||
Livepatching <livepatch/index>
|
||||
Rust <rust/index>
|
||||
|
||||
|
||||
User-oriented documentation
|
||||
@ -72,11 +72,11 @@ developers seeking information on the kernel's user-space APIs.
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
admin-guide/index
|
||||
The kernel build system <kbuild/index>
|
||||
admin-guide/reporting-issues.rst
|
||||
User-space tools <tools/index>
|
||||
userspace-api/index
|
||||
Administration <admin-guide/index>
|
||||
Build system <kbuild/index>
|
||||
Reporting issues <admin-guide/reporting-issues.rst>
|
||||
Userspace tools <tools/index>
|
||||
Userspace API <userspace-api/index>
|
||||
|
||||
See also: the `Linux man pages <https://www.kernel.org/doc/man-pages/>`_,
|
||||
which are kept separately from the kernel's own documentation.
|
||||
@ -89,8 +89,8 @@ platform firmwares.
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
firmware-guide/index
|
||||
devicetree/index
|
||||
Firmware <firmware-guide/index>
|
||||
Firmware and Devicetree <devicetree/index>
|
||||
|
||||
|
||||
Architecture-specific documentation
|
||||
@ -99,7 +99,7 @@ Architecture-specific documentation
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
arch/index
|
||||
CPU architectures <arch/index>
|
||||
|
||||
|
||||
Other documentation
|
||||
@ -112,7 +112,7 @@ to ReStructured Text format, or are simply too old.
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
staging/index
|
||||
Unsorted documentation <staging/index>
|
||||
|
||||
|
||||
Translations
|
||||
@ -121,7 +121,7 @@ Translations
|
||||
.. toctree::
|
||||
:maxdepth: 2
|
||||
|
||||
translations/index
|
||||
Translations <translations/index>
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
@ -102,7 +102,10 @@ to do something different in the near future.
|
||||
../doc-guide/maintainer-profile
|
||||
../nvdimm/maintainer-entry-profile
|
||||
../arch/riscv/patch-acceptance
|
||||
../process/maintainer-soc
|
||||
../process/maintainer-soc-clean-dts
|
||||
../driver-api/media/maintainer-entry-profile
|
||||
../process/maintainer-netdev
|
||||
../driver-api/vfio-pci-device-specific-driver-acceptance
|
||||
../nvme/feature-and-quirk-policy
|
||||
../filesystems/xfs/xfs-maintainer-entry-profile
|
||||
|
@ -324,7 +324,7 @@ Contact Info
|
||||
The code is currently maintained by Roopa Prabhu <roopa@nvidia.com> and
|
||||
Nikolay Aleksandrov <razor@blackwall.org>. Bridge bugs and enhancements
|
||||
are discussed on the linux-netdev mailing list netdev@vger.kernel.org and
|
||||
bridge@lists.linux-foundation.org.
|
||||
bridge@lists.linux.dev.
|
||||
|
||||
The list is open to anyone interested: http://vger.kernel.org/vger-lists.html#netdev
|
||||
|
||||
|
@ -144,8 +144,8 @@ Bison
|
||||
Since Linux 4.16, the build system generates parsers
|
||||
during build. This requires bison 2.0 or later.
|
||||
|
||||
pahole:
|
||||
-------
|
||||
pahole
|
||||
------
|
||||
|
||||
Since Linux 5.2, if CONFIG_DEBUG_INFO_BTF is selected, the build system
|
||||
generates BTF (BPF Type Format) from DWARF in vmlinux, a bit later from kernel
|
||||
|
@ -203,7 +203,7 @@ Do not unnecessarily use braces where a single statement will do.
|
||||
|
||||
and
|
||||
|
||||
.. code-block:: none
|
||||
.. code-block:: c
|
||||
|
||||
if (condition)
|
||||
do_this();
|
||||
@ -586,9 +586,9 @@ fix for this is to split it up into two error labels ``err_free_bar:`` and
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
err_free_bar:
|
||||
err_free_bar:
|
||||
kfree(foo->bar);
|
||||
err_free_foo:
|
||||
err_free_foo:
|
||||
kfree(foo);
|
||||
return ret;
|
||||
|
||||
@ -660,7 +660,7 @@ make a good program).
|
||||
So, you can either get rid of GNU emacs, or change it to use saner
|
||||
values. To do the latter, you can stick the following in your .emacs file:
|
||||
|
||||
.. code-block:: none
|
||||
.. code-block:: elisp
|
||||
|
||||
(defun c-lineup-arglist-tabs-only (ignored)
|
||||
"Line up argument lists by tabs, not spaces"
|
||||
@ -679,7 +679,7 @@ values. To do the latter, you can stick the following in your .emacs file:
|
||||
(c-offsets-alist . (
|
||||
(arglist-close . c-lineup-arglist-tabs-only)
|
||||
(arglist-cont-nonempty .
|
||||
(c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only))
|
||||
(c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only))
|
||||
(arglist-intro . +)
|
||||
(brace-list-intro . +)
|
||||
(c . c-lineup-C-comments)
|
||||
@ -899,7 +899,8 @@ which you should use to make sure messages are matched to the right device
|
||||
and driver, and are tagged with the right level: dev_err(), dev_warn(),
|
||||
dev_info(), and so forth. For messages that aren't associated with a
|
||||
particular device, <linux/printk.h> defines pr_notice(), pr_info(),
|
||||
pr_warn(), pr_err(), etc.
|
||||
pr_warn(), pr_err(), etc. When drivers are working properly they are quiet,
|
||||
so prefer to use dev_dbg/pr_debug unless something is wrong.
|
||||
|
||||
Coming up with good debugging messages can be quite a challenge; and once
|
||||
you have them, they can be a huge help for remote troubleshooting. However
|
||||
|
@ -255,7 +255,7 @@ an involved disclosed party. The current ambassadors list:
|
||||
IBM Power Anton Blanchard <anton@linux.ibm.com>
|
||||
IBM Z Christian Borntraeger <borntraeger@de.ibm.com>
|
||||
Intel Tony Luck <tony.luck@intel.com>
|
||||
Qualcomm Trilok Soni <tsoni@codeaurora.org>
|
||||
Qualcomm Trilok Soni <quic_tsoni@quicinc.com>
|
||||
RISC-V Palmer Dabbelt <palmer@dabbelt.com>
|
||||
Samsung Javier González <javier.gonz@samsung.com>
|
||||
|
||||
|
@ -351,8 +351,8 @@ Managing bug reports
|
||||
--------------------
|
||||
|
||||
One of the best ways to put into practice your hacking skills is by fixing
|
||||
bugs reported by other people. Not only you will help to make the kernel
|
||||
more stable, but you'll also learn to fix real world problems and you will
|
||||
bugs reported by other people. Not only will you help to make the kernel
|
||||
more stable, but you'll also learn to fix real-world problems and you will
|
||||
improve your skills, and other developers will be aware of your presence.
|
||||
Fixing bugs is one of the best ways to get merits among other developers,
|
||||
because not many people like wasting time fixing other people's bugs.
|
||||
|
@ -167,4 +167,4 @@ If no one can be found to internally review patches and you need
|
||||
help finding such a person, or if you have any other questions
|
||||
related to this document and the developer community's expectations,
|
||||
please reach out to the private Technical Advisory Board mailing list:
|
||||
<tech-board@lists.linux-foundation.org>.
|
||||
<tech-board@groups.linuxfoundation.org>.
|
||||
|
@ -1,7 +1,8 @@
|
||||
.. _submitchecklist:
|
||||
|
||||
=======================================
|
||||
Linux Kernel patch submission checklist
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
=======================================
|
||||
|
||||
Here are some basic things that developers should do if they want to see their
|
||||
kernel patch submissions accepted more quickly.
|
||||
@ -10,12 +11,73 @@ These are all above and beyond the documentation that is provided in
|
||||
:ref:`Documentation/process/submitting-patches.rst <submittingpatches>`
|
||||
and elsewhere regarding submitting Linux kernel patches.
|
||||
|
||||
Review your code
|
||||
================
|
||||
|
||||
1) If you use a facility then #include the file that defines/declares
|
||||
that facility. Don't depend on other header files pulling in ones
|
||||
that you use.
|
||||
|
||||
2) Builds cleanly:
|
||||
2) Check your patch for general style as detailed in
|
||||
:ref:`Documentation/process/coding-style.rst <codingstyle>`.
|
||||
|
||||
3) All memory barriers {e.g., ``barrier()``, ``rmb()``, ``wmb()``} need a
|
||||
comment in the source code that explains the logic of what they are doing
|
||||
and why.
|
||||
|
||||
Review Kconfig changes
|
||||
======================
|
||||
|
||||
1) Any new or modified ``CONFIG`` options do not muck up the config menu and
|
||||
default to off unless they meet the exception criteria documented in
|
||||
``Documentation/kbuild/kconfig-language.rst`` Menu attributes: default value.
|
||||
|
||||
2) All new ``Kconfig`` options have help text.
|
||||
|
||||
3) Has been carefully reviewed with respect to relevant ``Kconfig``
|
||||
combinations. This is very hard to get right with testing---brainpower
|
||||
pays off here.
|
||||
|
||||
Provide documentation
|
||||
=====================
|
||||
|
||||
1) Include :ref:`kernel-doc <kernel_doc>` to document global kernel APIs.
|
||||
(Not required for static functions, but OK there also.)
|
||||
|
||||
2) All new ``/proc`` entries are documented under ``Documentation/``
|
||||
|
||||
3) All new kernel boot parameters are documented in
|
||||
``Documentation/admin-guide/kernel-parameters.rst``.
|
||||
|
||||
4) All new module parameters are documented with ``MODULE_PARM_DESC()``
|
||||
|
||||
5) All new userspace interfaces are documented in ``Documentation/ABI/``.
|
||||
See ``Documentation/ABI/README`` for more information.
|
||||
Patches that change userspace interfaces should be CCed to
|
||||
linux-api@vger.kernel.org.
|
||||
|
||||
6) If any ioctl's are added by the patch, then also update
|
||||
``Documentation/userspace-api/ioctl/ioctl-number.rst``.
|
||||
|
||||
Check your code with tools
|
||||
==========================
|
||||
|
||||
1) Check for trivial violations with the patch style checker prior to
|
||||
submission (``scripts/checkpatch.pl``).
|
||||
You should be able to justify all violations that remain in
|
||||
your patch.
|
||||
|
||||
2) Check cleanly with sparse.
|
||||
|
||||
3) Use ``make checkstack`` and fix any problems that it finds.
|
||||
Note that ``checkstack`` does not point out problems explicitly,
|
||||
but any one function that uses more than 512 bytes on the stack is a
|
||||
candidate for change.
|
||||
|
||||
Build your code
|
||||
===============
|
||||
|
||||
1) Builds cleanly:
|
||||
|
||||
a) with applicable or modified ``CONFIG`` options ``=y``, ``=m``, and
|
||||
``=n``. No ``gcc`` warnings/errors, no linker warnings/errors.
|
||||
@ -28,93 +90,44 @@ and elsewhere regarding submitting Linux kernel patches.
|
||||
Use ``make htmldocs`` or ``make pdfdocs`` to check the build and
|
||||
fix any issues.
|
||||
|
||||
3) Builds on multiple CPU architectures by using local cross-compile tools
|
||||
or some other build farm.
|
||||
2) Builds on multiple CPU architectures by using local cross-compile tools
|
||||
or some other build farm. Note that ppc64 is a good architecture for
|
||||
cross-compilation checking because it tends to use ``unsigned long`` for
|
||||
64-bit quantities.
|
||||
|
||||
4) ppc64 is a good architecture for cross-compilation checking because it
|
||||
tends to use ``unsigned long`` for 64-bit quantities.
|
||||
3) Newly-added code has been compiled with ``gcc -W`` (use
|
||||
``make KCFLAGS=-W``). This will generate lots of noise, but is good
|
||||
for finding bugs like "warning: comparison between signed and unsigned".
|
||||
|
||||
5) Check your patch for general style as detailed in
|
||||
:ref:`Documentation/process/coding-style.rst <codingstyle>`.
|
||||
Check for trivial violations with the patch style checker prior to
|
||||
submission (``scripts/checkpatch.pl``).
|
||||
You should be able to justify all violations that remain in
|
||||
your patch.
|
||||
4) If your modified source code depends on or uses any of the kernel
|
||||
APIs or features that are related to the following ``Kconfig`` symbols,
|
||||
then test multiple builds with the related ``Kconfig`` symbols disabled
|
||||
and/or ``=m`` (if that option is available) [not all of these at the
|
||||
same time, just various/random combinations of them]:
|
||||
|
||||
6) Any new or modified ``CONFIG`` options do not muck up the config menu and
|
||||
default to off unless they meet the exception criteria documented in
|
||||
``Documentation/kbuild/kconfig-language.rst`` Menu attributes: default value.
|
||||
``CONFIG_SMP``, ``CONFIG_SYSFS``, ``CONFIG_PROC_FS``, ``CONFIG_INPUT``,
|
||||
``CONFIG_PCI``, ``CONFIG_BLOCK``, ``CONFIG_PM``, ``CONFIG_MAGIC_SYSRQ``,
|
||||
``CONFIG_NET``, ``CONFIG_INET=n`` (but latter with ``CONFIG_NET=y``).
|
||||
|
||||
7) All new ``Kconfig`` options have help text.
|
||||
Test your code
|
||||
==============
|
||||
|
||||
8) Has been carefully reviewed with respect to relevant ``Kconfig``
|
||||
combinations. This is very hard to get right with testing -- brainpower
|
||||
pays off here.
|
||||
1) Has been tested with ``CONFIG_PREEMPT``, ``CONFIG_DEBUG_PREEMPT``,
|
||||
``CONFIG_SLUB_DEBUG``, ``CONFIG_DEBUG_PAGEALLOC``, ``CONFIG_DEBUG_MUTEXES``,
|
||||
``CONFIG_DEBUG_SPINLOCK``, ``CONFIG_DEBUG_ATOMIC_SLEEP``,
|
||||
``CONFIG_PROVE_RCU`` and ``CONFIG_DEBUG_OBJECTS_RCU_HEAD`` all
|
||||
simultaneously enabled.
|
||||
|
||||
9) Check cleanly with sparse.
|
||||
2) Has been build- and runtime tested with and without ``CONFIG_SMP`` and
|
||||
``CONFIG_PREEMPT.``
|
||||
|
||||
10) Use ``make checkstack`` and fix any problems that it finds.
|
||||
3) All codepaths have been exercised with all lockdep features enabled.
|
||||
|
||||
.. note::
|
||||
4) Has been checked with injection of at least slab and page-allocation
|
||||
failures. See ``Documentation/fault-injection/``.
|
||||
If the new code is substantial, addition of subsystem-specific fault
|
||||
injection might be appropriate.
|
||||
|
||||
``checkstack`` does not point out problems explicitly,
|
||||
but any one function that uses more than 512 bytes on the stack is a
|
||||
candidate for change.
|
||||
|
||||
11) Include :ref:`kernel-doc <kernel_doc>` to document global kernel APIs.
|
||||
(Not required for static functions, but OK there also.) Use
|
||||
``make htmldocs`` or ``make pdfdocs`` to check the
|
||||
:ref:`kernel-doc <kernel_doc>` and fix any issues.
|
||||
|
||||
12) Has been tested with ``CONFIG_PREEMPT``, ``CONFIG_DEBUG_PREEMPT``,
|
||||
``CONFIG_DEBUG_SLAB``, ``CONFIG_DEBUG_PAGEALLOC``, ``CONFIG_DEBUG_MUTEXES``,
|
||||
``CONFIG_DEBUG_SPINLOCK``, ``CONFIG_DEBUG_ATOMIC_SLEEP``,
|
||||
``CONFIG_PROVE_RCU`` and ``CONFIG_DEBUG_OBJECTS_RCU_HEAD`` all
|
||||
simultaneously enabled.
|
||||
|
||||
13) Has been build- and runtime tested with and without ``CONFIG_SMP`` and
|
||||
``CONFIG_PREEMPT.``
|
||||
|
||||
14) All codepaths have been exercised with all lockdep features enabled.
|
||||
|
||||
15) All new ``/proc`` entries are documented under ``Documentation/``
|
||||
|
||||
16) All new kernel boot parameters are documented in
|
||||
``Documentation/admin-guide/kernel-parameters.rst``.
|
||||
|
||||
17) All new module parameters are documented with ``MODULE_PARM_DESC()``
|
||||
|
||||
18) All new userspace interfaces are documented in ``Documentation/ABI/``.
|
||||
See ``Documentation/ABI/README`` for more information.
|
||||
Patches that change userspace interfaces should be CCed to
|
||||
linux-api@vger.kernel.org.
|
||||
|
||||
19) Has been checked with injection of at least slab and page-allocation
|
||||
failures. See ``Documentation/fault-injection/``.
|
||||
|
||||
If the new code is substantial, addition of subsystem-specific fault
|
||||
injection might be appropriate.
|
||||
|
||||
20) Newly-added code has been compiled with ``gcc -W`` (use
|
||||
``make KCFLAGS=-W``). This will generate lots of noise, but is good
|
||||
for finding bugs like "warning: comparison between signed and unsigned".
|
||||
|
||||
21) Tested after it has been merged into the -mm patchset to make sure
|
||||
that it still works with all of the other queued patches and various
|
||||
changes in the VM, VFS, and other subsystems.
|
||||
|
||||
22) All memory barriers {e.g., ``barrier()``, ``rmb()``, ``wmb()``} need a
|
||||
comment in the source code that explains the logic of what they are doing
|
||||
and why.
|
||||
|
||||
23) If any ioctl's are added by the patch, then also update
|
||||
``Documentation/userspace-api/ioctl/ioctl-number.rst``.
|
||||
|
||||
24) If your modified source code depends on or uses any of the kernel
|
||||
APIs or features that are related to the following ``Kconfig`` symbols,
|
||||
then test multiple builds with the related ``Kconfig`` symbols disabled
|
||||
and/or ``=m`` (if that option is available) [not all of these at the
|
||||
same time, just various/random combinations of them]:
|
||||
|
||||
``CONFIG_SMP``, ``CONFIG_SYSFS``, ``CONFIG_PROC_FS``, ``CONFIG_INPUT``, ``CONFIG_PCI``, ``CONFIG_BLOCK``, ``CONFIG_PM``, ``CONFIG_MAGIC_SYSRQ``,
|
||||
``CONFIG_NET``, ``CONFIG_INET=n`` (but latter with ``CONFIG_NET=y``).
|
||||
5) Tested with the most recent tag of linux-next to make sure that it still
|
||||
works with all of the other queued patches and various changes in the VM,
|
||||
VFS, and other subsystems.
|
||||
|
@ -54,9 +54,7 @@
|
||||
\renewcommand*\l@section{\@dottedtocline{1}{2.4em}{3.2em}}
|
||||
\renewcommand*\l@subsection{\@dottedtocline{2}{5.6em}{4.3em}}
|
||||
\makeatother
|
||||
%% Sphinx < 1.8 doesn't have \sphinxtableofcontentshook
|
||||
\providecommand{\sphinxtableofcontentshook}{}
|
||||
%% Undefine it for compatibility with Sphinx 1.7.9
|
||||
%% Prevent default \sphinxtableofcontentshook from overwriting above tweaks.
|
||||
\renewcommand{\sphinxtableofcontentshook}{} % Empty the hook
|
||||
|
||||
% Prevent column squeezing of tabulary. \tymin is set by Sphinx as:
|
||||
@ -136,9 +134,6 @@
|
||||
}
|
||||
\newCJKfontfamily[JPsans]\jpsans{Noto Sans CJK JP}[AutoFakeSlant]
|
||||
\newCJKfontfamily[JPmono]\jpmono{Noto Sans Mono CJK JP}[AutoFakeSlant]
|
||||
% Dummy commands for Sphinx < 2.3 (no 'extrapackages' support)
|
||||
\providecommand{\onehalfspacing}{}
|
||||
\providecommand{\singlespacing}{}
|
||||
% Define custom macros to on/off CJK
|
||||
%% One and half spacing for CJK contents
|
||||
\newcommand{\kerneldocCJKon}{\makexeCJKactive\onehalfspacing}
|
||||
|
@ -61,9 +61,9 @@ class KernelDocDirective(Directive):
|
||||
env = self.state.document.settings.env
|
||||
cmd = [env.config.kerneldoc_bin, '-rst', '-enable-lineno']
|
||||
|
||||
# Pass the version string to kernel-doc, as it needs to use a different
|
||||
# dialect, depending what the C domain supports for each specific
|
||||
# Sphinx versions
|
||||
# Pass the version string to kernel-doc, as it needs to use a different
|
||||
# dialect, depending what the C domain supports for each specific
|
||||
# Sphinx versions
|
||||
cmd += ['-sphinx-version', sphinx.__version__]
|
||||
|
||||
filename = env.config.kerneldoc_srctree + '/' + self.arguments[0]
|
||||
|
@ -1,6 +1,3 @@
|
||||
# jinja2>=3.1 is not compatible with Sphinx<4.0
|
||||
jinja2<3.1
|
||||
# alabaster>=0.7.14 is not compatible with Sphinx<=3.3
|
||||
alabaster<0.7.14
|
||||
Sphinx==2.4.4
|
||||
alabaster
|
||||
Sphinx
|
||||
pyyaml
|
||||
|
@ -157,7 +157,7 @@ Returns 0 on success and an appropriate error value on failure.
|
||||
int rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst)
|
||||
|
||||
|
||||
sends a message across to the remote processor from a given endoint,
|
||||
sends a message across to the remote processor from a given endpoint,
|
||||
to a destination address provided by the user.
|
||||
|
||||
The user should specify the channel, the data it wants to send,
|
||||
|
@ -61,6 +61,8 @@ Storage interfaces
|
||||
scsi/index
|
||||
target/index
|
||||
|
||||
Other subsystems
|
||||
----------------
|
||||
**Fixme**: much more organizational work is needed here.
|
||||
|
||||
.. toctree::
|
||||
|
19
Documentation/translations/it_IT/RCU/index.rst
Normal file
19
Documentation/translations/it_IT/RCU/index.rst
Normal file
@ -0,0 +1,19 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
.. _it_rcu_concepts:
|
||||
|
||||
===============
|
||||
Concetti su RCU
|
||||
===============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
torture
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
Indici
|
||||
======
|
||||
|
||||
* :ref:`genindex`
|
369
Documentation/translations/it_IT/RCU/torture.rst
Normal file
369
Documentation/translations/it_IT/RCU/torture.rst
Normal file
@ -0,0 +1,369 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
.. include:: ../disclaimer-ita.rst
|
||||
|
||||
=============================================
|
||||
Le operazioni RCU per le verifiche *torture*
|
||||
=============================================
|
||||
|
||||
CONFIG_RCU_TORTURE_TEST
|
||||
=======================
|
||||
|
||||
L'opzione CONFIG_RCU_TORTURE_TEST è disponibile per tutte le implementazione di
|
||||
RCU. L'opzione creerà un modulo rcutorture che potrete caricare per avviare le
|
||||
verifiche. La verifica userà printk() per riportare lo stato, dunque potrete
|
||||
visualizzarlo con dmesg (magari usate grep per filtrare "torture"). Le verifiche
|
||||
inizieranno al caricamento, e si fermeranno alla sua rimozione.
|
||||
|
||||
I parametri di modulo hanno tutti il prefisso "rcutortute.", vedere
|
||||
Documentation/admin-guide/kernel-parameters.txt.
|
||||
|
||||
Rapporto
|
||||
========
|
||||
|
||||
Il rapporto sulle verifiche si presenta nel seguente modo::
|
||||
|
||||
rcu-torture:--- Start of test: nreaders=16 nfakewriters=4 stat_interval=30 verbose=0 test_no_idle_hz=1 shuffle_interval=3 stutter=5 irqreader=1 fqs_duration=0 fqs_holdoff=0 fqs_stutter=3 test_boost=1/0 test_boost_interval=7 test_boost_duration=4
|
||||
rcu-torture: rtc: (null) ver: 155441 tfle: 0 rta: 155441 rtaf: 8884 rtf: 155440 rtmbe: 0 rtbe: 0 rtbke: 0 rtbre: 0 rtbf: 0 rtb: 0 nt: 3055767
|
||||
rcu-torture: Reader Pipe: 727860534 34213 0 0 0 0 0 0 0 0 0
|
||||
rcu-torture: Reader Batch: 727877838 17003 0 0 0 0 0 0 0 0 0
|
||||
rcu-torture: Free-Block Circulation: 155440 155440 155440 155440 155440 155440 155440 155440 155440 155440 0
|
||||
rcu-torture:--- End of test: SUCCESS: nreaders=16 nfakewriters=4 stat_interval=30 verbose=0 test_no_idle_hz=1 shuffle_interval=3 stutter=5 irqreader=1 fqs_duration=0 fqs_holdoff=0 fqs_stutter=3 test_boost=1/0 test_boost_interval=7 test_boost_duration=4
|
||||
|
||||
Sulla maggior parte dei sistemi questo rapporto si produce col comando "dmesg |
|
||||
grep torture:". Su configurazioni più esoteriche potrebbe essere necessario
|
||||
usare altri comandi per visualizzare i messaggi di printk(). La funzione
|
||||
printk() usa KERN_ALERT, dunque i messaggi dovrebbero essere ben visibili. ;-)
|
||||
|
||||
La prima e l'ultima riga mostrano i parametri di module di rcutorture, e solo
|
||||
sull'ultima riga abbiamo il risultato finale delle verifiche effettuate che può
|
||||
essere "SUCCESS" (successo) or "FAILURE" (insuccesso).
|
||||
|
||||
Le voci sono le seguenti:
|
||||
|
||||
* "rtc": L'indirizzo in esadecimale della struttura attualmente visibile dai
|
||||
lettori.
|
||||
|
||||
* "ver": Il numero di volte dall'avvio che il processo scrittore di RCU ha
|
||||
cambiato la struttura visible ai lettori.
|
||||
|
||||
* "tfle": se non è zero, indica la lista di strutture "torture freelist" da
|
||||
mettere in "rtc" è vuota. Questa condizione è importante perché potrebbe
|
||||
illuderti che RCU stia funzionando mentre invece non è il caso. :-/
|
||||
|
||||
* "rta": numero di strutture allocate dalla lista "torture freelist".
|
||||
|
||||
* "rtaf": il numero di allocazioni fallite dalla lista "torture freelist" a
|
||||
causa del fatto che fosse vuota. Non è inusuale che sia diverso da zero, ma è
|
||||
un brutto segno se questo numero rappresenta una frazione troppo alta di
|
||||
"rta".
|
||||
|
||||
* "rtf": il numero di rilasci nella lista "torture freelist"
|
||||
|
||||
* "rtmbe": Un valore diverso da zero indica che rcutorture crede che
|
||||
rcu_assign_pointer() e rcu_dereference() non funzionino correttamente. Il
|
||||
valore dovrebbe essere zero.
|
||||
|
||||
* "rtbe": un valore diverso da zero indica che le funzioni della famiglia
|
||||
rcu_barrier() non funzionano correttamente.
|
||||
|
||||
* "rtbke": rcutorture è stato capace di creare dei kthread real-time per forzare
|
||||
l'inversione di priorità di RCU. Il valore dovrebbe essere zero.
|
||||
|
||||
* "rtbre": sebbene rcutorture sia riuscito a creare dei kthread capaci di
|
||||
forzare l'inversione di priorità, non è riuscito però ad impostarne la
|
||||
priorità real-time al livello 1. Il valore dovrebbe essere zero.
|
||||
|
||||
* "rtbf": Il numero di volte che è fallita la promozione della priorità per
|
||||
risolvere un'inversione.
|
||||
|
||||
* "rtb": Il numero di volte che rcutorture ha provato a forzare l'inversione di
|
||||
priorità. Il valore dovrebbe essere diverso da zero Se state verificando la
|
||||
promozione della priorità col parametro "test_bootst".
|
||||
|
||||
* "nt": il numero di volte che rcutorture ha eseguito codice lato lettura
|
||||
all'interno di un gestore di *timer*. Questo valore dovrebbe essere diverso da
|
||||
zero se avete specificato il parametro "irqreader".
|
||||
|
||||
* "Reader Pipe": un istogramma dell'età delle strutture viste dai lettori. RCU
|
||||
non funziona correttamente se una qualunque voce, dalla terza in poi, ha un
|
||||
valore diverso da zero. Se dovesse succedere, rcutorture stampa la stringa
|
||||
"!!!" per renderlo ben visibile. L'età di una struttura appena creata è zero,
|
||||
diventerà uno quando sparisce dalla visibilità di un lettore, e incrementata
|
||||
successivamente per ogni periodo di grazia; infine rilasciata dopo essere
|
||||
passata per (RCU_TORTURE_PIPE_LEN-2) periodi di grazia.
|
||||
|
||||
L'istantanea qui sopra è stata presa da una corretta implementazione di RCU.
|
||||
Se volete vedere come appare quando non funziona, sbizzarritevi nel romperla.
|
||||
;-)
|
||||
|
||||
* "Reader Batch": un istogramma di età di strutture viste dai lettori, ma
|
||||
conteggiata in termini di lotti piuttosto che periodi. Anche qui dalla terza
|
||||
voce in poi devono essere zero. La ragione d'esistere di questo rapporto è che
|
||||
a volte è più facile scatenare un terzo valore diverso da zero qui piuttosto
|
||||
che nella lista "Reader Pipe".
|
||||
|
||||
* "Free-Block Circulation": il numero di strutture *torture* che hanno raggiunto
|
||||
un certo punto nella catena. Il primo numero dovrebbe corrispondere
|
||||
strettamente al numero di strutture allocate; il secondo conta quelle rimosse
|
||||
dalla vista dei lettori. Ad eccezione dell'ultimo valore, gli altri
|
||||
corrispondono al numero di passaggi attraverso il periodo di grazia. L'ultimo
|
||||
valore dovrebbe essere zero, perché viene incrementato solo se il contatore
|
||||
della struttura torture viene in un qualche modo incrementato oltre il
|
||||
normale.
|
||||
|
||||
Una diversa implementazione di RCU potrebbe fornire informazioni aggiuntive. Per
|
||||
esempio, *Tree SRCU* fornisce anche la seguente riga::
|
||||
|
||||
srcud-torture: Tree SRCU per-CPU(idx=0): 0(35,-21) 1(-4,24) 2(1,1) 3(-26,20) 4(28,-47) 5(-9,4) 6(-10,14) 7(-14,11) T(1,6)
|
||||
|
||||
Questa riga mostra lo stato dei contatori per processore, in questo caso per
|
||||
*Tree SRCU*, usando un'allocazione dinamica di srcu_struct (dunque "srcud-"
|
||||
piuttosto che "srcu-"). I numeri fra parentesi sono i valori del "vecchio"
|
||||
contatore e di quello "corrente" per ogni processore. Il valore "idx" mappa
|
||||
questi due valori nell'array, ed è utile per il *debug*. La "T" finale contiene
|
||||
il valore totale dei contatori.
|
||||
|
||||
Uso su specifici kernel
|
||||
=======================
|
||||
|
||||
A volte può essere utile eseguire RCU torture su un kernel già compilato, ad
|
||||
esempio quando lo si sta per mettere in proeduzione. In questo caso, il kernel
|
||||
dev'essere compilato con CONFIG_RCU_TORTURE_TEST=m, cosicché le verifiche possano
|
||||
essere avviate usano modprobe e terminate con rmmod.
|
||||
|
||||
Per esempio, potreste usare questo script::
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
modprobe rcutorture
|
||||
sleep 3600
|
||||
rmmod rcutorture
|
||||
dmesg | grep torture:
|
||||
|
||||
Potete controllare il rapporto verificando manualmente la presenza del marcatore
|
||||
di errore "!!!". Ovviamente, siete liberi di scriverne uno più elaborato che
|
||||
identifichi automaticamente gli errori. Il comando "rmmod" forza la stampa di
|
||||
"SUCCESS" (successo), "FAILURE" (fallimento), o "RCU_HOTPLUG". I primi due sono
|
||||
autoesplicativi; invece, l'ultimo indica che non son stati trovati problemi in
|
||||
RCU, tuttavia ci sono stati problemi con CPU-hotplug.
|
||||
|
||||
|
||||
Uso sul kernel di riferimento
|
||||
=============================
|
||||
|
||||
Quando si usa rcutorture per verificare modifiche ad RCU stesso, spesso è
|
||||
necessario compilare un certo numero di kernel usando configurazioni diverse e
|
||||
con parametri d'avvio diversi. In questi casi, usare modprobe ed rmmod potrebbe
|
||||
richiedere molto tempo ed il processo essere suscettibile ad errori.
|
||||
|
||||
Dunque, viene messo a disposizione il programma
|
||||
tools/testing/selftests/rcutorture/bin/kvm.sh per le architetture x86, arm64 e
|
||||
powerpc. Di base, eseguirà la serie di verifiche elencate in
|
||||
tools/testing/selftests/rcutorture/configs/rcu/CFLIST. Ognuna di queste verrà
|
||||
eseguita per 30 minuti in una macchina virtuale con uno spazio utente minimale
|
||||
fornito da un initrd generato automaticamente. Al completamento, gli artefatti
|
||||
prodotti e i messaggi vengono analizzati alla ricerca di errori, ed i risultati
|
||||
delle esecuzioni riassunti in un rapporto.
|
||||
|
||||
Su grandi sistemi, le verifiche di rcutorture posso essere velocizzare passano a
|
||||
kvm.sh l'argomento --cpus. Per esempio, su un sistema a 64 processori, "--cpus
|
||||
43" userà fino a 43 processori per eseguire contemporaneamente le verifiche. Su
|
||||
un kernel v5.4 per eseguire tutti gli scenari in due serie, riduce il tempo
|
||||
d'esecuzione da otto ore a un'ora (senza contare il tempo per compilare sedici
|
||||
kernel). L'argomento "--dryrun sched" non eseguirà verifiche, piuttosto vi
|
||||
informerà su come queste verranno organizzate in serie. Questo può essere utile
|
||||
per capire quanti processori riservare per le verifiche in --cpus.
|
||||
|
||||
Non serve eseguire tutti gli scenari di verifica per ogni modifica. Per esempio,
|
||||
per una modifica a Tree SRCU potete eseguire gli scenari SRCU-N e SRCU-P. Per
|
||||
farlo usate l'argomento --configs di kvm.sh in questo modo: "--configs 'SRCU-N
|
||||
SRCU-P'". Su grandi sistemi si possono eseguire più copie degli stessi scenari,
|
||||
per esempio, un hardware che permette di eseguire 448 thread, può eseguire 5
|
||||
istanze complete contemporaneamente. Per farlo::
|
||||
|
||||
kvm.sh --cpus 448 --configs '5*CFLIST'
|
||||
|
||||
Oppure, lo stesso sistema, può eseguire contemporaneamente 56 istanze dello
|
||||
scenario su otto processori::
|
||||
|
||||
kvm.sh --cpus 448 --configs '56*TREE04'
|
||||
|
||||
O ancora 28 istanze per ogni scenario su otto processori::
|
||||
|
||||
kvm.sh --cpus 448 --configs '28*TREE03 28*TREE04'
|
||||
|
||||
Ovviamente, ogni esecuzione utilizzerà della memoria. Potete limitarne l'uso con
|
||||
l'argomento --memory, che di base assume il valore 512M. Per poter usare valori
|
||||
piccoli dovrete disabilitare le verifiche *callback-flooding* usando il
|
||||
parametro --bootargs che vedremo in seguito.
|
||||
|
||||
A volte è utile avere informazioni aggiuntive di debug, in questo caso potete
|
||||
usare il parametro --kconfig, per esempio, ``--kconfig
|
||||
'CONFIG_RCU_EQS_DEBUG=y'``. In aggiunta, ci sono i parametri --gdb, --kasan, and
|
||||
kcsan. Da notare che --gdb vi limiterà all'uso di un solo scenario per
|
||||
esecuzione di kvm.sh e richiede di avere anche un'altra finestra aperta dalla
|
||||
quale eseguire ``gdb`` come viene spiegato dal programma.
|
||||
|
||||
Potete passare anche i parametri d'avvio del kernel, per esempio, per
|
||||
controllare i parametri del modulo rcutorture. Per esempio, per verificare
|
||||
modifiche del codice RCU CPU stall-warning, usate ``bootargs
|
||||
'rcutorture.stall_cpu=30``. Il programma riporterà un fallimento, ossia il
|
||||
risultato della verifica. Come visto in precedenza, ridurre la memoria richiede
|
||||
la disabilitazione delle verifiche *callback-flooding*::
|
||||
|
||||
kvm.sh --cpus 448 --configs '56*TREE04' --memory 128M \
|
||||
--bootargs 'rcutorture.fwd_progress=0'
|
||||
|
||||
A volte tutto quello che serve è una serie completa di compilazioni del kernel.
|
||||
Questo si ottiene col parametro --buildonly.
|
||||
|
||||
Il parametro --duration sovrascrive quello di base di 30 minuti. Per esempio,
|
||||
con ``--duration 2d`` l'esecuzione sarà di due giorni, ``--duraction 5min`` di
|
||||
cinque minuti, e ``--duration 45s`` di 45 secondi. L'ultimo può essere utile per
|
||||
scovare rari errori nella sequenza d'avvio.
|
||||
|
||||
Infine, il parametro --trust-make permette ad ogni nuova compilazione del kernel
|
||||
di riutilizzare tutto il possibile da quelle precedenti. Da notare che senza il
|
||||
parametro --trust-make, i vostri file di *tag* potrebbero essere distrutti.
|
||||
|
||||
Ci sono altri parametri più misteriosi che sono documentati nel codice sorgente
|
||||
dello programma kvm.sh.
|
||||
|
||||
Se un'esecuzione contiene degli errori, il loro numero durante la compilazione e
|
||||
all'esecuzione verranno elencati alla fine fra i risultati di kvm.sh (che vi
|
||||
consigliamo caldamente di reindirizzare verso un file). I file prodotti dalla
|
||||
compilazione ed i risultati stampati vengono salvati, usando un riferimento
|
||||
temporale, nelle cartella tools/testing/selftests/rcutorture/res. Una cartella
|
||||
di queste cartelle può essere fornita a kvm-find-errors.sh per estrarne gli
|
||||
errori. Per esempio::
|
||||
|
||||
tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh \
|
||||
tools/testing/selftests/rcutorture/res/2020.01.20-15.54.23
|
||||
|
||||
Tuttavia, molto spesso è più conveniente aprire i file direttamente. I file
|
||||
riguardanti tutti gli scenari di un'esecuzione di trovano nella cartella
|
||||
principale (2020.01.20-15.54.23 nell'esempio precedente), mentre quelli
|
||||
specifici per scenario si trovano in sotto cartelle che prendono il nome dello
|
||||
scenario stesso (per esempio, "TREE04"). Se un dato scenario viene eseguito più
|
||||
di una volta (come abbiamo visto con "--configs '56*TREE04'"), allora dalla
|
||||
seconda esecuzione in poi le sottocartelle includeranno un numero di
|
||||
progressione, per esempio "TREE04.2", "TREE04.3", e via dicendo.
|
||||
|
||||
Il file solitamente più usato nella cartella principale è testid.txt. Se la
|
||||
verifica viene eseguita in un repositorio git, allora questo file conterrà il
|
||||
*commit* sul quale si basano le verifiche, mentre tutte le modifiche non
|
||||
registrare verranno mostrate in formato diff.
|
||||
|
||||
I file solitamente più usati nelle cartelle di scenario sono:
|
||||
|
||||
.config
|
||||
Questo file contiene le opzioni di Kconfig
|
||||
|
||||
Make.out
|
||||
Questo file contiene il risultato di compilazione per uno specifico scenario
|
||||
|
||||
console.log
|
||||
Questo file contiene il risultato d'esecuzione per uno specifico scenario.
|
||||
Questo file può essere esaminato una volta che il kernel è stato avviato,
|
||||
ma potrebbe non esistere se l'avvia non è fallito.
|
||||
|
||||
vmlinux
|
||||
Questo file contiene il kernel, e potrebbe essere utile da esaminare con
|
||||
programmi come pbjdump e gdb
|
||||
|
||||
Ci sono altri file, ma vengono usati meno. Molti sono utili all'analisi di
|
||||
rcutorture stesso o dei suoi programmi.
|
||||
|
||||
Nel kernel v5.4, su un sistema a 12 processori, un'esecuzione senza errori
|
||||
usando gli scenari di base produce il seguente risultato::
|
||||
|
||||
SRCU-N ------- 804233 GPs (148.932/s) [srcu: g10008272 f0x0 ]
|
||||
SRCU-P ------- 202320 GPs (37.4667/s) [srcud: g1809476 f0x0 ]
|
||||
SRCU-t ------- 1122086 GPs (207.794/s) [srcu: g0 f0x0 ]
|
||||
SRCU-u ------- 1111285 GPs (205.794/s) [srcud: g1 f0x0 ]
|
||||
TASKS01 ------- 19666 GPs (3.64185/s) [tasks: g0 f0x0 ]
|
||||
TASKS02 ------- 20541 GPs (3.80389/s) [tasks: g0 f0x0 ]
|
||||
TASKS03 ------- 19416 GPs (3.59556/s) [tasks: g0 f0x0 ]
|
||||
TINY01 ------- 836134 GPs (154.84/s) [rcu: g0 f0x0 ] n_max_cbs: 34198
|
||||
TINY02 ------- 850371 GPs (157.476/s) [rcu: g0 f0x0 ] n_max_cbs: 2631
|
||||
TREE01 ------- 162625 GPs (30.1157/s) [rcu: g1124169 f0x0 ]
|
||||
TREE02 ------- 333003 GPs (61.6672/s) [rcu: g2647753 f0x0 ] n_max_cbs: 35844
|
||||
TREE03 ------- 306623 GPs (56.782/s) [rcu: g2975325 f0x0 ] n_max_cbs: 1496497
|
||||
CPU count limited from 16 to 12
|
||||
TREE04 ------- 246149 GPs (45.5831/s) [rcu: g1695737 f0x0 ] n_max_cbs: 434961
|
||||
TREE05 ------- 314603 GPs (58.2598/s) [rcu: g2257741 f0x2 ] n_max_cbs: 193997
|
||||
TREE07 ------- 167347 GPs (30.9902/s) [rcu: g1079021 f0x0 ] n_max_cbs: 478732
|
||||
CPU count limited from 16 to 12
|
||||
TREE09 ------- 752238 GPs (139.303/s) [rcu: g13075057 f0x0 ] n_max_cbs: 99011
|
||||
|
||||
Ripetizioni
|
||||
===========
|
||||
|
||||
Immaginate di essere alla caccia di un raro problema che si verifica all'avvio.
|
||||
Potreste usare kvm.sh, tuttavia questo ricompilerebbe il kernel ad ogni
|
||||
esecuzione. Se avete bisogno di (diciamo) 1000 esecuzioni per essere sicuri di
|
||||
aver risolto il problema, allora queste inutili ricompilazioni possono diventare
|
||||
estremamente fastidiose.
|
||||
|
||||
Per questo motivo esiste kvm-again.sh.
|
||||
|
||||
Immaginate che un'esecuzione precedente di kvm.sh abbia lasciato i suoi
|
||||
artefatti nella cartella::
|
||||
|
||||
tools/testing/selftests/rcutorture/res/2022.11.03-11.26.28
|
||||
|
||||
Questa esecuzione può essere rieseguita senza ricompilazioni::
|
||||
|
||||
kvm-again.sh tools/testing/selftests/rcutorture/res/2022.11.03-11.26.28
|
||||
|
||||
Alcuni dei parametri originali di kvm.sh possono essere sovrascritti, in
|
||||
particolare --duration e --bootargs. Per esempio::
|
||||
|
||||
kvm-again.sh tools/testing/selftests/rcutorture/res/2022.11.03-11.26.28 \
|
||||
--duration 45s
|
||||
|
||||
rieseguirebbe il test precedente, ma solo per 45 secondi, e quindi aiutando a
|
||||
trovare quel raro problema all'avvio sopracitato.
|
||||
|
||||
Esecuzioni distribuite
|
||||
======================
|
||||
|
||||
Sebbene kvm.sh sia utile, le sue verifiche sono limitate ad un singolo sistema.
|
||||
Non è poi così difficile usare un qualsiasi ambiente di sviluppo per eseguire
|
||||
(diciamo) 5 istanze di kvm.sh su altrettanti sistemi, ma questo avvierebbe
|
||||
inutili ricompilazioni del kernel. In aggiunta, il processo di distribuzione
|
||||
degli scenari di verifica per rcutorture sui sistemi disponibili richiede
|
||||
scrupolo perché soggetto ad errori.
|
||||
|
||||
Per questo esiste kvm-remote.sh.
|
||||
|
||||
Se il seguente comando funziona::
|
||||
|
||||
ssh system0 date
|
||||
|
||||
e funziona anche per system1, system2, system3, system4, e system5, e tutti
|
||||
questi sistemi hanno 64 CPU, allora potere eseguire::
|
||||
|
||||
kvm-remote.sh "system0 system1 system2 system3 system4 system5" \
|
||||
--cpus 64 --duration 8h --configs "5*CFLIST"
|
||||
|
||||
Questo compilerà lo scenario di base sul sistema locale, poi lo distribuirà agli
|
||||
altri cinque sistemi elencati fra i parametri, ed eseguirà ogni scenario per
|
||||
otto ore. Alla fine delle esecuzioni, i risultati verranno raccolti, registrati,
|
||||
e stampati. La maggior parte dei parametri di kvm.sh possono essere usati con
|
||||
kvm-remote.sh, tuttavia la lista dei sistemi deve venire sempre per prima.
|
||||
|
||||
L'argomento di kvm.sh ``--dryrun scenarios`` può essere utile per scoprire
|
||||
quanti scenari potrebbero essere eseguiti in gruppo di sistemi.
|
||||
|
||||
Potete rieseguire anche una precedente esecuzione remota come abbiamo già fatto
|
||||
per kvm.sh::
|
||||
|
||||
kvm-remote.sh "system0 system1 system2 system3 system4 system5" \
|
||||
tools/testing/selftests/rcutorture/res/2022.11.03-11.26.28-remote \
|
||||
--duration 24h
|
||||
|
||||
In questo caso, la maggior parte dei parametri di kvm-again.sh possono essere
|
||||
usati dopo il percorso alla cartella contenente gli artefatti dell'esecuzione da
|
||||
ripetere.
|
@ -10,6 +10,18 @@ Utilità di base
|
||||
|
||||
symbol-namespaces
|
||||
|
||||
Primitive di sincronizzazione
|
||||
=============================
|
||||
|
||||
Come Linux impedisce che tutto si verifichi contemporaneamente. Consultate
|
||||
Documentation/translations/it_IT/locking/index.rst per maggiorni informazioni
|
||||
sul tema.
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
../RCU/index
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
Indices
|
||||
|
99
Documentation/translations/it_IT/i2c/i2c-protocol.rst
Normal file
99
Documentation/translations/it_IT/i2c/i2c-protocol.rst
Normal file
@ -0,0 +1,99 @@
|
||||
=================
|
||||
Il protocollo I2C
|
||||
=================
|
||||
|
||||
Questo documento è una panoramica delle transazioni di base I2C e delle API
|
||||
del kernel per eseguirli.
|
||||
|
||||
Spiegazione dei simboli
|
||||
=======================
|
||||
|
||||
=============== ===========================================================
|
||||
S Condizione di avvio
|
||||
P Condizione di stop
|
||||
Rd/Wr (1 bit) Bit di lettura/scrittura. Rd vale 1, Wr vale 0.
|
||||
A, NA (1 bit) Bit di riconoscimento (ACK) e di non riconoscimento (NACK).
|
||||
Addr (7 bit) Indirizzo I2C a 7 bit. Nota che questo può essere espanso
|
||||
per ottenere un indirizzo I2C a 10 bit.
|
||||
Dati (8 bit) Un byte di dati.
|
||||
|
||||
[..] Fra parentesi quadre i dati inviati da dispositivi I2C,
|
||||
anziché dal master.
|
||||
=============== ===========================================================
|
||||
|
||||
|
||||
Transazione semplice di invio
|
||||
=============================
|
||||
|
||||
Implementato da i2c_master_send()::
|
||||
|
||||
S Addr Wr [A] Dati [A] Dati [A] ... [A] Dati [A] P
|
||||
|
||||
|
||||
Transazione semplice di ricezione
|
||||
=================================
|
||||
|
||||
Implementato da i2c_master_recv()::
|
||||
|
||||
S Addr Rd [A] [Dati] A [Dati] A ... A [Dati] NA P
|
||||
|
||||
|
||||
Transazioni combinate
|
||||
=====================
|
||||
|
||||
Implementato da i2c_transfer().
|
||||
|
||||
Sono come le transazioni di cui sopra, ma invece di uno condizione di stop P
|
||||
viene inviata una condizione di inizio S e la transazione continua.
|
||||
Un esempio di lettura di un byte, seguita da una scrittura di un byte::
|
||||
|
||||
S Addr Rd [A] [Dati] NA S Addr Wr [A] Dati [A] P
|
||||
|
||||
|
||||
Transazioni modificate
|
||||
======================
|
||||
|
||||
Le seguenti modifiche al protocollo I2C possono essere generate
|
||||
impostando questi flag per i messaggi I2C. Ad eccezione di I2C_M_NOSTART, sono
|
||||
di solito necessari solo per risolvere problemi di un dispositivo:
|
||||
|
||||
I2C_M_IGNORE_NAK:
|
||||
Normalmente il messaggio viene interrotto immediatamente se il dispositivo
|
||||
risponde con [NA]. Impostando questo flag, si considera qualsiasi [NA] come
|
||||
[A] e tutto il messaggio viene inviato.
|
||||
Questi messaggi potrebbero comunque non riuscire a raggiungere il timeout
|
||||
SCL basso->alto.
|
||||
|
||||
I2C_M_NO_RD_ACK:
|
||||
In un messaggio di lettura, il bit A/NA del master viene saltato.
|
||||
|
||||
I2C_M_NOSTART:
|
||||
In una transazione combinata, potrebbe non essere generato alcun
|
||||
"S Addr Wr/Rd [A]".
|
||||
Ad esempio, impostando I2C_M_NOSTART sul secondo messaggio parziale
|
||||
genera qualcosa del tipo::
|
||||
|
||||
S Addr Rd [A] [Dati] NA Dati [A] P
|
||||
|
||||
Se si imposta il flag I2C_M_NOSTART per il primo messaggio parziale,
|
||||
non viene generato Addr, ma si genera la condizione di avvio S.
|
||||
Questo probabilmente confonderà tutti gli altri dispositivi sul bus, quindi
|
||||
meglio non usarlo.
|
||||
|
||||
Questo viene spesso utilizzato per raccogliere le trasmissioni da più
|
||||
buffer di dati presenti nella memoria di sistema in qualcosa che appare
|
||||
come un singolo trasferimento verso il dispositivo I2C. Inoltre, alcuni
|
||||
dispositivi particolari lo utilizzano anche tra i cambi di direzione.
|
||||
|
||||
I2C_M_REV_DIR_ADDR:
|
||||
Questo inverte il flag Rd/Wr. Cioè, se si vuole scrivere, ma si ha bisogno
|
||||
di emettere una Rd invece di una Wr, o viceversa, si può impostare questo
|
||||
flag.
|
||||
Per esempio::
|
||||
|
||||
S Addr Rd [A] Dati [A] Dati [A] ... [A] Dati [A] P
|
||||
|
||||
I2C_M_STOP:
|
||||
Forza una condizione di stop (P) dopo il messaggio. Alcuni protocolli
|
||||
simili a I2C come SCCB lo richiedono. Normalmente, non si vuole essere
|
||||
interrotti tra i messaggi di un trasferimento.
|
46
Documentation/translations/it_IT/i2c/index.rst
Normal file
46
Documentation/translations/it_IT/i2c/index.rst
Normal file
@ -0,0 +1,46 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
=========================
|
||||
Il sottosistema I2C/SMBus
|
||||
=========================
|
||||
|
||||
Introduzione
|
||||
============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
summary
|
||||
i2c-protocol
|
||||
|
||||
Scrivere un device driver
|
||||
=========================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
Debugging
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
Slave I2C
|
||||
=========
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
|
||||
Argomenti avanzati
|
||||
==================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
Indici
|
||||
======
|
||||
|
||||
* :ref:`genindex`
|
64
Documentation/translations/it_IT/i2c/summary.rst
Normal file
64
Documentation/translations/it_IT/i2c/summary.rst
Normal file
@ -0,0 +1,64 @@
|
||||
==========================
|
||||
Introduzione a I2C e SMBus
|
||||
==========================
|
||||
|
||||
I²C (letteralmente "I al quadrato C" e scritto I2C nella documentazione del
|
||||
kernel) è un protocollo sviluppato da Philips. É un protocollo lento a 2 fili
|
||||
(a velocità variabile, al massimo 400KHz), con un'estensione per le velocità
|
||||
elevate (3.4 MHz). Questo protocollo offre un bus a basso costo per collegare
|
||||
dispositivi di vario genere a cui si accede sporadicamente e utilizzando
|
||||
poca banda. Alcuni sistemi usano varianti che non rispettano i requisiti
|
||||
originali, per cui non sono indicati come I2C, ma hanno nomi diversi, per
|
||||
esempio TWI (Interfaccia a due fili), IIC.
|
||||
|
||||
L'ultima specifica ufficiale I2C è la `"Specifica I2C-bus e manuale utente"
|
||||
(UM10204) <https://www.nxp.com/webapp/Download?colCode=UM10204>`_
|
||||
pubblicata da NXP Semiconductors. Tuttavia, è necessario effettuare il login
|
||||
al sito per accedere al PDF. Una versione precedente della specifica
|
||||
(revisione 6) è archiviata
|
||||
`qui <https://web.archive.org/web/20210813122132/
|
||||
https://www.nxp.com/docs/en/user-guide/UM10204.pdf>`_.
|
||||
|
||||
SMBus (Bus per la gestione del sistema) si basa sul protocollo I2C ed è
|
||||
principalmente un sottoinsieme di protocolli e segnali I2C. Molti dispositivi
|
||||
I2C funzioneranno su SMBus, ma alcuni protocolli SMBus aggiungono semantica
|
||||
oltre quanto richiesto da I2C. Le moderne schede madri dei PC si affidano a
|
||||
SMBus. I più comuni dispositivi collegati tramite SMBus sono moduli RAM
|
||||
configurati utilizzando EEPROM I2C, e circuiti integrati di monitoraggio
|
||||
hardware.
|
||||
|
||||
Poiché SMBus è principalmente un sottoinsieme del bus I2C,
|
||||
possiamo farne uso su molti sistemi I2C. Ci sono però sistemi che non
|
||||
soddisfano i vincoli elettrici sia di SMBus che di I2C; e altri che non possono
|
||||
implementare tutta la semantica o messaggi comuni del protocollo SMBus.
|
||||
|
||||
|
||||
Terminologia
|
||||
============
|
||||
|
||||
Utilizzando la terminologia della documentazione ufficiale, il bus I2C connette
|
||||
uno o più circuiti integrati *master* e uno o più circuiti integrati *slave*.
|
||||
|
||||
.. kernel-figure:: ../../../i2c/i2c_bus.svg
|
||||
:alt: Un semplice bus I2C con un master e 3 slave
|
||||
|
||||
Un semplice Bus I2C
|
||||
|
||||
Un circuito integrato **master** è un nodo che inizia le comunicazioni con gli
|
||||
slave. Nell'implementazione del kernel Linux è chiamato **adattatore** o bus. I
|
||||
driver degli adattatori si trovano nella sottocartella ``drivers/i2c/busses/``.
|
||||
|
||||
Un **algoritmo** contiene codice generico che può essere utilizzato per
|
||||
implementare una intera classe di adattatori I2C. Ciascun driver dell'
|
||||
adattatore specifico dipende da un driver dell'algoritmo nella sottocartella
|
||||
``drivers/i2c/algos/`` o include la propria implementazione.
|
||||
|
||||
Un circuito integrato **slave** è un nodo che risponde alle comunicazioni
|
||||
quando indirizzato dal master. In Linux è chiamato **client** (dispositivo). I
|
||||
driver dei dispositivi sono contenuti in una cartella specifica per la
|
||||
funzionalità che forniscono, ad esempio ``drivers/media/gpio/`` per espansori
|
||||
GPIO e ``drivers/media/i2c/`` per circuiti integrati relativi ai video.
|
||||
|
||||
Per la configurazione di esempio in figura, avrai bisogno di un driver per il
|
||||
tuo adattatore I2C e driver per i tuoi dispositivi I2C (solitamente un driver
|
||||
per ciascuno dispositivo).
|
@ -91,6 +91,8 @@ interfacciarsi con il resto del kernel.
|
||||
:maxdepth: 1
|
||||
|
||||
core-api/index
|
||||
Sincronizzazione nel kernel <locking/index>
|
||||
subsystem-apis
|
||||
|
||||
Strumenti e processi per lo sviluppo
|
||||
====================================
|
||||
|
20
Documentation/translations/it_IT/locking/index.rst
Normal file
20
Documentation/translations/it_IT/locking/index.rst
Normal file
@ -0,0 +1,20 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
================
|
||||
Sincronizzazione
|
||||
================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
locktypes
|
||||
lockdep-design
|
||||
lockstat
|
||||
locktorture
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
Indici
|
||||
======
|
||||
|
||||
* :ref:`genindex`
|
678
Documentation/translations/it_IT/locking/lockdep-design.rst
Normal file
678
Documentation/translations/it_IT/locking/lockdep-design.rst
Normal file
@ -0,0 +1,678 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
.. include:: ../disclaimer-ita.rst
|
||||
|
||||
Validatore di sincronizzazione durante l'esecuzione
|
||||
===================================================
|
||||
|
||||
Classi di blocchi
|
||||
-----------------
|
||||
|
||||
L'oggetto su cui il validatore lavora è una "classe" di blocchi.
|
||||
|
||||
Una classe di blocchi è un gruppo di blocchi che seguono le stesse regole di
|
||||
sincronizzazione, anche quando i blocchi potrebbero avere più istanze (anche
|
||||
decine di migliaia). Per esempio un blocco nella struttura inode è una classe,
|
||||
mentre ogni inode sarà un'istanza di questa classe di blocco.
|
||||
|
||||
Il validatore traccia lo "stato d'uso" di una classe di blocchi e le sue
|
||||
dipendenze con altre classi. L'uso di un blocco indica come quel blocco viene
|
||||
usato rispetto al suo contesto d'interruzione, mentre le dipendenze di un blocco
|
||||
possono essere interpretate come il loro ordine; per esempio L1 -> L2 suggerisce
|
||||
che un processo cerca di acquisire L2 mentre già trattiene L1. Dal punto di
|
||||
vista di lockdep, i due blocchi (L1 ed L2) non sono per forza correlati: quella
|
||||
dipendenza indica solamente l'ordine in cui sono successe le cose. Il validatore
|
||||
verifica permanentemente la correttezza dell'uso dei blocchi e delle loro
|
||||
dipendenze, altrimenti ritornerà un errore.
|
||||
|
||||
Il comportamento di una classe di blocchi viene costruito dall'insieme delle sue
|
||||
istanze. Una classe di blocco viene registrata alla creazione della sua prima
|
||||
istanza, mentre tutte le successive istanze verranno mappate; dunque, il loro
|
||||
uso e le loro dipendenze contribuiranno a costruire quello della classe. Una
|
||||
classe di blocco non sparisce quando sparisce una sua istanza, ma può essere
|
||||
rimossa quando il suo spazio in memoria viene reclamato. Per esempio, questo
|
||||
succede quando si rimuove un modulo, o quando una *workqueue* viene eliminata.
|
||||
|
||||
Stato
|
||||
-----
|
||||
|
||||
Il validatore traccia l'uso cronologico delle classi di blocchi e ne divide
|
||||
l'uso in categorie (4 USI * n STATI + 1).
|
||||
|
||||
I quattro USI possono essere:
|
||||
|
||||
- 'sempre trattenuto nel contesto <STATO>'
|
||||
- 'sempre trattenuto come blocco di lettura nel contesto <STATO>'
|
||||
- 'sempre trattenuto con <STATO> abilitato'
|
||||
- 'sempre trattenuto come blocco di lettura con <STATO> abilitato'
|
||||
|
||||
gli `n` STATI sono codificati in kernel/locking/lockdep_states.h, ad oggi
|
||||
includono:
|
||||
|
||||
- hardirq
|
||||
- softirq
|
||||
|
||||
infine l'ultima categoria è:
|
||||
|
||||
- 'sempre trattenuto' [ == !unused ]
|
||||
|
||||
Quando vengono violate le regole di sincronizzazione, questi bit di utilizzo
|
||||
vengono presentati nei messaggi di errore di sincronizzazione, fra parentesi
|
||||
graffe, per un totale di `2 * n` (`n`: bit STATO). Un esempio inventato::
|
||||
|
||||
modprobe/2287 is trying to acquire lock:
|
||||
(&sio_locks[i].lock){-.-.}, at: [<c02867fd>] mutex_lock+0x21/0x24
|
||||
|
||||
but task is already holding lock:
|
||||
(&sio_locks[i].lock){-.-.}, at: [<c02867fd>] mutex_lock+0x21/0x24
|
||||
|
||||
Per un dato blocco, da sinistra verso destra, la posizione del bit indica l'uso
|
||||
del blocco e di un eventuale blocco di lettura, per ognuno degli `n` STATI elencati
|
||||
precedentemente. Il carattere mostrato per ogni bit indica:
|
||||
|
||||
=== ===========================================================================
|
||||
'.' acquisito con interruzioni disabilitate fuori da un contesto d'interruzione
|
||||
'-' acquisito in contesto d'interruzione
|
||||
'+' acquisito con interruzioni abilitate
|
||||
'?' acquisito in contesto d'interruzione con interruzioni abilitate
|
||||
=== ===========================================================================
|
||||
|
||||
Il seguente esempio mostra i bit::
|
||||
|
||||
(&sio_locks[i].lock){-.-.}, at: [<c02867fd>] mutex_lock+0x21/0x24
|
||||
||||
|
||||
||| \-> softirq disabilitati e fuori da un contesto di softirq
|
||||
|| \--> acquisito in un contesto di softirq
|
||||
| \---> hardirq disabilitati e fuori da un contesto di hardirq
|
||||
\----> acquisito in un contesto di hardirq
|
||||
|
||||
Per un dato STATO, che il blocco sia mai stato acquisito in quel contesto di
|
||||
STATO, o che lo STATO sia abilitato, ci lascia coi quattro possibili scenari
|
||||
mostrati nella seguente tabella. Il carattere associato al bit indica con
|
||||
esattezza in quale scenario ci si trova al momento del rapporto.
|
||||
|
||||
+---------------+---------------+------------------+
|
||||
| | irq abilitati | irq disabilitati |
|
||||
+---------------+---------------+------------------+
|
||||
| sempre in irq | '?' | '-' |
|
||||
+---------------+---------------+------------------+
|
||||
| mai in irq | '+' | '.' |
|
||||
+---------------+---------------+------------------+
|
||||
|
||||
Il carattere '-' suggerisce che le interruzioni sono disabilitate perché
|
||||
altrimenti verrebbe mostrato il carattere '?'. Una deduzione simile può essere
|
||||
fatta anche per '+'
|
||||
|
||||
I blocchi inutilizzati (ad esempio i mutex) non possono essere fra le cause di
|
||||
un errore.
|
||||
|
||||
Regole dello stato per un blocco singolo
|
||||
----------------------------------------
|
||||
|
||||
Avere un blocco sicuro in interruzioni (*irq-safe*) significa che è sempre stato
|
||||
usato in un contesto d'interruzione, mentre un blocco insicuro in interruzioni
|
||||
(*irq-unsafe*) significa che è sempre stato acquisito con le interruzioni
|
||||
abilitate.
|
||||
|
||||
Una classe softirq insicura è automaticamente insicura anche per hardirq. I
|
||||
seguenti stati sono mutualmente esclusivi: solo una può essere vero quando viene
|
||||
usata una classe di blocco::
|
||||
|
||||
<hardirq-safe> o <hardirq-unsafe>
|
||||
<softirq-safe> o <softirq-unsafe>
|
||||
|
||||
Questo perché se un blocco può essere usato in un contesto di interruzioni
|
||||
(sicuro in interruzioni), allora non può mai essere acquisito con le
|
||||
interruzioni abilitate (insicuro in interruzioni). Altrimenti potrebbe
|
||||
verificarsi uno stallo. Per esempio, questo blocco viene acquisito, ma prima di
|
||||
essere rilasciato il contesto d'esecuzione viene interrotto nuovamente, e quindi
|
||||
si tenterà di acquisirlo nuovamente. Questo porterà ad uno stallo, in
|
||||
particolare uno stallo ricorsivo.
|
||||
|
||||
Il validatore rileva e riporta gli usi di blocchi che violano queste regole per
|
||||
blocchi singoli.
|
||||
|
||||
Regole per le dipendenze di blocchi multipli
|
||||
--------------------------------------------
|
||||
|
||||
La stessa classe di blocco non deve essere acquisita due volte, questo perché
|
||||
potrebbe portare ad uno blocco ricorsivo e dunque ad uno stallo.
|
||||
|
||||
Inoltre, due blocchi non possono essere trattenuti in ordine inverso::
|
||||
|
||||
<L1> -> <L2>
|
||||
<L2> -> <L1>
|
||||
|
||||
perché porterebbe ad uno stallo - chiamato stallo da blocco inverso - in cui si
|
||||
cerca di trattenere i due blocchi in un ciclo in cui entrambe i contesti
|
||||
aspettano per sempre che l'altro termini. Il validatore è in grado di trovare
|
||||
queste dipendenze cicliche di qualsiasi complessità, ovvero nel mezzo ci
|
||||
potrebbero essere altre sequenze di blocchi. Il validatore troverà se questi
|
||||
blocchi possono essere acquisiti circolarmente.
|
||||
|
||||
In aggiunta, le seguenti sequenze di blocco nei contesti indicati non sono
|
||||
permesse, indipendentemente da quale che sia la classe di blocco::
|
||||
|
||||
<hardirq-safe> -> <hardirq-unsafe>
|
||||
<softirq-safe> -> <softirq-unsafe>
|
||||
|
||||
La prima regola deriva dal fatto che un blocco sicuro in interruzioni può essere
|
||||
trattenuto in un contesto d'interruzione che, per definizione, ha la possibilità
|
||||
di interrompere un blocco insicuro in interruzioni; questo porterebbe ad uno
|
||||
stallo da blocco inverso. La seconda, analogamente, ci dice che un blocco sicuro
|
||||
in interruzioni software potrebbe essere trattenuto in un contesto di
|
||||
interruzione software, dunque potrebbe interrompere un blocco insicuro in
|
||||
interruzioni software.
|
||||
|
||||
Le suddette regole vengono applicate per qualsiasi sequenza di blocchi: quando
|
||||
si acquisiscono nuovi blocchi, il validatore verifica se vi è una violazione
|
||||
delle regole fra il nuovo blocco e quelli già trattenuti.
|
||||
|
||||
Quando una classe di blocco cambia stato, applicheremo le seguenti regole:
|
||||
|
||||
- se viene trovato un nuovo blocco sicuro in interruzioni, verificheremo se
|
||||
abbia mai trattenuto dei blocchi insicuri in interruzioni.
|
||||
|
||||
- se viene trovato un nuovo blocco sicuro in interruzioni software,
|
||||
verificheremo se abbia trattenuto dei blocchi insicuri in interruzioni
|
||||
software.
|
||||
|
||||
- se viene trovato un nuovo blocco insicuro in interruzioni, verificheremo se
|
||||
abbia trattenuto dei blocchi sicuri in interruzioni.
|
||||
|
||||
- se viene trovato un nuovo blocco insicuro in interruzioni software,
|
||||
verificheremo se abbia trattenuto dei blocchi sicuri in interruzioni
|
||||
software.
|
||||
|
||||
(Di nuovo, questi controlli vengono fatti perché un contesto d'interruzione
|
||||
potrebbe interrompere l'esecuzione di qualsiasi blocco insicuro portando ad uno
|
||||
stallo; questo anche se lo stallo non si verifica in pratica)
|
||||
|
||||
Eccezione: dipendenze annidate sui dati portano a blocchi annidati
|
||||
------------------------------------------------------------------
|
||||
|
||||
Ci sono alcuni casi in cui il kernel Linux acquisisce più volte la stessa
|
||||
istanza di una classe di blocco. Solitamente, questo succede quando esiste una
|
||||
gerarchia fra oggetti dello stesso tipo. In questi casi viene ereditato
|
||||
implicitamente l'ordine fra i due oggetti (definito dalle proprietà di questa
|
||||
gerarchia), ed il kernel tratterrà i blocchi in questo ordine prefissato per
|
||||
ognuno degli oggetti.
|
||||
|
||||
Un esempio di questa gerarchia di oggetti che producono "blocchi annidati" sono
|
||||
i *block-dev* che rappresentano l'intero disco e quelli che rappresentano una
|
||||
sua partizione; la partizione è una parte del disco intero, e l'ordine dei
|
||||
blocchi sarà corretto fintantoche uno acquisisce il blocco del disco intero e
|
||||
poi quello della partizione. Il validatore non rileva automaticamente questo
|
||||
ordine implicito, perché queste regole di sincronizzazione non sono statiche.
|
||||
|
||||
Per istruire il validatore riguardo a questo uso corretto dei blocchi sono stati
|
||||
introdotte nuove primitive per specificare i "livelli di annidamento". Per
|
||||
esempio, per i blocchi a mutua esclusione dei *block-dev* si avrebbe una
|
||||
chiamata simile a::
|
||||
|
||||
enum bdev_bd_mutex_lock_class
|
||||
{
|
||||
BD_MUTEX_NORMAL,
|
||||
BD_MUTEX_WHOLE,
|
||||
BD_MUTEX_PARTITION
|
||||
};
|
||||
|
||||
mutex_lock_nested(&bdev->bd_contains->bd_mutex, BD_MUTEX_PARTITION);
|
||||
|
||||
In questo caso la sincronizzazione viene fatta su un *block-dev* sapendo che si
|
||||
tratta di una partizione.
|
||||
|
||||
Ai fini della validazione, il validatore lo considererà con una - sotto - classe
|
||||
di blocco separata.
|
||||
|
||||
Nota: Prestate estrema attenzione che la vostra gerarchia sia corretta quando si
|
||||
vogliono usare le primitive _nested(); altrimenti potreste avere sia falsi
|
||||
positivi che falsi negativi.
|
||||
|
||||
Annotazioni
|
||||
-----------
|
||||
|
||||
Si possono utilizzare due costrutti per verificare ed annotare se certi blocchi
|
||||
devono essere trattenuti: lockdep_assert_held*(&lock) e
|
||||
lockdep_*pin_lock(&lock).
|
||||
|
||||
Come suggerito dal nome, la famiglia di macro lockdep_assert_held* asseriscono
|
||||
che un dato blocco in un dato momento deve essere trattenuto (altrimenti, verrà
|
||||
generato un WARN()). Queste vengono usate abbondantemente nel kernel, per
|
||||
esempio in kernel/sched/core.c::
|
||||
|
||||
void update_rq_clock(struct rq *rq)
|
||||
{
|
||||
s64 delta;
|
||||
|
||||
lockdep_assert_held(&rq->lock);
|
||||
[...]
|
||||
}
|
||||
|
||||
dove aver trattenuto rq->lock è necessario per aggiornare in sicurezza il clock
|
||||
rq.
|
||||
|
||||
L'altra famiglia di macro è lockdep_*pin_lock(), che a dire il vero viene usata
|
||||
solo per rq->lock ATM. Se per caso un blocco non viene trattenuto, queste
|
||||
genereranno un WARN(). Questo si rivela particolarmente utile quando si deve
|
||||
verificare la correttezza di codice con *callback*, dove livelli superiori
|
||||
potrebbero assumere che un blocco rimanga trattenuto, ma livelli inferiori
|
||||
potrebbero invece pensare che il blocco possa essere rilasciato e poi
|
||||
riacquisito (involontariamente si apre una sezione critica). lockdep_pin_lock()
|
||||
restituisce 'struct pin_cookie' che viene usato da lockdep_unpin_lock() per
|
||||
verificare che nessuno abbia manomesso il blocco. Per esempio in
|
||||
kernel/sched/sched.h abbiamo::
|
||||
|
||||
static inline void rq_pin_lock(struct rq *rq, struct rq_flags *rf)
|
||||
{
|
||||
rf->cookie = lockdep_pin_lock(&rq->lock);
|
||||
[...]
|
||||
}
|
||||
|
||||
static inline void rq_unpin_lock(struct rq *rq, struct rq_flags *rf)
|
||||
{
|
||||
[...]
|
||||
lockdep_unpin_lock(&rq->lock, rf->cookie);
|
||||
}
|
||||
|
||||
I commenti riguardo alla sincronizzazione possano fornire informazioni utili,
|
||||
tuttavia sono le verifiche in esecuzione effettuate da queste macro ad essere
|
||||
vitali per scovare problemi di sincronizzazione, ed inoltre forniscono lo stesso
|
||||
livello di informazioni quando si ispeziona il codice. Nel dubbio, preferite
|
||||
queste annotazioni!
|
||||
|
||||
Dimostrazione di correttezza al 100%
|
||||
------------------------------------
|
||||
|
||||
Il validatore verifica la proprietà di chiusura in senso matematico. Ovvero, per
|
||||
ogni sequenza di sincronizzazione di un singolo processo che si verifichi almeno
|
||||
una volta nel kernel, il validatore dimostrerà con una certezza del 100% che
|
||||
nessuna combinazione e tempistica di queste sequenze possa causare uno stallo in
|
||||
una qualsiasi classe di blocco. [1]_
|
||||
|
||||
In pratica, per dimostrare l'esistenza di uno stallo non servono complessi
|
||||
scenari di sincronizzazione multi-processore e multi-processo. Il validatore può
|
||||
dimostrare la correttezza basandosi sulla sola sequenza di sincronizzazione
|
||||
apparsa almeno una volta (in qualunque momento, in qualunque processo o
|
||||
contesto). Uno scenario complesso che avrebbe bisogno di 3 processori e una
|
||||
sfortunata presenza di processi, interruzioni, e pessimo tempismo, può essere
|
||||
riprodotto su un sistema a singolo processore.
|
||||
|
||||
Questo riduce drasticamente la complessità del controllo di qualità della
|
||||
sincronizzazione nel kernel: quello che deve essere fatto è di innescare nel
|
||||
kernel quante più possibili "semplici" sequenze di sincronizzazione, almeno una
|
||||
volta, allo scopo di dimostrarne la correttezza. Questo al posto di innescare
|
||||
una verifica per ogni possibile combinazione di sincronizzazione fra processori,
|
||||
e differenti scenari con hardirq e softirq e annidamenti vari (nella pratica,
|
||||
impossibile da fare)
|
||||
|
||||
.. [1]
|
||||
|
||||
assumendo che il validatore sia corretto al 100%, e che nessun altra parte
|
||||
del sistema possa corromperne lo stato. Assumiamo anche che tutti i percorsi
|
||||
MNI/SMM [potrebbero interrompere anche percorsi dove le interruzioni sono
|
||||
disabilitate] sono corretti e non interferiscono con il validatore. Inoltre,
|
||||
assumiamo che un hash a 64-bit sia unico per ogni sequenza di
|
||||
sincronizzazione nel sistema. Infine, la ricorsione dei blocchi non deve
|
||||
essere maggiore di 20.
|
||||
|
||||
Prestazione
|
||||
-----------
|
||||
|
||||
Le regole sopracitate hanno bisogno di una quantità **enorme** di verifiche
|
||||
durante l'esecuzione. Il sistema sarebbe diventato praticamente inutilizzabile
|
||||
per la sua lentezza se le avessimo fatte davvero per ogni blocco trattenuto e
|
||||
per ogni abilitazione delle interruzioni. La complessità della verifica è
|
||||
O(N^2), quindi avremmo dovuto fare decine di migliaia di verifiche per ogni
|
||||
evento, il tutto per poche centinaia di classi.
|
||||
|
||||
Il problema è stato risolto facendo una singola verifica per ogni 'scenario di
|
||||
sincronizzazione' (una sequenza unica di blocchi trattenuti uno dopo l'altro).
|
||||
Per farlo, viene mantenuta una pila dei blocchi trattenuti, e viene calcolato un
|
||||
hash a 64-bit unico per ogni sequenza. Quando la sequenza viene verificata per
|
||||
la prima volta, l'hash viene inserito in una tabella hash. La tabella potrà
|
||||
essere verificata senza bisogno di blocchi. Se la sequenza dovesse ripetersi, la
|
||||
tabella ci dirà che non è necessario verificarla nuovamente.
|
||||
|
||||
Risoluzione dei problemi
|
||||
------------------------
|
||||
|
||||
Il massimo numero di classi di blocco che il validatore può tracciare è:
|
||||
MAX_LOCKDEP_KEYS. Oltrepassare questo limite indurrà lokdep a generare il
|
||||
seguente avviso::
|
||||
|
||||
(DEBUG_LOCKS_WARN_ON(id >= MAX_LOCKDEP_KEYS))
|
||||
|
||||
Di base questo valore è 8191, e un classico sistema da ufficio ha meno di 1000
|
||||
classi, dunque questo avviso è solitamente la conseguenza di un problema di
|
||||
perdita delle classi di blocco o d'inizializzazione dei blocchi. Di seguito una
|
||||
descrizione dei due problemi:
|
||||
|
||||
1. caricare e rimuovere continuamente i moduli mentre il validatore è in
|
||||
esecuzione porterà ad una perdita di classi di blocco. Il problema è che ogni
|
||||
caricamento crea un nuovo insieme di classi di blocco per tutti i blocchi di
|
||||
quel modulo. Tuttavia, la rimozione del modulo non rimuove le vecchie classi
|
||||
(vedi dopo perché non le riusiamo). Dunque, il continuo caricamento e
|
||||
rimozione di un modulo non fa altro che aumentare il contatore di classi fino
|
||||
a raggiungere, eventualmente, il limite.
|
||||
|
||||
2. Usare array con un gran numero di blocchi che non vengono esplicitamente
|
||||
inizializzati. Per esempio, una tabella hash con 8192 *bucket* dove ognuno ha
|
||||
il proprio spinlock_t consumerà 8192 classi di blocco a meno che non vengano
|
||||
esplicitamente inizializzati in esecuzione usando spin_lock_init() invece
|
||||
dell'inizializzazione durante la compilazione con __SPIN_LOCK_UNLOCKED().
|
||||
Sbagliare questa inizializzazione garantisce un esaurimento di classi di
|
||||
blocco. Viceversa, un ciclo che invoca spin_lock_init() su tutti i blocchi li
|
||||
mapperebbe tutti alla stessa classe di blocco.
|
||||
|
||||
La morale della favola è che dovete sempre inizializzare esplicitamente i
|
||||
vostri blocchi.
|
||||
|
||||
Qualcuno potrebbe argomentare che il validatore debba permettere il riuso di
|
||||
classi di blocco. Tuttavia, se siete tentati dall'argomento, prima revisionate
|
||||
il codice e pensate alla modifiche necessarie, e tenendo a mente che le classi
|
||||
di blocco da rimuovere probabilmente sono legate al grafo delle dipendenze. Più
|
||||
facile a dirsi che a farsi.
|
||||
|
||||
Ovviamente, se non esaurite le classi di blocco, la prossima cosa da fare è
|
||||
quella di trovare le classi non funzionanti. Per prima cosa, il seguente comando
|
||||
ritorna il numero di classi attualmente in uso assieme al valore massimo::
|
||||
|
||||
grep "lock-classes" /proc/lockdep_stats
|
||||
|
||||
Questo comando produce il seguente messaggio::
|
||||
|
||||
lock-classes: 748 [max: 8191]
|
||||
|
||||
Se il numero di assegnazioni (748 qui sopra) aumenta continuamente nel tempo,
|
||||
allora c'è probabilmente un problema da qualche parte. Il seguente comando può
|
||||
essere utilizzato per identificare le classi di blocchi problematiche::
|
||||
|
||||
grep "BD" /proc/lockdep
|
||||
|
||||
Eseguite il comando e salvatene l'output, quindi confrontatelo con l'output di
|
||||
un'esecuzione successiva per identificare eventuali problemi. Questo stesso
|
||||
output può anche aiutarti a trovare situazioni in cui l'inizializzazione del
|
||||
blocco è stata omessa.
|
||||
|
||||
Lettura ricorsiva dei blocchi
|
||||
-----------------------------
|
||||
|
||||
Il resto di questo documento vuole dimostrare che certi cicli equivalgono ad una
|
||||
possibilità di stallo.
|
||||
|
||||
Ci sono tre tipi di bloccatori: gli scrittori (bloccatori esclusivi, come
|
||||
spin_lock() o write_lock()), lettori non ricorsivi (bloccatori condivisi, come
|
||||
down_read()), e lettori ricorsivi (bloccatori condivisi ricorsivi, come
|
||||
rcu_read_lock()). D'ora in poi, per questi tipi di bloccatori, useremo la
|
||||
seguente notazione:
|
||||
|
||||
W o E: per gli scrittori (bloccatori esclusivi) (W dall'inglese per
|
||||
*Writer*, ed E per *Exclusive*).
|
||||
|
||||
r: per i lettori non ricorsivi (r dall'inglese per *reader*).
|
||||
|
||||
R: per i lettori ricorsivi (R dall'inglese per *Reader*).
|
||||
|
||||
S: per qualsiasi lettore (non ricorsivi + ricorsivi), dato che entrambe
|
||||
sono bloccatori condivisi (S dall'inglese per *Shared*).
|
||||
|
||||
N: per gli scrittori ed i lettori non ricorsivi, dato che entrambe sono
|
||||
non ricorsivi.
|
||||
|
||||
Ovviamente, N equivale a "r o W" ed S a "r o R".
|
||||
|
||||
Come suggerisce il nome, i lettori ricorsivi sono dei bloccatori a cui è
|
||||
permesso di acquisire la stessa istanza di blocco anche all'interno della
|
||||
sezione critica di un altro lettore. In altre parole, permette di annidare la
|
||||
stessa istanza di blocco nelle sezioni critiche dei lettori.
|
||||
|
||||
Dall'altro canto, lo stesso comportamento indurrebbe un lettore non ricorsivo ad
|
||||
auto infliggersi uno stallo.
|
||||
|
||||
La differenza fra questi due tipi di lettori esiste perché: quelli ricorsivi
|
||||
vengono bloccati solo dal trattenimento di un blocco di scrittura, mentre quelli
|
||||
non ricorsivi possono essere bloccati dall'attesa di un blocco di scrittura.
|
||||
Consideriamo il seguente esempio::
|
||||
|
||||
TASK A: TASK B:
|
||||
|
||||
read_lock(X);
|
||||
write_lock(X);
|
||||
read_lock_2(X);
|
||||
|
||||
L'attività A acquisisce il blocco di lettura X (non importa se di tipo ricorsivo
|
||||
o meno) usando read_lock(). Quando l'attività B tenterà di acquisire il blocco
|
||||
X, si fermerà e rimarrà in attesa che venga rilasciato. Ora se read_lock_2() è
|
||||
un tipo lettore ricorsivo, l'attività A continuerà perché gli scrittori in
|
||||
attesa non possono bloccare lettori ricorsivi, e non avremo alcuno stallo.
|
||||
Tuttavia, se read_lock_2() è un lettore non ricorsivo, allora verrà bloccato
|
||||
dall'attività B e si causerà uno stallo.
|
||||
|
||||
Condizioni bloccanti per lettori/scrittori su uno stesso blocco
|
||||
---------------------------------------------------------------
|
||||
Essenzialmente ci sono quattro condizioni bloccanti:
|
||||
|
||||
1. Uno scrittore blocca un altro scrittore.
|
||||
2. Un lettore blocca uno scrittore.
|
||||
3. Uno scrittore blocca sia i lettori ricorsivi che non ricorsivi.
|
||||
4. Un lettore (ricorsivo o meno) non blocca altri lettori ricorsivi ma potrebbe
|
||||
bloccare quelli non ricorsivi (perché potrebbero esistere degli scrittori in
|
||||
attesa).
|
||||
|
||||
Di seguito le tabella delle condizioni bloccanti, Y (*Yes*) significa che il
|
||||
tipo in riga blocca quello in colonna, mentre N l'opposto.
|
||||
|
||||
+---+---+---+---+
|
||||
| | W | r | R |
|
||||
+---+---+---+---+
|
||||
| W | Y | Y | Y |
|
||||
+---+---+---+---+
|
||||
| r | Y | Y | N |
|
||||
+---+---+---+---+
|
||||
| R | Y | Y | N |
|
||||
+---+---+---+---+
|
||||
|
||||
(W: scrittori, r: lettori non ricorsivi, R: lettori ricorsivi)
|
||||
|
||||
Al contrario dei blocchi per lettori non ricorsivi, quelli ricorsivi vengono
|
||||
trattenuti da chi trattiene il blocco di scrittura piuttosto che da chi ne
|
||||
attende il rilascio. Per esempio::
|
||||
|
||||
TASK A: TASK B:
|
||||
|
||||
read_lock(X);
|
||||
|
||||
write_lock(X);
|
||||
|
||||
read_lock(X);
|
||||
|
||||
non produce uno stallo per i lettori ricorsivi, in quanto il processo B rimane
|
||||
in attesta del blocco X, mentre il secondo read_lock() non ha bisogno di
|
||||
aspettare perché si tratta di un lettore ricorsivo. Tuttavia, se read_lock()
|
||||
fosse un lettore non ricorsivo, questo codice produrrebbe uno stallo.
|
||||
|
||||
Da notare che in funzione dell'operazione di blocco usate per l'acquisizione (in
|
||||
particolare il valore del parametro 'read' in lock_acquire()), un blocco può
|
||||
essere di scrittura (blocco esclusivo), di lettura non ricorsivo (blocco
|
||||
condiviso e non ricorsivo), o di lettura ricorsivo (blocco condiviso e
|
||||
ricorsivo). In altre parole, per un'istanza di blocco esistono tre tipi di
|
||||
acquisizione che dipendono dalla funzione di acquisizione usata: esclusiva, di
|
||||
lettura non ricorsiva, e di lettura ricorsiva.
|
||||
|
||||
In breve, chiamiamo "non ricorsivi" blocchi di scrittura e quelli di lettura non
|
||||
ricorsiva, mentre "ricorsivi" i blocchi di lettura ricorsivi.
|
||||
|
||||
I blocchi ricorsivi non si bloccano a vicenda, mentre quelli non ricorsivi sì
|
||||
(anche in lettura). Un blocco di lettura non ricorsivi può bloccare uno
|
||||
ricorsivo, e viceversa.
|
||||
|
||||
Il seguente esempio mostra uno stallo con blocchi ricorsivi::
|
||||
|
||||
TASK A: TASK B:
|
||||
|
||||
read_lock(X);
|
||||
read_lock(Y);
|
||||
write_lock(Y);
|
||||
write_lock(X);
|
||||
|
||||
Il processo A attende che il processo B esegua read_unlock() so Y, mentre il
|
||||
processo B attende che A esegua read_unlock() su X.
|
||||
|
||||
Tipi di dipendenze e percorsi forti
|
||||
-----------------------------------
|
||||
Le dipendenze fra blocchi tracciano l'ordine con cui una coppia di blocchi viene
|
||||
acquisita, e perché vi sono 3 tipi di bloccatori, allora avremo 9 tipi di
|
||||
dipendenze. Tuttavia, vi mostreremo che 4 sono sufficienti per individuare gli
|
||||
stalli.
|
||||
|
||||
Per ogni dipendenza fra blocchi avremo::
|
||||
|
||||
L1 -> L2
|
||||
|
||||
Questo significa che lockdep ha visto acquisire L1 prima di L2 nello stesso
|
||||
contesto di esecuzione. Per quanto riguarda l'individuazione degli stalli, ci
|
||||
interessa sapere se possiamo rimanere bloccati da L2 mentre L1 viene trattenuto.
|
||||
In altre parole, vogliamo sapere se esiste un bloccatore L3 che viene bloccato
|
||||
da L1 e un L2 che viene bloccato da L3. Dunque, siamo interessati a (1) quello
|
||||
che L1 blocca e (2) quello che blocca L2. Di conseguenza, possiamo combinare
|
||||
lettori ricorsivi e non per L1 (perché bloccano gli stessi tipi) e possiamo
|
||||
combinare scrittori e lettori non ricorsivi per L2 (perché vengono bloccati
|
||||
dagli stessi tipi).
|
||||
|
||||
Con questa semplificazione, possiamo dedurre che ci sono 4 tipi di rami nel
|
||||
grafo delle dipendenze di lockdep:
|
||||
|
||||
1) -(ER)->:
|
||||
dipendenza da scrittore esclusivo a lettore ricorsivo. "X -(ER)-> Y"
|
||||
significa X -> Y, dove X è uno scrittore e Y un lettore ricorsivo.
|
||||
|
||||
2) -(EN)->:
|
||||
dipendenza da scrittore esclusivo a bloccatore non ricorsivo.
|
||||
"X -(EN)->" significa X-> Y, dove X è uno scrittore e Y può essere
|
||||
o uno scrittore o un lettore non ricorsivo.
|
||||
|
||||
3) -(SR)->:
|
||||
dipendenza da lettore condiviso a lettore ricorsivo. "X -(SR)->"
|
||||
significa X -> Y, dove X è un lettore (ricorsivo o meno) e Y è un
|
||||
lettore ricorsivo.
|
||||
|
||||
4) -(SN)->:
|
||||
dipendenza da lettore condiviso a bloccatore non ricorsivo.
|
||||
"X -(SN)-> Y" significa X -> Y , dove X è un lettore (ricorsivo
|
||||
o meno) e Y può essere o uno scrittore o un lettore non ricorsivo.
|
||||
|
||||
Da notare che presi due blocchi, questi potrebbero avere più dipendenza fra di
|
||||
loro. Per esempio::
|
||||
|
||||
TASK A:
|
||||
|
||||
read_lock(X);
|
||||
write_lock(Y);
|
||||
...
|
||||
|
||||
TASK B:
|
||||
|
||||
write_lock(X);
|
||||
write_lock(Y);
|
||||
|
||||
Nel grafo delle dipendenze avremo sia X -(SN)-> Y che X -(EN)-> Y.
|
||||
|
||||
Usiamo -(xN)-> per rappresentare i rami sia per -(EN)-> che -(SN)->, allo stesso
|
||||
modo -(Ex)->, -(xR)-> e -(Sx)->
|
||||
|
||||
Un "percorso" in un grafo è una serie di nodi e degli archi che li congiungono.
|
||||
Definiamo un percorso "forte", come il percorso che non ha archi (dipendenze) di
|
||||
tipo -(xR)-> e -(Sx)->. In altre parole, un percorso "forte" è un percorso da un
|
||||
blocco ad un altro attraverso le varie dipendenze, e se sul percorso abbiamo X
|
||||
-> Y -> Z (dove X, Y, e Z sono blocchi), e da X a Y si ha una dipendenza -(SR)->
|
||||
o -(ER)->, allora fra Y e Z non deve esserci una dipendenza -(SN)-> o -(SR)->.
|
||||
|
||||
Nella prossima sezione vedremo perché definiamo questo percorso "forte".
|
||||
|
||||
Identificazione di stalli da lettura ricorsiva
|
||||
----------------------------------------------
|
||||
Ora vogliamo dimostrare altre due cose:
|
||||
|
||||
Lemma 1:
|
||||
|
||||
Se esiste un percorso chiuso forte (ciclo forte), allora esiste anche una
|
||||
combinazione di sequenze di blocchi che causa uno stallo. In altre parole,
|
||||
l'esistenza di un ciclo forte è sufficiente alla scoperta di uno stallo.
|
||||
|
||||
Lemma 2:
|
||||
|
||||
Se non esiste un percorso chiuso forte (ciclo forte), allora non esiste una
|
||||
combinazione di sequenze di blocchi che causino uno stallo. In altre parole, i
|
||||
cicli forti sono necessari alla rilevazione degli stallo.
|
||||
|
||||
Con questi due lemmi possiamo facilmente affermare che un percorso chiuso forte
|
||||
è sia sufficiente che necessario per avere gli stalli, dunque averli equivale
|
||||
alla possibilità di imbattersi concretamente in uno stallo. Un percorso chiuso
|
||||
forte significa che può causare stalli, per questo lo definiamo "forte", ma ci
|
||||
sono anche cicli di dipendenze che non causeranno stalli.
|
||||
|
||||
Dimostrazione di sufficienza (lemma 1):
|
||||
|
||||
Immaginiamo d'avere un ciclo forte::
|
||||
|
||||
L1 -> L2 ... -> Ln -> L1
|
||||
|
||||
Questo significa che abbiamo le seguenti dipendenze::
|
||||
|
||||
L1 -> L2
|
||||
L2 -> L3
|
||||
...
|
||||
Ln-1 -> Ln
|
||||
Ln -> L1
|
||||
|
||||
Ora possiamo costruire una combinazione di sequenze di blocchi che causano lo
|
||||
stallo.
|
||||
|
||||
Per prima cosa facciamo sì che un processo/processore prenda L1 in L1 -> L2, poi
|
||||
un altro prende L2 in L2 -> L3, e così via. Alla fine, tutti i Lx in Lx -> Lx+1
|
||||
saranno trattenuti da processi/processori diversi.
|
||||
|
||||
Poi visto che abbiamo L1 -> L2, chi trattiene L1 vorrà acquisire L2 in L1 -> L2,
|
||||
ma prima dovrà attendere che venga rilasciato da chi lo trattiene. Questo perché
|
||||
L2 è già trattenuto da un altro processo/processore, ed in più L1 -> L2 e L2 ->
|
||||
L3 non sono -(xR)-> né -(Sx)-> (la definizione di forte). Questo significa che L2
|
||||
in L1 -> L2 non è un bloccatore non ricorsivo (bloccabile da chiunque), e L2 in
|
||||
L2 -> L3 non è uno scrittore (che blocca chiunque).
|
||||
|
||||
In aggiunta, possiamo trarre una simile conclusione per chi sta trattenendo L2:
|
||||
deve aspettare che L3 venga rilasciato, e così via. Ora possiamo dimostrare che
|
||||
chi trattiene Lx deve aspettare che Lx+1 venga rilasciato. Notiamo che Ln+1 è
|
||||
L1, dunque si è creato un ciclo dal quale non possiamo uscire, quindi si ha uno
|
||||
stallo.
|
||||
|
||||
Dimostrazione della necessità (lemma 2):
|
||||
|
||||
Questo lemma equivale a dire che: se siamo in uno scenario di stallo, allora
|
||||
deve esiste un ciclo forte nel grafo delle dipendenze.
|
||||
|
||||
Secondo Wikipedia[1], se c'è uno stallo, allora deve esserci un ciclo di attese,
|
||||
ovvero ci sono N processi/processori dove P1 aspetta un blocco trattenuto da P2,
|
||||
e P2 ne aspetta uno trattenuto da P3, ... e Pn attende che il blocco P1 venga
|
||||
rilasciato. Chiamiamo Lx il blocco che attende Px, quindi P1 aspetta L1 e
|
||||
trattiene Ln. Quindi avremo Ln -> L1 nel grafo delle dipendenze. Similarmente,
|
||||
nel grafo delle dipendenze avremo L1 -> L2, L2 -> L3, ..., Ln-1 -> Ln, il che
|
||||
significa che abbiamo un ciclo::
|
||||
|
||||
Ln -> L1 -> L2 -> ... -> Ln
|
||||
|
||||
, ed ora dimostriamo d'avere un ciclo forte.
|
||||
|
||||
Per un blocco Lx, il processo Px contribuisce alla dipendenza Lx-1 -> Lx e Px+1
|
||||
contribuisce a quella Lx -> Lx+1. Visto che Px aspetta che Px+1 rilasci Lx, sarà
|
||||
impossibile che Lx in Px+1 sia un lettore e che Lx in Px sia un lettore
|
||||
ricorsivo. Questo perché i lettori (ricorsivi o meno) non bloccano lettori
|
||||
ricorsivi. Dunque, Lx-1 -> Lx e Lx -> Lx+1 non possono essere una coppia di
|
||||
-(xR)-> -(Sx)->. Questo è vero per ogni ciclo, dunque, questo è un ciclo forte.
|
||||
|
||||
Riferimenti
|
||||
-----------
|
||||
|
||||
[1]: https://it.wikipedia.org/wiki/Stallo_(informatica)
|
||||
|
||||
[2]: Shibu, K. (2009). Intro To Embedded Systems (1st ed.). Tata McGraw-Hill
|
230
Documentation/translations/it_IT/locking/lockstat.rst
Normal file
230
Documentation/translations/it_IT/locking/lockstat.rst
Normal file
@ -0,0 +1,230 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
.. include:: ../disclaimer-ita.rst
|
||||
|
||||
=======================
|
||||
Statistiche sui blocchi
|
||||
=======================
|
||||
|
||||
Cosa
|
||||
====
|
||||
|
||||
Come suggerisce il nome, fornisce statistiche sui blocchi.
|
||||
|
||||
|
||||
Perché
|
||||
======
|
||||
|
||||
Perché, tanto per fare un esempio, le contese sui blocchi possono influenzare
|
||||
significativamente le prestazioni.
|
||||
|
||||
Come
|
||||
====
|
||||
|
||||
*Lockdep* ha punti di collegamento nelle funzioni di blocco e inoltre
|
||||
mappa le istanze di blocco con le relative classi. Partiamo da questo punto
|
||||
(vedere Documentation/translations/it_IT/locking/lockdep-design.rst).
|
||||
Il grafico sottostante mostra la relazione che intercorre fra le
|
||||
funzioni di blocco e i vari punti di collegamenti che ci sono al loro
|
||||
interno::
|
||||
|
||||
__acquire
|
||||
|
|
||||
lock _____
|
||||
| \
|
||||
| __contended
|
||||
| |
|
||||
| <wait>
|
||||
| _______/
|
||||
|/
|
||||
|
|
||||
__acquired
|
||||
|
|
||||
.
|
||||
<hold>
|
||||
.
|
||||
|
|
||||
__release
|
||||
|
|
||||
unlock
|
||||
|
||||
lock, unlock - le classiche funzioni di blocco
|
||||
__* - i punti di collegamento
|
||||
<> - stati
|
||||
|
||||
Grazie a questi punti di collegamento possiamo fornire le seguenti statistiche:
|
||||
|
||||
con-bounces
|
||||
- numero di contese su un blocco che riguarda dati di un processore
|
||||
|
||||
contentions
|
||||
- numero di acquisizioni di blocchi che hanno dovuto attendere
|
||||
|
||||
wait time
|
||||
min
|
||||
- tempo minimo (diverso da zero) che sia mai stato speso in attesa di
|
||||
un blocco
|
||||
|
||||
max
|
||||
- tempo massimo che sia mai stato speso in attesa di un blocco
|
||||
|
||||
total
|
||||
- tempo totale speso in attesa di un blocco
|
||||
|
||||
avg
|
||||
- tempo medio speso in attesa di un blocco
|
||||
|
||||
acq-bounces
|
||||
- numero di acquisizioni di blocco che riguardavano i dati su un processore
|
||||
|
||||
acquisitions
|
||||
- numero di volte che un blocco è stato ottenuto
|
||||
|
||||
hold time
|
||||
min
|
||||
- tempo minimo (diverso da zero) che sia mai stato speso trattenendo un blocco
|
||||
|
||||
max
|
||||
- tempo massimo che sia mai stato speso trattenendo un blocco
|
||||
|
||||
total
|
||||
- tempo totale di trattenimento di un blocco
|
||||
|
||||
avg
|
||||
- tempo medio di trattenimento di un blocco
|
||||
|
||||
Questi numeri vengono raccolti per classe di blocco, e per ogni stato di
|
||||
lettura/scrittura (quando applicabile).
|
||||
|
||||
Inoltre, questa raccolta di statistiche tiene traccia di 4 punti di contesa
|
||||
per classe di blocco. Un punto di contesa è una chiamata che ha dovuto
|
||||
aspettare l'acquisizione di un blocco.
|
||||
|
||||
Configurazione
|
||||
--------------
|
||||
|
||||
Le statistiche sui blocchi si abilitano usando l'opzione di configurazione
|
||||
CONFIG_LOCK_STAT.
|
||||
|
||||
Uso
|
||||
---
|
||||
|
||||
Abilitare la raccolta di statistiche::
|
||||
|
||||
# echo 1 >/proc/sys/kernel/lock_stat
|
||||
|
||||
Disabilitare la raccolta di statistiche::
|
||||
|
||||
# echo 0 >/proc/sys/kernel/lock_stat
|
||||
|
||||
Per vedere le statistiche correnti sui blocchi::
|
||||
|
||||
( i numeri di riga non fanno parte dell'output del comando, ma sono stati
|
||||
aggiunti ai fini di questa spiegazione )
|
||||
|
||||
# less /proc/lock_stat
|
||||
|
||||
01 lock_stat version 0.4
|
||||
02-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
03 class name con-bounces contentions waittime-min waittime-max waittime-total waittime-avg acq-bounces acquisitions holdtime-min holdtime-max holdtime-total holdtime-avg
|
||||
04-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
05
|
||||
06 &mm->mmap_sem-W: 46 84 0.26 939.10 16371.53 194.90 47291 2922365 0.16 2220301.69 17464026916.32 5975.99
|
||||
07 &mm->mmap_sem-R: 37 100 1.31 299502.61 325629.52 3256.30 212344 34316685 0.10 7744.91 95016910.20 2.77
|
||||
08 ---------------
|
||||
09 &mm->mmap_sem 1 [<ffffffff811502a7>] khugepaged_scan_mm_slot+0x57/0x280
|
||||
10 &mm->mmap_sem 96 [<ffffffff815351c4>] __do_page_fault+0x1d4/0x510
|
||||
11 &mm->mmap_sem 34 [<ffffffff81113d77>] vm_mmap_pgoff+0x87/0xd0
|
||||
12 &mm->mmap_sem 17 [<ffffffff81127e71>] vm_munmap+0x41/0x80
|
||||
13 ---------------
|
||||
14 &mm->mmap_sem 1 [<ffffffff81046fda>] dup_mmap+0x2a/0x3f0
|
||||
15 &mm->mmap_sem 60 [<ffffffff81129e29>] SyS_mprotect+0xe9/0x250
|
||||
16 &mm->mmap_sem 41 [<ffffffff815351c4>] __do_page_fault+0x1d4/0x510
|
||||
17 &mm->mmap_sem 68 [<ffffffff81113d77>] vm_mmap_pgoff+0x87/0xd0
|
||||
18
|
||||
19.............................................................................................................................................................................................................................
|
||||
20
|
||||
21 unix_table_lock: 110 112 0.21 49.24 163.91 1.46 21094 66312 0.12 624.42 31589.81 0.48
|
||||
22 ---------------
|
||||
23 unix_table_lock 45 [<ffffffff8150ad8e>] unix_create1+0x16e/0x1b0
|
||||
24 unix_table_lock 47 [<ffffffff8150b111>] unix_release_sock+0x31/0x250
|
||||
25 unix_table_lock 15 [<ffffffff8150ca37>] unix_find_other+0x117/0x230
|
||||
26 unix_table_lock 5 [<ffffffff8150a09f>] unix_autobind+0x11f/0x1b0
|
||||
27 ---------------
|
||||
28 unix_table_lock 39 [<ffffffff8150b111>] unix_release_sock+0x31/0x250
|
||||
29 unix_table_lock 49 [<ffffffff8150ad8e>] unix_create1+0x16e/0x1b0
|
||||
30 unix_table_lock 20 [<ffffffff8150ca37>] unix_find_other+0x117/0x230
|
||||
31 unix_table_lock 4 [<ffffffff8150a09f>] unix_autobind+0x11f/0x1b0
|
||||
|
||||
Questo estratto mostra le statistiche delle prime due classi di
|
||||
blocco. La riga 01 mostra la versione dell'output - la versione
|
||||
cambierà ogni volta che cambia il formato. Le righe dalla 02 alla 04
|
||||
rappresentano l'intestazione con la descrizione delle colonne. Le
|
||||
statistiche sono mostrate nelle righe dalla 05 alla 18 e dalla 20
|
||||
alla 31. Queste statistiche sono divise in due parti: le statistiche,
|
||||
seguite dai punti di contesa (righe 08 e 13) separati da un divisore.
|
||||
|
||||
Le righe dalla 09 alla 12 mostrano i primi quattro punti di contesa
|
||||
registrati (il codice che tenta di acquisire un blocco) e le righe
|
||||
dalla 14 alla 17 mostrano i primi quattro punti contesi registrati
|
||||
(ovvero codice che ha acquisito un blocco). È possibile che nelle
|
||||
statistiche manchi il valore *max con-bounces*.
|
||||
|
||||
Il primo blocco (righe dalla 05 alla 18) è di tipo lettura/scrittura e quindi
|
||||
mostra due righe prima del divisore. I punti di contesa non corrispondono alla
|
||||
descrizione delle colonne nell'intestazione; essi hanno due colonne: *punti di
|
||||
contesa* e *[<IP>] simboli*. Il secondo gruppo di punti di contesa sono i punti
|
||||
con cui si contende il blocco.
|
||||
|
||||
La parte interna del tempo è espressa in us (microsecondi).
|
||||
|
||||
Quando si ha a che fare con blocchi annidati si potrebbero vedere le
|
||||
sottoclassi di blocco::
|
||||
|
||||
32...........................................................................................................................................................................................................................
|
||||
33
|
||||
34 &rq->lock: 13128 13128 0.43 190.53 103881.26 7.91 97454 3453404 0.00 401.11 13224683.11 3.82
|
||||
35 ---------
|
||||
36 &rq->lock 645 [<ffffffff8103bfc4>] task_rq_lock+0x43/0x75
|
||||
37 &rq->lock 297 [<ffffffff8104ba65>] try_to_wake_up+0x127/0x25a
|
||||
38 &rq->lock 360 [<ffffffff8103c4c5>] select_task_rq_fair+0x1f0/0x74a
|
||||
39 &rq->lock 428 [<ffffffff81045f98>] scheduler_tick+0x46/0x1fb
|
||||
40 ---------
|
||||
41 &rq->lock 77 [<ffffffff8103bfc4>] task_rq_lock+0x43/0x75
|
||||
42 &rq->lock 174 [<ffffffff8104ba65>] try_to_wake_up+0x127/0x25a
|
||||
43 &rq->lock 4715 [<ffffffff8103ed4b>] double_rq_lock+0x42/0x54
|
||||
44 &rq->lock 893 [<ffffffff81340524>] schedule+0x157/0x7b8
|
||||
45
|
||||
46...........................................................................................................................................................................................................................
|
||||
47
|
||||
48 &rq->lock/1: 1526 11488 0.33 388.73 136294.31 11.86 21461 38404 0.00 37.93 109388.53 2.84
|
||||
49 -----------
|
||||
50 &rq->lock/1 11526 [<ffffffff8103ed58>] double_rq_lock+0x4f/0x54
|
||||
51 -----------
|
||||
52 &rq->lock/1 5645 [<ffffffff8103ed4b>] double_rq_lock+0x42/0x54
|
||||
53 &rq->lock/1 1224 [<ffffffff81340524>] schedule+0x157/0x7b8
|
||||
54 &rq->lock/1 4336 [<ffffffff8103ed58>] double_rq_lock+0x4f/0x54
|
||||
55 &rq->lock/1 181 [<ffffffff8104ba65>] try_to_wake_up+0x127/0x25a
|
||||
|
||||
La riga 48 mostra le statistiche per la seconda sottoclasse (/1) della
|
||||
classe *&irq->lock* (le sottoclassi partono da 0); in questo caso,
|
||||
come suggerito dalla riga 50, ``double_rq_lock`` tenta di acquisire un blocco
|
||||
annidato di due spinlock.
|
||||
|
||||
Per vedere i blocco più contesi::
|
||||
|
||||
# grep : /proc/lock_stat | head
|
||||
clockevents_lock: 2926159 2947636 0.15 46882.81 1784540466.34 605.41 3381345 3879161 0.00 2260.97 53178395.68 13.71
|
||||
tick_broadcast_lock: 346460 346717 0.18 2257.43 39364622.71 113.54 3642919 4242696 0.00 2263.79 49173646.60 11.59
|
||||
&mapping->i_mmap_mutex: 203896 203899 3.36 645530.05 31767507988.39 155800.21 3361776 8893984 0.17 2254.15 14110121.02 1.59
|
||||
&rq->lock: 135014 136909 0.18 606.09 842160.68 6.15 1540728 10436146 0.00 728.72 17606683.41 1.69
|
||||
&(&zone->lru_lock)->rlock: 93000 94934 0.16 59.18 188253.78 1.98 1199912 3809894 0.15 391.40 3559518.81 0.93
|
||||
tasklist_lock-W: 40667 41130 0.23 1189.42 428980.51 10.43 270278 510106 0.16 653.51 3939674.91 7.72
|
||||
tasklist_lock-R: 21298 21305 0.20 1310.05 215511.12 10.12 186204 241258 0.14 1162.33 1179779.23 4.89
|
||||
rcu_node_1: 47656 49022 0.16 635.41 193616.41 3.95 844888 1865423 0.00 764.26 1656226.96 0.89
|
||||
&(&dentry->d_lockref.lock)->rlock: 39791 40179 0.15 1302.08 88851.96 2.21 2790851 12527025 0.10 1910.75 3379714.27 0.27
|
||||
rcu_node_0: 29203 30064 0.16 786.55 1555573.00 51.74 88963 244254 0.00 398.87 428872.51 1.76
|
||||
|
||||
Per cancellare le statistiche::
|
||||
|
||||
# echo 0 > /proc/lock_stat
|
181
Documentation/translations/it_IT/locking/locktorture.rst
Normal file
181
Documentation/translations/it_IT/locking/locktorture.rst
Normal file
@ -0,0 +1,181 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
.. include:: ../disclaimer-ita.rst
|
||||
|
||||
============================================
|
||||
Funzionamento del test *Kernel Lock Torture*
|
||||
============================================
|
||||
|
||||
CONFIG_LOCK_TORTURE_TEST
|
||||
========================
|
||||
|
||||
L'opzione di configurazione CONFIG_LOCK_TORTURE_TEST fornisce un
|
||||
modulo kernel che esegue delle verifiche che *torturano* le primitive di
|
||||
sincronizzazione del kernel. Se dovesse servire, il modulo kernel,
|
||||
'locktorture', può essere generato successivamente su un kernel che
|
||||
volete verificare. Periodicamente le verifiche stampano messaggi tramite
|
||||
``printk()`` e che quindi possono essere letti tramite ``dmesg`` (magari
|
||||
filtrate l'output con ``grep "torture"``). La verifica inizia quando
|
||||
il modulo viene caricato e termina quando viene rimosso. Questo
|
||||
programma si basa sulle modalità di verifica di RCU tramite rcutorture.
|
||||
|
||||
Questa verifica consiste nella creazione di un certo numero di thread
|
||||
del kernel che acquisiscono un blocco e lo trattengono per una certa
|
||||
quantità di tempo così da simulare diversi comportamenti nelle sezioni
|
||||
critiche. La quantità di contese su un blocco può essere simulata
|
||||
allargando la sezione critica e/o creando più thread.
|
||||
|
||||
|
||||
Parametri del modulo
|
||||
====================
|
||||
|
||||
Questo modulo ha i seguenti parametri:
|
||||
|
||||
|
||||
Specifici di locktorture
|
||||
------------------------
|
||||
|
||||
nwriters_stress
|
||||
Numero di thread del kernel che stresseranno l'acquisizione
|
||||
esclusiva dei blocchi (scrittori). Il valore di base è il
|
||||
doppio del numero di processori attivi presenti.
|
||||
|
||||
nreaders_stress
|
||||
Numero di thread del kernel che stresseranno l'acquisizione
|
||||
condivisa dei blocchi (lettori). Il valore di base è lo stesso
|
||||
di nwriters_stress. Se l'utente non ha specificato
|
||||
nwriters_stress, allora entrambe i valori corrisponderanno
|
||||
al numero di processori attivi presenti.
|
||||
|
||||
torture_type
|
||||
Tipo di blocco da verificare. Di base, solo gli spinlock
|
||||
verranno verificati. Questo modulo può verificare anche
|
||||
i seguenti tipi di blocchi:
|
||||
|
||||
- "lock_busted":
|
||||
Simula un'incorretta implementazione del
|
||||
blocco.
|
||||
|
||||
- "spin_lock":
|
||||
coppie di spin_lock() e spin_unlock().
|
||||
|
||||
- "spin_lock_irq":
|
||||
coppie di spin_lock_irq() e spin_unlock_irq().
|
||||
|
||||
- "rw_lock":
|
||||
coppie di rwlock read/write lock() e unlock().
|
||||
|
||||
- "rw_lock_irq":
|
||||
copie di rwlock read/write lock_irq() e
|
||||
unlock_irq().
|
||||
|
||||
- "mutex_lock":
|
||||
coppie di mutex_lock() e mutex_unlock().
|
||||
|
||||
- "rtmutex_lock":
|
||||
coppie di rtmutex_lock() e rtmutex_unlock().
|
||||
Il kernel deve avere CONFIG_RT_MUTEXES=y.
|
||||
|
||||
- "rwsem_lock":
|
||||
coppie di semafori read/write down() e up().
|
||||
|
||||
|
||||
Generici dell'ambiente di sviluppo 'torture' (RCU + locking)
|
||||
------------------------------------------------------------
|
||||
|
||||
shutdown_secs
|
||||
Numero di secondi prima che la verifica termini e il sistema
|
||||
venga spento. Il valore di base è zero, il che disabilita
|
||||
la possibilità di terminare e spegnere. Questa funzionalità
|
||||
può essere utile per verifiche automatizzate.
|
||||
|
||||
onoff_interval
|
||||
Numero di secondi fra ogni tentativo di esecuzione di
|
||||
un'operazione casuale di CPU-hotplug. Di base è zero, il
|
||||
che disabilita la funzionalità di CPU-hotplug. Nei kernel
|
||||
con CONFIG_HOTPLUG_CPU=n, locktorture si rifiuterà, senza
|
||||
dirlo, di effettuare una qualsiasi operazione di
|
||||
CPU-hotplug indipendentemente dal valore specificato in
|
||||
onoff_interval.
|
||||
|
||||
onoff_holdoff
|
||||
Numero di secondi da aspettare prima di iniziare le
|
||||
operazioni di CPU-hotplug. Normalmente questo verrebbe
|
||||
usato solamente quando locktorture è compilato come parte
|
||||
integrante del kernel ed eseguito automaticamente all'avvio,
|
||||
in questo caso è utile perché permette di non confondere
|
||||
l'avvio con i processori che vanno e vengono. Questo
|
||||
parametro è utile sono se CONFIG_HOTPLUG_CPU è abilitato.
|
||||
|
||||
stat_interval
|
||||
Numero di secondi fra una stampa (printk()) delle
|
||||
statistiche e l'altra. Di base, locktorture riporta le
|
||||
statistiche ogni 60 secondi. Impostando l'intervallo a 0
|
||||
ha l'effetto di stampare le statistiche -solo- quando il
|
||||
modulo viene rimosso.
|
||||
|
||||
stutter
|
||||
Durata della verifica prima di effettuare una pausa di
|
||||
eguale durata. Di base "stutter=5", quindi si eseguono
|
||||
verifiche e pause di (circa) cinque secondi.
|
||||
L'impostazione di "stutter=0" fa si che la verifica
|
||||
venga eseguita continuamente senza fermarsi.
|
||||
|
||||
shuffle_interval
|
||||
Il numero di secondi per cui un thread debba mantenere
|
||||
l'affinità con un sottoinsieme di processori, di base è
|
||||
3 secondi. Viene usato assieme a test_no_idle_hz.
|
||||
|
||||
verbose
|
||||
Abilita le stampe di debug, via printk(). Di base è
|
||||
abilitato. Queste informazioni aggiuntive sono per la
|
||||
maggior parte relative ad errori di alto livello e resoconti
|
||||
da parte dell'struttura 'torture'.
|
||||
|
||||
|
||||
Statistiche
|
||||
===========
|
||||
|
||||
Le statistiche vengono stampate secondo il seguente formato::
|
||||
|
||||
spin_lock-torture: Writes: Total: 93746064 Max/Min: 0/0 Fail: 0
|
||||
(A) (B) (C) (D) (E)
|
||||
|
||||
(A): tipo di lock sotto verifica -- parametro torture_type.
|
||||
|
||||
(B): Numero di acquisizione del blocco in scrittura. Se si ha a che fare
|
||||
con una primitiva di lettura/scrittura apparirà di seguito anche una
|
||||
seconda voce "Reads"
|
||||
|
||||
(C): Numero di volte che il blocco è stato acquisito
|
||||
|
||||
(D): Numero minimo e massimo di volte che un thread ha fallito
|
||||
nell'acquisire il blocco
|
||||
|
||||
(E): valori true/false nel caso di errori durante l'acquisizione del blocco.
|
||||
Questo dovrebbe dare un riscontro positivo -solo- se c'è un baco
|
||||
nell'implementazione delle primitive di sincronizzazione. Altrimenti un
|
||||
blocco non dovrebbe mai fallire (per esempio, spin_lock()).
|
||||
Ovviamente lo stesso si applica per (C). Un semplice esempio è il tipo
|
||||
"lock_busted".
|
||||
|
||||
Uso
|
||||
===
|
||||
|
||||
Il seguente script può essere utilizzato per verificare i blocchi::
|
||||
|
||||
#!/bin/sh
|
||||
|
||||
modprobe locktorture
|
||||
sleep 3600
|
||||
rmmod locktorture
|
||||
dmesg | grep torture:
|
||||
|
||||
L'output può essere manualmente ispezionato cercando il marcatore d'errore
|
||||
"!!!". Ovviamente potreste voler creare degli script più elaborati che
|
||||
verificano automaticamente la presenza di errori. Il comando "rmmod" forza la
|
||||
stampa (usando printk()) di "SUCCESS", "FAILURE", oppure "RCU_HOTPLUG". I primi
|
||||
due si piegano da soli, mentre l'ultimo indica che non stati trovati problemi di
|
||||
sincronizzazione, tuttavia ne sono stati trovati in CPU-hotplug.
|
||||
|
||||
Consultate anche: Documentation/translations/it_IT/RCU/torture.rst
|
547
Documentation/translations/it_IT/locking/locktypes.rst
Normal file
547
Documentation/translations/it_IT/locking/locktypes.rst
Normal file
@ -0,0 +1,547 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
.. include:: ../disclaimer-ita.rst
|
||||
|
||||
.. _it_kernel_hacking_locktypes:
|
||||
|
||||
========================================
|
||||
Tipologie di blocco e le loro istruzioni
|
||||
========================================
|
||||
|
||||
Introduzione
|
||||
============
|
||||
|
||||
Il kernel fornisce un certo numero di primitive di blocco che possiamo dividere
|
||||
in tre categorie:
|
||||
|
||||
- blocchi ad attesa con sospensione
|
||||
- blocchi locali per CPU
|
||||
- blocchi ad attesa attiva
|
||||
|
||||
Questo documento descrive questi tre tipi e fornisce istruzioni su come
|
||||
annidarli, ed usarli su kernel PREEMPT_RT.
|
||||
|
||||
Categorie di blocchi
|
||||
====================
|
||||
|
||||
Blocchi ad attesa con sospensione
|
||||
---------------------------------
|
||||
|
||||
I blocchi ad attesa con sospensione possono essere acquisiti solo in un contesti
|
||||
dov'è possibile la prelazione.
|
||||
|
||||
Diverse implementazioni permettono di usare try_lock() anche in altri contesti,
|
||||
nonostante ciò è bene considerare anche la sicurezza dei corrispondenti
|
||||
unlock(). Inoltre, vanno prese in considerazione anche le varianti di *debug*
|
||||
di queste primitive. Insomma, non usate i blocchi ad attesa con sospensioni in
|
||||
altri contesti a meno che proprio non vi siano alternative.
|
||||
|
||||
In questa categoria troviamo:
|
||||
|
||||
- mutex
|
||||
- rt_mutex
|
||||
- semaphore
|
||||
- rw_semaphore
|
||||
- ww_mutex
|
||||
- percpu_rw_semaphore
|
||||
|
||||
Nei kernel con PREEMPT_RT, i seguenti blocchi sono convertiti in blocchi ad
|
||||
attesa con sospensione:
|
||||
|
||||
- local_lock
|
||||
- spinlock_t
|
||||
- rwlock_t
|
||||
|
||||
Blocchi locali per CPU
|
||||
----------------------
|
||||
|
||||
- local_lock
|
||||
|
||||
Su kernel non-PREEMPT_RT, le funzioni local_lock gestiscono le primitive di
|
||||
disabilitazione di prelazione ed interruzioni. Al contrario di altri meccanismi,
|
||||
la disabilitazione della prelazione o delle interruzioni sono puri meccanismi
|
||||
per il controllo della concorrenza su una CPU e quindi non sono adatti per la
|
||||
gestione della concorrenza inter-CPU.
|
||||
|
||||
Blocchi ad attesa attiva
|
||||
------------------------
|
||||
|
||||
- raw_spinlcok_t
|
||||
- bit spinlocks
|
||||
|
||||
Nei kernel non-PREEMPT_RT, i seguenti blocchi sono ad attesa attiva:
|
||||
|
||||
- spinlock_t
|
||||
- rwlock_t
|
||||
|
||||
Implicitamente, i blocchi ad attesa attiva disabilitano la prelazione e le
|
||||
funzioni lock/unlock hanno anche dei suffissi per gestire il livello di
|
||||
protezione:
|
||||
|
||||
=================== =========================================================================
|
||||
_bh() disabilita / abilita *bottom halves* (interruzioni software)
|
||||
_irq() disabilita / abilita le interruzioni
|
||||
_irqsave/restore() salva e disabilita le interruzioni / ripristina ed attiva le interruzioni
|
||||
=================== =========================================================================
|
||||
|
||||
Semantica del proprietario
|
||||
==========================
|
||||
|
||||
Eccetto i semafori, i sopracitati tipi di blocchi hanno tutti una semantica
|
||||
molto stringente riguardo al proprietario di un blocco:
|
||||
|
||||
Il contesto (attività) che ha acquisito il blocco deve rilasciarlo
|
||||
|
||||
I semafori rw_semaphores hanno un'interfaccia speciale che permette anche ai non
|
||||
proprietari del blocco di rilasciarlo per i lettori.
|
||||
|
||||
rtmutex
|
||||
=======
|
||||
|
||||
I blocchi a mutua esclusione RT (*rtmutex*) sono un sistema a mutua esclusione
|
||||
con supporto all'ereditarietà della priorità (PI).
|
||||
|
||||
Questo meccanismo ha delle limitazioni sui kernel non-PREEMPT_RT dovuti alla
|
||||
prelazione e alle sezioni con interruzioni disabilitate.
|
||||
|
||||
Chiaramente, questo meccanismo non può avvalersi della prelazione su una sezione
|
||||
dove la prelazione o le interruzioni sono disabilitate; anche sui kernel
|
||||
PREEMPT_RT. Tuttavia, i kernel PREEMPT_RT eseguono la maggior parte delle
|
||||
sezioni in contesti dov'è possibile la prelazione, specialmente in contesti
|
||||
d'interruzione (anche software). Questa conversione permette a spinlock_t e
|
||||
rwlock_t di essere implementati usando rtmutex.
|
||||
|
||||
semaphore
|
||||
=========
|
||||
|
||||
La primitiva semaphore implementa un semaforo con contatore.
|
||||
|
||||
I semafori vengono spesso utilizzati per la serializzazione e l'attesa, ma per
|
||||
nuovi casi d'uso si dovrebbero usare meccanismi diversi, come mutex e
|
||||
completion.
|
||||
|
||||
semaphore e PREEMPT_RT
|
||||
----------------------
|
||||
|
||||
I kernel PREEMPT_RT non cambiano l'implementazione di semaphore perché non hanno
|
||||
un concetto di proprietario, dunque impediscono a PREEMPT_RT d'avere
|
||||
l'ereditarietà della priorità sui semafori. Un proprietario sconosciuto non può
|
||||
ottenere una priorità superiore. Di consequenza, bloccarsi sui semafori porta
|
||||
all'inversione di priorità.
|
||||
|
||||
|
||||
rw_semaphore
|
||||
============
|
||||
|
||||
Il blocco rw_semaphore è un meccanismo che permette più lettori ma un solo scrittore.
|
||||
|
||||
Sui kernel non-PREEMPT_RT l'implementazione è imparziale, quindi previene
|
||||
l'inedia dei processi scrittori.
|
||||
|
||||
Questi blocchi hanno una semantica molto stringente riguardo il proprietario, ma
|
||||
offre anche interfacce speciali che permettono ai processi non proprietari di
|
||||
rilasciare un processo lettore. Queste interfacce funzionano indipendentemente
|
||||
dalla configurazione del kernel.
|
||||
|
||||
rw_semaphore e PREEMPT_RT
|
||||
-------------------------
|
||||
|
||||
I kernel PREEMPT_RT sostituiscono i rw_semaphore con un'implementazione basata
|
||||
su rt_mutex, e questo ne modifica l'imparzialità:
|
||||
|
||||
Dato che uno scrittore rw_semaphore non può assicurare la propria priorità ai
|
||||
suoi lettori, un lettore con priorità più bassa che ha subito la prelazione
|
||||
continuerà a trattenere il blocco, quindi porta all'inedia anche gli scrittori
|
||||
con priorità più alta. Per contro, dato che i lettori possono garantire la
|
||||
propria priorità agli scrittori, uno scrittore a bassa priorità che subisce la
|
||||
prelazione vedrà la propria priorità alzata finché non rilascerà il blocco, e
|
||||
questo preverrà l'inedia dei processi lettori a causa di uno scrittore.
|
||||
|
||||
|
||||
local_lock
|
||||
==========
|
||||
|
||||
I local_lock forniscono nomi agli ambiti di visibilità delle sezioni critiche
|
||||
protette tramite la disattivazione della prelazione o delle interruzioni.
|
||||
|
||||
Sui kernel non-PREEMPT_RT le operazioni local_lock si traducono
|
||||
nell'abilitazione o disabilitazione della prelazione o le interruzioni.
|
||||
|
||||
=============================== ======================
|
||||
local_lock(&llock) preempt_disable()
|
||||
local_unlock(&llock) preempt_enable()
|
||||
local_lock_irq(&llock) local_irq_disable()
|
||||
local_unlock_irq(&llock) local_irq_enable()
|
||||
local_lock_irqsave(&llock) local_irq_save()
|
||||
local_unlock_irqrestore(&llock) local_irq_restore()
|
||||
=============================== ======================
|
||||
|
||||
Gli ambiti di visibilità con nome hanno due vantaggi rispetto alle primitive di
|
||||
base:
|
||||
|
||||
- Il nome del blocco permette di fare un'analisi statica, ed è anche chiaro su
|
||||
cosa si applichi la protezione cosa che invece non si può fare con le
|
||||
classiche primitive in quanto sono opache e senza alcun ambito di
|
||||
visibilità.
|
||||
|
||||
- Se viene abilitato lockdep, allora local_lock ottiene un lockmap che
|
||||
permette di verificare la bontà della protezione. Per esempio, questo può
|
||||
identificare i casi dove una funzione usa preempt_disable() come meccanismo
|
||||
di protezione in un contesto d'interruzione (anche software). A parte
|
||||
questo, lockdep_assert_held(&llock) funziona come tutte le altre primitive
|
||||
di sincronizzazione.
|
||||
|
||||
local_lock e PREEMPT_RT
|
||||
-------------------------
|
||||
|
||||
I kernel PREEMPT_RT sostituiscono local_lock con uno spinlock_t per CPU, quindi
|
||||
ne cambia la semantica:
|
||||
|
||||
- Tutte le modifiche a spinlock_t si applicano anche a local_lock
|
||||
|
||||
L'uso di local_lock
|
||||
-------------------
|
||||
|
||||
I local_lock dovrebbero essere usati su kernel non-PREEMPT_RT quando la
|
||||
disabilitazione della prelazione o delle interruzioni è il modo più adeguato per
|
||||
gestire l'accesso concorrente a strutture dati per CPU.
|
||||
|
||||
Questo meccanismo non è adatto alla protezione da prelazione o interruzione su
|
||||
kernel PREEMPT_RT dato che verrà convertito in spinlock_t.
|
||||
|
||||
|
||||
raw_spinlock_t e spinlock_t
|
||||
===========================
|
||||
|
||||
raw_spinlock_t
|
||||
--------------
|
||||
|
||||
I blocco raw_spinlock_t è un blocco ad attesa attiva su tutti i tipi di kernel,
|
||||
incluso quello PREEMPT_RT. Usate raw_spinlock_t solo in sezioni critiche nel
|
||||
cuore del codice, nella gestione delle interruzioni di basso livello, e in posti
|
||||
dove è necessario disabilitare la prelazione o le interruzioni. Per esempio, per
|
||||
accedere in modo sicuro lo stato dell'hardware. A volte, i raw_spinlock_t
|
||||
possono essere usati quando la sezione critica è minuscola, per evitare gli
|
||||
eccessi di un rtmutex.
|
||||
|
||||
spinlock_t
|
||||
----------
|
||||
|
||||
Il significato di spinlock_t cambia in base allo stato di PREEMPT_RT.
|
||||
|
||||
Sui kernel non-PREEMPT_RT, spinlock_t si traduce in un raw_spinlock_t ed ha
|
||||
esattamente lo stesso significato.
|
||||
|
||||
spinlock_t e PREEMPT_RT
|
||||
-----------------------
|
||||
|
||||
Sui kernel PREEMPT_RT, spinlock_t ha un'implementazione dedicata che si basa
|
||||
sull'uso di rt_mutex. Questo ne modifica il significato:
|
||||
|
||||
- La prelazione non viene disabilitata.
|
||||
|
||||
- I suffissi relativi alla interruzioni (_irq, _irqsave / _irqrestore) per le
|
||||
operazioni spin_lock / spin_unlock non hanno alcun effetto sullo stato delle
|
||||
interruzioni della CPU.
|
||||
|
||||
- I suffissi relativi alle interruzioni software (_bh()) disabilitano i
|
||||
relativi gestori d'interruzione.
|
||||
|
||||
I kernel non-PREEMPT_RT disabilitano la prelazione per ottenere lo stesso effetto.
|
||||
|
||||
I kernel PREEMPT_RT usano un blocco per CPU per la serializzazione, il che
|
||||
permette di tenere attiva la prelazione. Il blocco disabilita i gestori
|
||||
d'interruzione software e previene la rientranza vista la prelazione attiva.
|
||||
|
||||
A parte quanto appena discusso, i kernel PREEMPT_RT preservano il significato
|
||||
di tutti gli altri aspetti di spinlock_t:
|
||||
|
||||
- Le attività che trattengono un blocco spinlock_t non migrano su altri
|
||||
processori. Disabilitando la prelazione, i kernel non-PREEMPT_RT evitano la
|
||||
migrazione. Invece, i kernel PREEMPT_RT disabilitano la migrazione per
|
||||
assicurarsi che i puntatori a variabili per CPU rimangano validi anche
|
||||
quando un'attività subisce la prelazione.
|
||||
|
||||
- Lo stato di un'attività si mantiene durante le acquisizioni del blocco al
|
||||
fine di garantire che le regole basate sullo stato delle attività si possano
|
||||
applicare a tutte le configurazioni del kernel. I kernel non-PREEMPT_RT
|
||||
lasciano lo stato immutato. Tuttavia, la funzionalità PREEMPT_RT deve
|
||||
cambiare lo stato se l'attività si blocca durante l'acquisizione. Dunque,
|
||||
salva lo stato attuale prima di bloccarsi ed il rispettivo risveglio lo
|
||||
ripristinerà come nell'esempio seguente::
|
||||
|
||||
task->state = TASK_INTERRUPTIBLE
|
||||
lock()
|
||||
block()
|
||||
task->saved_state = task->state
|
||||
task->state = TASK_UNINTERRUPTIBLE
|
||||
schedule()
|
||||
lock wakeup
|
||||
task->state = task->saved_state
|
||||
|
||||
Altri tipi di risvegli avrebbero impostato direttamente lo stato a RUNNING,
|
||||
ma in questo caso non avrebbe funzionato perché l'attività deve rimanere
|
||||
bloccata fintanto che il blocco viene trattenuto. Quindi, lo stato salvato
|
||||
viene messo a RUNNING quando il risveglio di un non-blocco cerca di
|
||||
risvegliare un'attività bloccata in attesa del rilascio di uno spinlock. Poi,
|
||||
quando viene completata l'acquisizione del blocco, il suo risveglio
|
||||
ripristinerà lo stato salvato, in questo caso a RUNNING::
|
||||
|
||||
task->state = TASK_INTERRUPTIBLE
|
||||
lock()
|
||||
block()
|
||||
task->saved_state = task->state
|
||||
task->state = TASK_UNINTERRUPTIBLE
|
||||
schedule()
|
||||
non lock wakeup
|
||||
task->saved_state = TASK_RUNNING
|
||||
|
||||
lock wakeup
|
||||
task->state = task->saved_state
|
||||
|
||||
Questo garantisce che il vero risveglio non venga perso.
|
||||
|
||||
rwlock_t
|
||||
========
|
||||
|
||||
Il blocco rwlock_t è un meccanismo che permette più lettori ma un solo scrittore.
|
||||
|
||||
Sui kernel non-PREEMPT_RT questo è un blocco ad attesa e per i suoi suffissi si
|
||||
applicano le stesse regole per spinlock_t. La sua implementazione è imparziale,
|
||||
quindi previene l'inedia dei processi scrittori.
|
||||
|
||||
rwlock_t e PREEMPT_RT
|
||||
---------------------
|
||||
|
||||
Sui kernel PREEMPT_RT rwlock_t ha un'implementazione dedicata che si basa
|
||||
sull'uso di rt_mutex. Questo ne modifica il significato:
|
||||
|
||||
- Tutte le modifiche fatte a spinlock_t si applicano anche a rwlock_t.
|
||||
|
||||
- Dato che uno scrittore rw_semaphore non può assicurare la propria priorità ai
|
||||
suoi lettori, un lettore con priorità più bassa che ha subito la prelazione
|
||||
continuerà a trattenere il blocco, quindi porta all'inedia anche gli
|
||||
scrittori con priorità più alta. Per contro, dato che i lettori possono
|
||||
garantire la propria priorità agli scrittori, uno scrittore a bassa priorità
|
||||
che subisce la prelazione vedrà la propria priorità alzata finché non
|
||||
rilascerà il blocco, e questo preverrà l'inedia dei processi lettori a causa
|
||||
di uno scrittore.
|
||||
|
||||
|
||||
Precisazioni su PREEMPT_RT
|
||||
==========================
|
||||
|
||||
local_lock su RT
|
||||
----------------
|
||||
|
||||
Sui kernel PREEMPT_RT Ci sono alcune implicazioni dovute alla conversione di
|
||||
local_lock in un spinlock_t. Per esempio, su un kernel non-PREEMPT_RT il
|
||||
seguente codice funzionerà come ci si aspetta::
|
||||
|
||||
local_lock_irq(&local_lock);
|
||||
raw_spin_lock(&lock);
|
||||
|
||||
ed è equivalente a::
|
||||
|
||||
raw_spin_lock_irq(&lock);
|
||||
|
||||
Ma su un kernel PREEMPT_RT questo codice non funzionerà perché local_lock_irq()
|
||||
si traduce in uno spinlock_t per CPU che non disabilita né le interruzioni né la
|
||||
prelazione. Il seguente codice funzionerà su entrambe i kernel con o senza
|
||||
PREEMPT_RT::
|
||||
|
||||
local_lock_irq(&local_lock);
|
||||
spin_lock(&lock);
|
||||
|
||||
Un altro dettaglio da tenere a mente con local_lock è che ognuno di loro ha un
|
||||
ambito di protezione ben preciso. Dunque, la seguente sostituzione è errate::
|
||||
|
||||
|
||||
func1()
|
||||
{
|
||||
local_irq_save(flags); -> local_lock_irqsave(&local_lock_1, flags);
|
||||
func3();
|
||||
local_irq_restore(flags); -> local_unlock_irqrestore(&local_lock_1, flags);
|
||||
}
|
||||
|
||||
func2()
|
||||
{
|
||||
local_irq_save(flags); -> local_lock_irqsave(&local_lock_2, flags);
|
||||
func3();
|
||||
local_irq_restore(flags); -> local_unlock_irqrestore(&local_lock_2, flags);
|
||||
}
|
||||
|
||||
func3()
|
||||
{
|
||||
lockdep_assert_irqs_disabled();
|
||||
access_protected_data();
|
||||
}
|
||||
|
||||
Questo funziona correttamente su un kernel non-PREEMPT_RT, ma su un kernel
|
||||
PREEMPT_RT local_lock_1 e local_lock_2 sono distinti e non possono serializzare
|
||||
i chiamanti di func3(). L'*assert* di lockdep verrà attivato su un kernel
|
||||
PREEMPT_RT perché local_lock_irqsave() non disabilita le interruzione a casa
|
||||
della specifica semantica di spinlock_t in PREEMPT_RT. La corretta sostituzione
|
||||
è::
|
||||
|
||||
func1()
|
||||
{
|
||||
local_irq_save(flags); -> local_lock_irqsave(&local_lock, flags);
|
||||
func3();
|
||||
local_irq_restore(flags); -> local_unlock_irqrestore(&local_lock, flags);
|
||||
}
|
||||
|
||||
func2()
|
||||
{
|
||||
local_irq_save(flags); -> local_lock_irqsave(&local_lock, flags);
|
||||
func3();
|
||||
local_irq_restore(flags); -> local_unlock_irqrestore(&local_lock, flags);
|
||||
}
|
||||
|
||||
func3()
|
||||
{
|
||||
lockdep_assert_held(&local_lock);
|
||||
access_protected_data();
|
||||
}
|
||||
|
||||
spinlock_t e rwlock_t
|
||||
---------------------
|
||||
|
||||
Ci sono alcune conseguenze di cui tener conto dal cambiamento di semantica di
|
||||
spinlock_t e rwlock_t sui kernel PREEMPT_RT. Per esempio, sui kernel non
|
||||
PREEMPT_RT il seguente codice funziona come ci si aspetta::
|
||||
|
||||
local_irq_disable();
|
||||
spin_lock(&lock);
|
||||
|
||||
ed è equivalente a::
|
||||
|
||||
spin_lock_irq(&lock);
|
||||
|
||||
Lo stesso vale per rwlock_t e le varianti con _irqsave().
|
||||
|
||||
Sui kernel PREEMPT_RT questo codice non funzionerà perché gli rtmutex richiedono
|
||||
un contesto con la possibilità di prelazione. Al suo posto, usate
|
||||
spin_lock_irq() o spin_lock_irqsave() e le loro controparti per il rilascio. I
|
||||
kernel PREEMPT_RT offrono un meccanismo local_lock per i casi in cui la
|
||||
disabilitazione delle interruzioni ed acquisizione di un blocco devono rimanere
|
||||
separati. Acquisire un local_lock àncora un processo ad una CPU permettendo cose
|
||||
come un'acquisizione di un blocco con interruzioni disabilitate per singola CPU.
|
||||
|
||||
Il tipico scenario è quando si vuole proteggere una variabile di processore nel
|
||||
contesto di un thread::
|
||||
|
||||
|
||||
struct foo *p = get_cpu_ptr(&var1);
|
||||
|
||||
spin_lock(&p->lock);
|
||||
p->count += this_cpu_read(var2);
|
||||
|
||||
Questo codice è corretto su un kernel non-PREEMPT_RT, ma non lo è su un
|
||||
PREEMPT_RT. La modifica della semantica di spinlock_t su PREEMPT_RT non permette
|
||||
di acquisire p->lock perché, implicitamente, get_cpu_ptr() disabilita la
|
||||
prelazione. La seguente sostituzione funzionerà su entrambe i kernel::
|
||||
|
||||
struct foo *p;
|
||||
|
||||
migrate_disable();
|
||||
p = this_cpu_ptr(&var1);
|
||||
spin_lock(&p->lock);
|
||||
p->count += this_cpu_read(var2);
|
||||
|
||||
La funzione migrate_disable() assicura che il processo venga tenuto sulla CPU
|
||||
corrente, e di conseguenza garantisce che gli accessi per-CPU alle variabili var1 e
|
||||
var2 rimangano sulla stessa CPU fintanto che il processo rimane prelabile.
|
||||
|
||||
La sostituzione con migrate_disable() non funzionerà nel seguente scenario::
|
||||
|
||||
func()
|
||||
{
|
||||
struct foo *p;
|
||||
|
||||
migrate_disable();
|
||||
p = this_cpu_ptr(&var1);
|
||||
p->val = func2();
|
||||
|
||||
Questo non funziona perché migrate_disable() non protegge dal ritorno da un
|
||||
processo che aveva avuto il diritto di prelazione. Una sostituzione più adatta
|
||||
per questo caso è::
|
||||
|
||||
func()
|
||||
{
|
||||
struct foo *p;
|
||||
|
||||
local_lock(&foo_lock);
|
||||
p = this_cpu_ptr(&var1);
|
||||
p->val = func2();
|
||||
|
||||
Su un kernel non-PREEMPT_RT, questo codice protegge dal rientro disabilitando la
|
||||
prelazione. Su un kernel PREEMPT_RT si ottiene lo stesso risultato acquisendo lo
|
||||
spinlock di CPU.
|
||||
|
||||
raw_spinlock_t su RT
|
||||
--------------------
|
||||
|
||||
Acquisire un raw_spinlock_t disabilita la prelazione e possibilmente anche le
|
||||
interruzioni, quindi la sezione critica deve evitare di acquisire uno spinlock_t
|
||||
o rwlock_t. Per esempio, la sezione critica non deve fare allocazioni di
|
||||
memoria. Su un kernel non-PREEMPT_RT il seguente codice funziona perfettamente::
|
||||
|
||||
raw_spin_lock(&lock);
|
||||
p = kmalloc(sizeof(*p), GFP_ATOMIC);
|
||||
|
||||
Ma lo stesso codice non funziona su un kernel PREEMPT_RT perché l'allocatore di
|
||||
memoria può essere oggetto di prelazione e quindi non può essere chiamato in un
|
||||
contesto atomico. Tuttavia, si può chiamare l'allocatore di memoria quando si
|
||||
trattiene un blocco *non-raw* perché non disabilitano la prelazione sui kernel
|
||||
PREEMPT_RT::
|
||||
|
||||
spin_lock(&lock);
|
||||
p = kmalloc(sizeof(*p), GFP_ATOMIC);
|
||||
|
||||
|
||||
bit spinlocks
|
||||
-------------
|
||||
|
||||
I kernel PREEMPT_RT non possono sostituire i bit spinlock perché un singolo bit
|
||||
è troppo piccolo per farci stare un rtmutex. Dunque, la semantica dei bit
|
||||
spinlock è mantenuta anche sui kernel PREEMPT_RT. Quindi, le precisazioni fatte
|
||||
per raw_spinlock_t valgono anche qui.
|
||||
|
||||
In PREEMPT_RT, alcuni bit spinlock sono sostituiti con normali spinlock_t usando
|
||||
condizioni di preprocessore in base a dove vengono usati. Per contro, questo non
|
||||
serve quando si sostituiscono gli spinlock_t. Invece, le condizioni poste sui
|
||||
file d'intestazione e sul cuore dell'implementazione della sincronizzazione
|
||||
permettono al compilatore di effettuare la sostituzione in modo trasparente.
|
||||
|
||||
|
||||
Regole d'annidamento dei tipi di blocchi
|
||||
========================================
|
||||
|
||||
Le regole principali sono:
|
||||
|
||||
- I tipi di blocco appartenenti alla stessa categoria possono essere annidati
|
||||
liberamente a patto che si rispetti l'ordine di blocco al fine di evitare
|
||||
stalli.
|
||||
|
||||
- I blocchi con sospensione non possono essere annidati in blocchi del tipo
|
||||
CPU locale o ad attesa attiva
|
||||
|
||||
- I blocchi ad attesa attiva e su CPU locale possono essere annidati nei
|
||||
blocchi ad attesa con sospensione.
|
||||
|
||||
- I blocchi ad attesa attiva possono essere annidati in qualsiasi altro tipo.
|
||||
|
||||
Queste limitazioni si applicano ad entrambe i kernel con o senza PREEMPT_RT.
|
||||
|
||||
Il fatto che un kernel PREEMPT_RT cambi i blocchi spinlock_t e rwlock_t dal tipo
|
||||
ad attesa attiva a quello con sospensione, e che sostituisca local_lock con uno
|
||||
spinlock_t per CPU, significa che non possono essere acquisiti quando si è in un
|
||||
blocco raw_spinlock. Ne consegue il seguente ordine d'annidamento:
|
||||
|
||||
1) blocchi ad attesa con sospensione
|
||||
2) spinlock_t, rwlock_t, local_lock
|
||||
3) raw_spinlock_t e bit spinlocks
|
||||
|
||||
Se queste regole verranno violate, allora lockdep se ne accorgerà e questo sia
|
||||
con o senza PREEMPT_RT.
|
@ -1,13 +0,0 @@
|
||||
.. include:: ../disclaimer-ita.rst
|
||||
|
||||
:Original: :ref:`Documentation/process/maintainer-netdev.rst <netdev-FAQ>`
|
||||
|
||||
.. _it_netdev-FAQ:
|
||||
|
||||
==========
|
||||
netdev FAQ
|
||||
==========
|
||||
|
||||
.. warning::
|
||||
|
||||
TODO ancora da tradurre
|
@ -575,9 +575,9 @@ due parti ``err_free_bar:`` e ``err_free_foo:``:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
err_free_bar:
|
||||
err_free_bar:
|
||||
kfree(foo->bar);
|
||||
err_free_foo:
|
||||
err_free_foo:
|
||||
kfree(foo);
|
||||
return ret;
|
||||
|
||||
@ -671,7 +671,7 @@ segue nel vostro file .emacs:
|
||||
(c-offsets-alist . (
|
||||
(arglist-close . c-lineup-arglist-tabs-only)
|
||||
(arglist-cont-nonempty .
|
||||
(c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only))
|
||||
(c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only))
|
||||
(arglist-intro . +)
|
||||
(brace-list-intro . +)
|
||||
(c . c-lineup-C-comments)
|
||||
|
47
Documentation/translations/it_IT/subsystem-apis.rst
Normal file
47
Documentation/translations/it_IT/subsystem-apis.rst
Normal file
@ -0,0 +1,47 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
==========================================
|
||||
Documentazione dei sottosistemi del kernel
|
||||
==========================================
|
||||
|
||||
In questa parte della documentazione si entra nel dettaglio di come funzionano
|
||||
i sottosistemi specifici del kernel dal punto di vista di uno sviluppatore del
|
||||
kernel. Molte delle informazioni qui contenute provengono direttamente dai
|
||||
sorgenti del kernel, con aggiunte di materiale dove è necessario (anche se
|
||||
talora *non* è stato aggiunto tutto ciò che era necessario).
|
||||
|
||||
Sottosistemi principali
|
||||
-----------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
core-api/index
|
||||
|
||||
Interfacce uomo-macchina
|
||||
------------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
|
||||
Interfacce di rete
|
||||
------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
Interfacce per l'archiviazione
|
||||
------------------------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
|
||||
Interfacce varie
|
||||
----------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
i2c/index
|
@ -11,7 +11,7 @@
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
howto
|
||||
process/howto
|
||||
|
||||
.. raw:: latex
|
||||
|
||||
|
@ -604,9 +604,9 @@ Normalmente la solución para esto es dividirlo en dos etiquetas de error
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
err_free_bar:
|
||||
err_free_bar:
|
||||
kfree(foo->bar);
|
||||
err_free_foo:
|
||||
err_free_foo:
|
||||
kfree(foo);
|
||||
return ret;
|
||||
|
||||
@ -698,7 +698,7 @@ sanos. Para hacer esto último, puede pegar lo siguiente en su archivo
|
||||
(c-offsets-alist . (
|
||||
(arglist-close . c-lineup-arglist-tabs-only)
|
||||
(arglist-cont-nonempty .
|
||||
(c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only))
|
||||
(c-lineup-gcc-asm-reg c-lineup-arglist-tabs-only))
|
||||
(arglist-intro . +)
|
||||
(brace-list-intro . +)
|
||||
(c . c-lineup-C-comments)
|
||||
|
@ -273,7 +273,7 @@ revelada involucrada. La lista de embajadores actuales:
|
||||
IBM Power Anton Blanchard <anton@linux.ibm.com>
|
||||
IBM Z Christian Borntraeger <borntraeger@de.ibm.com>
|
||||
Intel Tony Luck <tony.luck@intel.com>
|
||||
Qualcomm Trilok Soni <tsoni@codeaurora.org>
|
||||
Qualcomm Trilok Soni <quic_tsoni@quicinc.com>
|
||||
Samsung Javier González <javier.gonz@samsung.com>
|
||||
|
||||
Microsoft James Morris <jamorris@linux.microsoft.com>
|
||||
|
@ -147,4 +147,4 @@ Si no se puede encontrar a nadie para revisar internamente los parches y necesit
|
||||
ayuda para encontrar a esa persona, o si tiene alguna otra pregunta relacionada
|
||||
con este documento y las expectativas de la comunidad de desarrolladores, por
|
||||
favor contacte con la lista de correo privada Technical Advisory Board:
|
||||
<tech-board@lists.linux-foundation.org>.
|
||||
<tech-board@groups.linuxfoundation.org>.
|
||||
|
@ -523,9 +523,9 @@ Linux 里这是提倡的做法,因为这样可以很简单的给读者提供
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
err_free_bar:
|
||||
err_free_bar:
|
||||
kfree(foo->bar);
|
||||
err_free_foo:
|
||||
err_free_foo:
|
||||
kfree(foo);
|
||||
return ret;
|
||||
|
||||
|
@ -177,7 +177,7 @@ CVE分配
|
||||
AMD Tom Lendacky <thomas.lendacky@amd.com>
|
||||
IBM
|
||||
Intel Tony Luck <tony.luck@intel.com>
|
||||
Qualcomm Trilok Soni <tsoni@codeaurora.org>
|
||||
Qualcomm Trilok Soni <quic_tsoni@quicinc.com>
|
||||
|
||||
Microsoft Sasha Levin <sashal@kernel.org>
|
||||
VMware
|
||||
|
@ -53,7 +53,7 @@ OpenCAPI定义了一个在物理链路层上实现的数据链路层(TL)和
|
||||
|
||||
Processor:处理器
|
||||
Memory:内存
|
||||
Accelerated Function Unit:加速函数单元
|
||||
Accelerated Function Unit:加速功能单元
|
||||
|
||||
|
||||
|
||||
@ -97,7 +97,7 @@ OpenCAPI拥有AFU向主机进程发送中断的可能性。它通过定义在传
|
||||
========
|
||||
|
||||
驱动为每个在物理设备上发现的AFU创建一个字符设备。一个物理设备可能拥有多个
|
||||
函数,一个函数可以拥有多个AFU。不过编写这篇文档之时,只对导出一个AFU的设备
|
||||
功能,一个功能可以拥有多个AFU。不过编写这篇文档之时,只对导出一个AFU的设备
|
||||
测试过。
|
||||
|
||||
字符设备可以在 /dev/ocxl/ 中被找到,其命名为:
|
||||
|
@ -526,9 +526,9 @@ Linux 裏這是提倡的做法,因爲這樣可以很簡單的給讀者提供
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
err_free_bar:
|
||||
err_free_bar:
|
||||
kfree(foo->bar);
|
||||
err_free_foo:
|
||||
err_free_foo:
|
||||
kfree(foo);
|
||||
return ret;
|
||||
|
||||
|
@ -180,7 +180,7 @@ CVE分配
|
||||
AMD Tom Lendacky <thomas.lendacky@amd.com>
|
||||
IBM
|
||||
Intel Tony Luck <tony.luck@intel.com>
|
||||
Qualcomm Trilok Soni <tsoni@codeaurora.org>
|
||||
Qualcomm Trilok Soni <quic_tsoni@quicinc.com>
|
||||
|
||||
Microsoft Sasha Levin <sashal@kernel.org>
|
||||
VMware
|
||||
|
@ -9,31 +9,58 @@ While much of the kernel's user-space API is documented elsewhere
|
||||
also be found in the kernel tree itself. This manual is intended to be the
|
||||
place where this information is gathered.
|
||||
|
||||
|
||||
System calls
|
||||
============
|
||||
|
||||
.. toctree::
|
||||
:caption: Table of contents
|
||||
:maxdepth: 2
|
||||
:maxdepth: 1
|
||||
|
||||
unshare
|
||||
futex2
|
||||
ebpf/index
|
||||
ioctl/index
|
||||
|
||||
Security-related interfaces
|
||||
===========================
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
no_new_privs
|
||||
seccomp_filter
|
||||
landlock
|
||||
unshare
|
||||
lsm
|
||||
spec_ctrl
|
||||
tee
|
||||
|
||||
Devices and I/O
|
||||
===============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
accelerators/ocxl
|
||||
dma-buf-alloc-exchange
|
||||
ebpf/index
|
||||
ELF
|
||||
ioctl/index
|
||||
iommu
|
||||
iommufd
|
||||
media/index
|
||||
dcdbas
|
||||
vduse
|
||||
isapnp
|
||||
|
||||
Everything else
|
||||
===============
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 1
|
||||
|
||||
ELF
|
||||
netlink/index
|
||||
sysfs-platform_profile
|
||||
vduse
|
||||
futex2
|
||||
lsm
|
||||
tee
|
||||
isapnp
|
||||
dcdbas
|
||||
perf_ring_buffer
|
||||
|
||||
.. only:: subproject and html
|
||||
|
||||
|
830
Documentation/userspace-api/perf_ring_buffer.rst
Normal file
830
Documentation/userspace-api/perf_ring_buffer.rst
Normal file
@ -0,0 +1,830 @@
|
||||
.. SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
================
|
||||
Perf ring buffer
|
||||
================
|
||||
|
||||
.. CONTENTS
|
||||
|
||||
1. Introduction
|
||||
|
||||
2. Ring buffer implementation
|
||||
2.1 Basic algorithm
|
||||
2.2 Ring buffer for different tracing modes
|
||||
2.2.1 Default mode
|
||||
2.2.2 Per-thread mode
|
||||
2.2.3 Per-CPU mode
|
||||
2.2.4 System wide mode
|
||||
2.3 Accessing buffer
|
||||
2.3.1 Producer-consumer model
|
||||
2.3.2 Properties of the ring buffers
|
||||
2.3.3 Writing samples into buffer
|
||||
2.3.4 Reading samples from buffer
|
||||
2.3.5 Memory synchronization
|
||||
|
||||
3. The mechanism of AUX ring buffer
|
||||
3.1 The relationship between AUX and regular ring buffers
|
||||
3.2 AUX events
|
||||
3.3 Snapshot mode
|
||||
|
||||
|
||||
1. Introduction
|
||||
===============
|
||||
|
||||
The ring buffer is a fundamental mechanism for data transfer. perf uses
|
||||
ring buffers to transfer event data from kernel to user space, another
|
||||
kind of ring buffer which is so called auxiliary (AUX) ring buffer also
|
||||
plays an important role for hardware tracing with Intel PT, Arm
|
||||
CoreSight, etc.
|
||||
|
||||
The ring buffer implementation is critical but it's also a very
|
||||
challenging work. On the one hand, the kernel and perf tool in the user
|
||||
space use the ring buffer to exchange data and stores data into data
|
||||
file, thus the ring buffer needs to transfer data with high throughput;
|
||||
on the other hand, the ring buffer management should avoid significant
|
||||
overload to distract profiling results.
|
||||
|
||||
This documentation dives into the details for perf ring buffer with two
|
||||
parts: firstly it explains the perf ring buffer implementation, then the
|
||||
second part discusses the AUX ring buffer mechanism.
|
||||
|
||||
2. Ring buffer implementation
|
||||
=============================
|
||||
|
||||
2.1 Basic algorithm
|
||||
-------------------
|
||||
|
||||
That said, a typical ring buffer is managed by a head pointer and a tail
|
||||
pointer; the head pointer is manipulated by a writer and the tail
|
||||
pointer is updated by a reader respectively.
|
||||
|
||||
::
|
||||
|
||||
+---------------------------+
|
||||
| | |***|***|***| | |
|
||||
+---------------------------+
|
||||
`-> Tail `-> Head
|
||||
|
||||
* : the data is filled by the writer.
|
||||
|
||||
Figure 1. Ring buffer
|
||||
|
||||
Perf uses the same way to manage its ring buffer. In the implementation
|
||||
there are two key data structures held together in a set of consecutive
|
||||
pages, the control structure and then the ring buffer itself. The page
|
||||
with the control structure in is known as the "user page". Being held
|
||||
in continuous virtual addresses simplifies locating the ring buffer
|
||||
address, it is in the pages after the page with the user page.
|
||||
|
||||
The control structure is named as ``perf_event_mmap_page``, it contains a
|
||||
head pointer ``data_head`` and a tail pointer ``data_tail``. When the
|
||||
kernel starts to fill records into the ring buffer, it updates the head
|
||||
pointer to reserve the memory so later it can safely store events into
|
||||
the buffer. On the other side, when the user page is a writable mapping,
|
||||
the perf tool has the permission to update the tail pointer after consuming
|
||||
data from the ring buffer. Yet another case is for the user page's
|
||||
read-only mapping, which is to be addressed in the section
|
||||
:ref:`writing_samples_into_buffer`.
|
||||
|
||||
::
|
||||
|
||||
user page ring buffer
|
||||
+---------+---------+ +---------------------------------------+
|
||||
|data_head|data_tail|...| | |***|***|***|***|***| | | |
|
||||
+---------+---------+ +---------------------------------------+
|
||||
` `----------------^ ^
|
||||
`----------------------------------------------|
|
||||
|
||||
* : the data is filled by the writer.
|
||||
|
||||
Figure 2. Perf ring buffer
|
||||
|
||||
When using the ``perf record`` tool, we can specify the ring buffer size
|
||||
with option ``-m`` or ``--mmap-pages=``, the given size will be rounded up
|
||||
to a power of two that is a multiple of a page size. Though the kernel
|
||||
allocates at once for all memory pages, it's deferred to map the pages
|
||||
to VMA area until the perf tool accesses the buffer from the user space.
|
||||
In other words, at the first time accesses the buffer's page from user
|
||||
space in the perf tool, a data abort exception for page fault is taken
|
||||
and the kernel uses this occasion to map the page into process VMA
|
||||
(see ``perf_mmap_fault()``), thus the perf tool can continue to access
|
||||
the page after returning from the exception.
|
||||
|
||||
2.2 Ring buffer for different tracing modes
|
||||
-------------------------------------------
|
||||
|
||||
The perf profiles programs with different modes: default mode, per thread
|
||||
mode, per cpu mode, and system wide mode. This section describes these
|
||||
modes and how the ring buffer meets requirements for them. At last we
|
||||
will review the race conditions caused by these modes.
|
||||
|
||||
2.2.1 Default mode
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Usually we execute ``perf record`` command followed by a profiling program
|
||||
name, like below command::
|
||||
|
||||
perf record test_program
|
||||
|
||||
This command doesn't specify any options for CPU and thread modes, the
|
||||
perf tool applies the default mode on the perf event. It maps all the
|
||||
CPUs in the system and the profiled program's PID on the perf event, and
|
||||
it enables inheritance mode on the event so that child tasks inherits
|
||||
the events. As a result, the perf event is attributed as::
|
||||
|
||||
evsel::cpus::map[] = { 0 .. _SC_NPROCESSORS_ONLN-1 }
|
||||
evsel::threads::map[] = { pid }
|
||||
evsel::attr::inherit = 1
|
||||
|
||||
These attributions finally will be reflected on the deployment of ring
|
||||
buffers. As shown below, the perf tool allocates individual ring buffer
|
||||
for each CPU, but it only enables events for the profiled program rather
|
||||
than for all threads in the system. The *T1* thread represents the
|
||||
thread context of the 'test_program', whereas *T2* and *T3* are irrelevant
|
||||
threads in the system. The perf samples are exclusively collected for
|
||||
the *T1* thread and stored in the ring buffer associated with the CPU on
|
||||
which the *T1* thread is running.
|
||||
|
||||
::
|
||||
|
||||
T1 T2 T1
|
||||
+----+ +-----------+ +----+
|
||||
CPU0 |xxxx| |xxxxxxxxxxx| |xxxx|
|
||||
+----+--------------+-----------+----------+----+-------->
|
||||
| |
|
||||
v v
|
||||
+-----------------------------------------------------+
|
||||
| Ring buffer 0 |
|
||||
+-----------------------------------------------------+
|
||||
|
||||
T1
|
||||
+-----+
|
||||
CPU1 |xxxxx|
|
||||
-----+-----+--------------------------------------------->
|
||||
|
|
||||
v
|
||||
+-----------------------------------------------------+
|
||||
| Ring buffer 1 |
|
||||
+-----------------------------------------------------+
|
||||
|
||||
T1 T3
|
||||
+----+ +-------+
|
||||
CPU2 |xxxx| |xxxxxxx|
|
||||
--------------------------+----+--------+-------+-------->
|
||||
|
|
||||
v
|
||||
+-----------------------------------------------------+
|
||||
| Ring buffer 2 |
|
||||
+-----------------------------------------------------+
|
||||
|
||||
T1
|
||||
+--------------+
|
||||
CPU3 |xxxxxxxxxxxxxx|
|
||||
-----------+--------------+------------------------------>
|
||||
|
|
||||
v
|
||||
+-----------------------------------------------------+
|
||||
| Ring buffer 3 |
|
||||
+-----------------------------------------------------+
|
||||
|
||||
T1: Thread 1; T2: Thread 2; T3: Thread 3
|
||||
x: Thread is in running state
|
||||
|
||||
Figure 3. Ring buffer for default mode
|
||||
|
||||
2.2.2 Per-thread mode
|
||||
^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
By specifying option ``--per-thread`` in perf command, e.g.
|
||||
|
||||
::
|
||||
|
||||
perf record --per-thread test_program
|
||||
|
||||
The perf event doesn't map to any CPUs and is only bound to the
|
||||
profiled process, thus, the perf event's attributions are::
|
||||
|
||||
evsel::cpus::map[0] = { -1 }
|
||||
evsel::threads::map[] = { pid }
|
||||
evsel::attr::inherit = 0
|
||||
|
||||
In this mode, a single ring buffer is allocated for the profiled thread;
|
||||
if the thread is scheduled on a CPU, the events on that CPU will be
|
||||
enabled; and if the thread is scheduled out from the CPU, the events on
|
||||
the CPU will be disabled. When the thread is migrated from one CPU to
|
||||
another, the events are to be disabled on the previous CPU and enabled
|
||||
on the next CPU correspondingly.
|
||||
|
||||
::
|
||||
|
||||
T1 T2 T1
|
||||
+----+ +-----------+ +----+
|
||||
CPU0 |xxxx| |xxxxxxxxxxx| |xxxx|
|
||||
+----+--------------+-----------+----------+----+-------->
|
||||
| |
|
||||
| T1 |
|
||||
| +-----+ |
|
||||
CPU1 | |xxxxx| |
|
||||
--|--+-----+----------------------------------|---------->
|
||||
| | |
|
||||
| | T1 T3 |
|
||||
| | +----+ +---+ |
|
||||
CPU2 | | |xxxx| |xxx| |
|
||||
--|-----|-----------------+----+--------+---+-|---------->
|
||||
| | | |
|
||||
| | T1 | |
|
||||
| | +--------------+ | |
|
||||
CPU3 | | |xxxxxxxxxxxxxx| | |
|
||||
--|-----|--+--------------+-|-----------------|---------->
|
||||
| | | | |
|
||||
v v v v v
|
||||
+-----------------------------------------------------+
|
||||
| Ring buffer |
|
||||
+-----------------------------------------------------+
|
||||
|
||||
T1: Thread 1
|
||||
x: Thread is in running state
|
||||
|
||||
Figure 4. Ring buffer for per-thread mode
|
||||
|
||||
When perf runs in per-thread mode, a ring buffer is allocated for the
|
||||
profiled thread *T1*. The ring buffer is dedicated for thread *T1*, if the
|
||||
thread *T1* is running, the perf events will be recorded into the ring
|
||||
buffer; when the thread is sleeping, all associated events will be
|
||||
disabled, thus no trace data will be recorded into the ring buffer.
|
||||
|
||||
2.2.3 Per-CPU mode
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The option ``-C`` is used to collect samples on the list of CPUs, for
|
||||
example the below perf command receives option ``-C 0,2``::
|
||||
|
||||
perf record -C 0,2 test_program
|
||||
|
||||
It maps the perf event to CPUs 0 and 2, and the event is not associated to any
|
||||
PID. Thus the perf event attributions are set as::
|
||||
|
||||
evsel::cpus::map[0] = { 0, 2 }
|
||||
evsel::threads::map[] = { -1 }
|
||||
evsel::attr::inherit = 0
|
||||
|
||||
This results in the session of ``perf record`` will sample all threads on CPU0
|
||||
and CPU2, and be terminated until test_program exits. Even there have tasks
|
||||
running on CPU1 and CPU3, since the ring buffer is absent for them, any
|
||||
activities on these two CPUs will be ignored. A usage case is to combine the
|
||||
options for per-thread mode and per-CPU mode, e.g. the options ``–C 0,2`` and
|
||||
``––per–thread`` are specified together, the samples are recorded only when
|
||||
the profiled thread is scheduled on any of the listed CPUs.
|
||||
|
||||
::
|
||||
|
||||
T1 T2 T1
|
||||
+----+ +-----------+ +----+
|
||||
CPU0 |xxxx| |xxxxxxxxxxx| |xxxx|
|
||||
+----+--------------+-----------+----------+----+-------->
|
||||
| | |
|
||||
v v v
|
||||
+-----------------------------------------------------+
|
||||
| Ring buffer 0 |
|
||||
+-----------------------------------------------------+
|
||||
|
||||
T1
|
||||
+-----+
|
||||
CPU1 |xxxxx|
|
||||
-----+-----+--------------------------------------------->
|
||||
|
||||
T1 T3
|
||||
+----+ +-------+
|
||||
CPU2 |xxxx| |xxxxxxx|
|
||||
--------------------------+----+--------+-------+-------->
|
||||
| |
|
||||
v v
|
||||
+-----------------------------------------------------+
|
||||
| Ring buffer 1 |
|
||||
+-----------------------------------------------------+
|
||||
|
||||
T1
|
||||
+--------------+
|
||||
CPU3 |xxxxxxxxxxxxxx|
|
||||
-----------+--------------+------------------------------>
|
||||
|
||||
T1: Thread 1; T2: Thread 2; T3: Thread 3
|
||||
x: Thread is in running state
|
||||
|
||||
Figure 5. Ring buffer for per-CPU mode
|
||||
|
||||
2.2.4 System wide mode
|
||||
^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
By using option ``–a`` or ``––all–cpus``, perf collects samples on all CPUs
|
||||
for all tasks, we call it as the system wide mode, the command is::
|
||||
|
||||
perf record -a test_program
|
||||
|
||||
Similar to the per-CPU mode, the perf event doesn't bind to any PID, and
|
||||
it maps to all CPUs in the system::
|
||||
|
||||
evsel::cpus::map[] = { 0 .. _SC_NPROCESSORS_ONLN-1 }
|
||||
evsel::threads::map[] = { -1 }
|
||||
evsel::attr::inherit = 0
|
||||
|
||||
In the system wide mode, every CPU has its own ring buffer, all threads
|
||||
are monitored during the running state and the samples are recorded into
|
||||
the ring buffer belonging to the CPU which the events occurred on.
|
||||
|
||||
::
|
||||
|
||||
T1 T2 T1
|
||||
+----+ +-----------+ +----+
|
||||
CPU0 |xxxx| |xxxxxxxxxxx| |xxxx|
|
||||
+----+--------------+-----------+----------+----+-------->
|
||||
| | |
|
||||
v v v
|
||||
+-----------------------------------------------------+
|
||||
| Ring buffer 0 |
|
||||
+-----------------------------------------------------+
|
||||
|
||||
T1
|
||||
+-----+
|
||||
CPU1 |xxxxx|
|
||||
-----+-----+--------------------------------------------->
|
||||
|
|
||||
v
|
||||
+-----------------------------------------------------+
|
||||
| Ring buffer 1 |
|
||||
+-----------------------------------------------------+
|
||||
|
||||
T1 T3
|
||||
+----+ +-------+
|
||||
CPU2 |xxxx| |xxxxxxx|
|
||||
--------------------------+----+--------+-------+-------->
|
||||
| |
|
||||
v v
|
||||
+-----------------------------------------------------+
|
||||
| Ring buffer 2 |
|
||||
+-----------------------------------------------------+
|
||||
|
||||
T1
|
||||
+--------------+
|
||||
CPU3 |xxxxxxxxxxxxxx|
|
||||
-----------+--------------+------------------------------>
|
||||
|
|
||||
v
|
||||
+-----------------------------------------------------+
|
||||
| Ring buffer 3 |
|
||||
+-----------------------------------------------------+
|
||||
|
||||
T1: Thread 1; T2: Thread 2; T3: Thread 3
|
||||
x: Thread is in running state
|
||||
|
||||
Figure 6. Ring buffer for system wide mode
|
||||
|
||||
2.3 Accessing buffer
|
||||
--------------------
|
||||
|
||||
Based on the understanding of how the ring buffer is allocated in
|
||||
various modes, this section explains access the ring buffer.
|
||||
|
||||
2.3.1 Producer-consumer model
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In the Linux kernel, the PMU events can produce samples which are stored
|
||||
into the ring buffer; the perf command in user space consumes the
|
||||
samples by reading out data from the ring buffer and finally saves the
|
||||
data into the file for post analysis. It’s a typical producer-consumer
|
||||
model for using the ring buffer.
|
||||
|
||||
The perf process polls on the PMU events and sleeps when no events are
|
||||
incoming. To prevent frequent exchanges between the kernel and user
|
||||
space, the kernel event core layer introduces a watermark, which is
|
||||
stored in the ``perf_buffer::watermark``. When a sample is recorded into
|
||||
the ring buffer, and if the used buffer exceeds the watermark, the
|
||||
kernel wakes up the perf process to read samples from the ring buffer.
|
||||
|
||||
::
|
||||
|
||||
Perf
|
||||
/ | Read samples
|
||||
Polling / `--------------| Ring buffer
|
||||
v v ;---------------------v
|
||||
+----------------+ +---------+---------+ +-------------------+
|
||||
|Event wait queue| |data_head|data_tail| |***|***| | |***|
|
||||
+----------------+ +---------+---------+ +-------------------+
|
||||
^ ^ `------------------------^
|
||||
| Wake up tasks | Store samples
|
||||
+-----------------------------+
|
||||
| Kernel event core layer |
|
||||
+-----------------------------+
|
||||
|
||||
* : the data is filled by the writer.
|
||||
|
||||
Figure 7. Writing and reading the ring buffer
|
||||
|
||||
When the kernel event core layer notifies the user space, because
|
||||
multiple events might share the same ring buffer for recording samples,
|
||||
the core layer iterates every event associated with the ring buffer and
|
||||
wakes up tasks waiting on the event. This is fulfilled by the kernel
|
||||
function ``ring_buffer_wakeup()``.
|
||||
|
||||
After the perf process is woken up, it starts to check the ring buffers
|
||||
one by one, if it finds any ring buffer containing samples it will read
|
||||
out the samples for statistics or saving into the data file. Given the
|
||||
perf process is able to run on any CPU, this leads to the ring buffer
|
||||
potentially being accessed from multiple CPUs simultaneously, which
|
||||
causes race conditions. The race condition handling is described in the
|
||||
section :ref:`memory_synchronization`.
|
||||
|
||||
2.3.2 Properties of the ring buffers
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Linux kernel supports two write directions for the ring buffer: forward and
|
||||
backward. The forward writing saves samples from the beginning of the ring
|
||||
buffer, the backward writing stores data from the end of the ring buffer with
|
||||
the reversed direction. The perf tool determines the writing direction.
|
||||
|
||||
Additionally, the tool can map buffers in either read-write mode or read-only
|
||||
mode to the user space.
|
||||
|
||||
The ring buffer in the read-write mode is mapped with the property
|
||||
``PROT_READ | PROT_WRITE``. With the write permission, the perf tool
|
||||
updates the ``data_tail`` to indicate the data start position. Combining
|
||||
with the head pointer ``data_head``, which works as the end position of
|
||||
the current data, the perf tool can easily know where read out the data
|
||||
from.
|
||||
|
||||
Alternatively, in the read-only mode, only the kernel keeps to update
|
||||
the ``data_head`` while the user space cannot access the ``data_tail`` due
|
||||
to the mapping property ``PROT_READ``.
|
||||
|
||||
As a result, the matrix below illustrates the various combinations of
|
||||
direction and mapping characteristics. The perf tool employs two of these
|
||||
combinations to support buffer types: the non-overwrite buffer and the
|
||||
overwritable buffer.
|
||||
|
||||
.. list-table::
|
||||
:widths: 1 1 1
|
||||
:header-rows: 1
|
||||
|
||||
* - Mapping mode
|
||||
- Forward
|
||||
- Backward
|
||||
* - read-write
|
||||
- Non-overwrite ring buffer
|
||||
- Not used
|
||||
* - read-only
|
||||
- Not used
|
||||
- Overwritable ring buffer
|
||||
|
||||
The non-overwrite ring buffer uses the read-write mapping with forward
|
||||
writing. It starts to save data from the beginning of the ring buffer
|
||||
and wrap around when overflow, which is used with the read-write mode in
|
||||
the normal ring buffer. When the consumer doesn't keep up with the
|
||||
producer, it would lose some data, the kernel keeps how many records it
|
||||
lost and generates the ``PERF_RECORD_LOST`` records in the next time
|
||||
when it finds a space in the ring buffer.
|
||||
|
||||
The overwritable ring buffer uses the backward writing with the
|
||||
read-only mode. It saves the data from the end of the ring buffer and
|
||||
the ``data_head`` keeps the position of current data, the perf always
|
||||
knows where it starts to read and until the end of the ring buffer, thus
|
||||
it don't need the ``data_tail``. In this mode, it will not generate the
|
||||
``PERF_RECORD_LOST`` records.
|
||||
|
||||
.. _writing_samples_into_buffer:
|
||||
|
||||
2.3.3 Writing samples into buffer
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
When a sample is taken and saved into the ring buffer, the kernel
|
||||
prepares sample fields based on the sample type; then it prepares the
|
||||
info for writing ring buffer which is stored in the structure
|
||||
``perf_output_handle``. In the end, the kernel outputs the sample into
|
||||
the ring buffer and updates the head pointer in the user page so the
|
||||
perf tool can see the latest value.
|
||||
|
||||
The structure ``perf_output_handle`` serves as a temporary context for
|
||||
tracking the information related to the buffer. The advantages of it is
|
||||
that it enables concurrent writing to the buffer by different events.
|
||||
For example, a software event and a hardware PMU event both are enabled
|
||||
for profiling, two instances of ``perf_output_handle`` serve as separate
|
||||
contexts for the software event and the hardware event respectively.
|
||||
This allows each event to reserve its own memory space for populating
|
||||
the record data.
|
||||
|
||||
2.3.4 Reading samples from buffer
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
In the user space, the perf tool utilizes the ``perf_event_mmap_page``
|
||||
structure to handle the head and tail of the buffer. It also uses
|
||||
``perf_mmap`` structure to keep track of a context for the ring buffer, this
|
||||
context includes information about the buffer's starting and ending
|
||||
addresses. Additionally, the mask value can be utilized to compute the
|
||||
circular buffer pointer even for an overflow.
|
||||
|
||||
Similar to the kernel, the perf tool in the user space first reads out
|
||||
the recorded data from the ring buffer, and then updates the buffer's
|
||||
tail pointer ``perf_event_mmap_page::data_tail``.
|
||||
|
||||
.. _memory_synchronization:
|
||||
|
||||
2.3.5 Memory synchronization
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The modern CPUs with relaxed memory model cannot promise the memory
|
||||
ordering, this means it’s possible to access the ring buffer and the
|
||||
``perf_event_mmap_page`` structure out of order. To assure the specific
|
||||
sequence for memory accessing perf ring buffer, memory barriers are
|
||||
used to assure the data dependency. The rationale for the memory
|
||||
synchronization is as below::
|
||||
|
||||
Kernel User space
|
||||
|
||||
if (LOAD ->data_tail) { LOAD ->data_head
|
||||
(A) smp_rmb() (C)
|
||||
STORE $data LOAD $data
|
||||
smp_wmb() (B) smp_mb() (D)
|
||||
STORE ->data_head STORE ->data_tail
|
||||
}
|
||||
|
||||
The comments in tools/include/linux/ring_buffer.h gives nice description
|
||||
for why and how to use memory barriers, here we will just provide an
|
||||
alternative explanation:
|
||||
|
||||
(A) is a control dependency so that CPU assures order between checking
|
||||
pointer ``perf_event_mmap_page::data_tail`` and filling sample into ring
|
||||
buffer;
|
||||
|
||||
(D) pairs with (A). (D) separates the ring buffer data reading from
|
||||
writing the pointer ``data_tail``, perf tool first consumes samples and then
|
||||
tells the kernel that the data chunk has been released. Since a reading
|
||||
operation is followed by a writing operation, thus (D) is a full memory
|
||||
barrier.
|
||||
|
||||
(B) is a writing barrier in the middle of two writing operations, which
|
||||
makes sure that recording a sample must be prior to updating the head
|
||||
pointer.
|
||||
|
||||
(C) pairs with (B). (C) is a read memory barrier to ensure the head
|
||||
pointer is fetched before reading samples.
|
||||
|
||||
To implement the above algorithm, the ``perf_output_put_handle()`` function
|
||||
in the kernel and two helpers ``ring_buffer_read_head()`` and
|
||||
``ring_buffer_write_tail()`` in the user space are introduced, they rely
|
||||
on memory barriers as described above to ensure the data dependency.
|
||||
|
||||
Some architectures support one-way permeable barrier with load-acquire
|
||||
and store-release operations, these barriers are more relaxed with less
|
||||
performance penalty, so (C) and (D) can be optimized to use barriers
|
||||
``smp_load_acquire()`` and ``smp_store_release()`` respectively.
|
||||
|
||||
If an architecture doesn’t support load-acquire and store-release in its
|
||||
memory model, it will roll back to the old fashion of memory barrier
|
||||
operations. In this case, ``smp_load_acquire()`` encapsulates
|
||||
``READ_ONCE()`` + ``smp_mb()``, since ``smp_mb()`` is costly,
|
||||
``ring_buffer_read_head()`` doesn't invoke ``smp_load_acquire()`` and it uses
|
||||
the barriers ``READ_ONCE()`` + ``smp_rmb()`` instead.
|
||||
|
||||
3. The mechanism of AUX ring buffer
|
||||
===================================
|
||||
|
||||
In this chapter, we will explain the implementation of the AUX ring
|
||||
buffer. In the first part it will discuss the connection between the
|
||||
AUX ring buffer and the regular ring buffer, then the second part will
|
||||
examine how the AUX ring buffer co-works with the regular ring buffer,
|
||||
as well as the additional features introduced by the AUX ring buffer for
|
||||
the sampling mechanism.
|
||||
|
||||
3.1 The relationship between AUX and regular ring buffers
|
||||
---------------------------------------------------------
|
||||
|
||||
Generally, the AUX ring buffer is an auxiliary for the regular ring
|
||||
buffer. The regular ring buffer is primarily used to store the event
|
||||
samples and every event format complies with the definition in the
|
||||
union ``perf_event``; the AUX ring buffer is for recording the hardware
|
||||
trace data and the trace data format is hardware IP dependent.
|
||||
|
||||
The general use and advantage of the AUX ring buffer is that it is
|
||||
written directly by hardware rather than by the kernel. For example,
|
||||
regular profile samples that write to the regular ring buffer cause an
|
||||
interrupt. Tracing execution requires a high number of samples and
|
||||
using interrupts would be overwhelming for the regular ring buffer
|
||||
mechanism. Having an AUX buffer allows for a region of memory more
|
||||
decoupled from the kernel and written to directly by hardware tracing.
|
||||
|
||||
The AUX ring buffer reuses the same algorithm with the regular ring
|
||||
buffer for the buffer management. The control structure
|
||||
``perf_event_mmap_page`` extends the new fields ``aux_head`` and ``aux_tail``
|
||||
for the head and tail pointers of the AUX ring buffer.
|
||||
|
||||
During the initialisation phase, besides the mmap()-ed regular ring
|
||||
buffer, the perf tool invokes a second syscall in the
|
||||
``auxtrace_mmap__mmap()`` function for the mmap of the AUX buffer with
|
||||
non-zero file offset; ``rb_alloc_aux()`` in the kernel allocates pages
|
||||
correspondingly, these pages will be deferred to map into VMA when
|
||||
handling the page fault, which is the same lazy mechanism with the
|
||||
regular ring buffer.
|
||||
|
||||
AUX events and AUX trace data are two different things. Let's see an
|
||||
example::
|
||||
|
||||
perf record -a -e cycles -e cs_etm/@tmc_etr0/ -- sleep 2
|
||||
|
||||
The above command enables two events: one is the event *cycles* from PMU
|
||||
and another is the AUX event *cs_etm* from Arm CoreSight, both are saved
|
||||
into the regular ring buffer while the CoreSight's AUX trace data is
|
||||
stored in the AUX ring buffer.
|
||||
|
||||
As a result, we can see the regular ring buffer and the AUX ring buffer
|
||||
are allocated in pairs. The perf in default mode allocates the regular
|
||||
ring buffer and the AUX ring buffer per CPU-wise, which is the same as
|
||||
the system wide mode, however, the default mode records samples only for
|
||||
the profiled program, whereas the latter mode profiles for all programs
|
||||
in the system. For per-thread mode, the perf tool allocates only one
|
||||
regular ring buffer and one AUX ring buffer for the whole session. For
|
||||
the per-CPU mode, the perf allocates two kinds of ring buffers for
|
||||
selected CPUs specified by the option ``-C``.
|
||||
|
||||
The below figure demonstrates the buffers' layout in the system wide
|
||||
mode; if there are any activities on one CPU, the AUX event samples and
|
||||
the hardware trace data will be recorded into the dedicated buffers for
|
||||
the CPU.
|
||||
|
||||
::
|
||||
|
||||
T1 T2 T1
|
||||
+----+ +-----------+ +----+
|
||||
CPU0 |xxxx| |xxxxxxxxxxx| |xxxx|
|
||||
+----+--------------+-----------+----------+----+-------->
|
||||
| | |
|
||||
v v v
|
||||
+-----------------------------------------------------+
|
||||
| Ring buffer 0 |
|
||||
+-----------------------------------------------------+
|
||||
| | |
|
||||
v v v
|
||||
+-----------------------------------------------------+
|
||||
| AUX Ring buffer 0 |
|
||||
+-----------------------------------------------------+
|
||||
|
||||
T1
|
||||
+-----+
|
||||
CPU1 |xxxxx|
|
||||
-----+-----+--------------------------------------------->
|
||||
|
|
||||
v
|
||||
+-----------------------------------------------------+
|
||||
| Ring buffer 1 |
|
||||
+-----------------------------------------------------+
|
||||
|
|
||||
v
|
||||
+-----------------------------------------------------+
|
||||
| AUX Ring buffer 1 |
|
||||
+-----------------------------------------------------+
|
||||
|
||||
T1 T3
|
||||
+----+ +-------+
|
||||
CPU2 |xxxx| |xxxxxxx|
|
||||
--------------------------+----+--------+-------+-------->
|
||||
| |
|
||||
v v
|
||||
+-----------------------------------------------------+
|
||||
| Ring buffer 2 |
|
||||
+-----------------------------------------------------+
|
||||
| |
|
||||
v v
|
||||
+-----------------------------------------------------+
|
||||
| AUX Ring buffer 2 |
|
||||
+-----------------------------------------------------+
|
||||
|
||||
T1
|
||||
+--------------+
|
||||
CPU3 |xxxxxxxxxxxxxx|
|
||||
-----------+--------------+------------------------------>
|
||||
|
|
||||
v
|
||||
+-----------------------------------------------------+
|
||||
| Ring buffer 3 |
|
||||
+-----------------------------------------------------+
|
||||
|
|
||||
v
|
||||
+-----------------------------------------------------+
|
||||
| AUX Ring buffer 3 |
|
||||
+-----------------------------------------------------+
|
||||
|
||||
T1: Thread 1; T2: Thread 2; T3: Thread 3
|
||||
x: Thread is in running state
|
||||
|
||||
Figure 8. AUX ring buffer for system wide mode
|
||||
|
||||
3.2 AUX events
|
||||
--------------
|
||||
|
||||
Similar to ``perf_output_begin()`` and ``perf_output_end()``'s working for the
|
||||
regular ring buffer, ``perf_aux_output_begin()`` and ``perf_aux_output_end()``
|
||||
serve for the AUX ring buffer for processing the hardware trace data.
|
||||
|
||||
Once the hardware trace data is stored into the AUX ring buffer, the PMU
|
||||
driver will stop hardware tracing by calling the ``pmu::stop()`` callback.
|
||||
Similar to the regular ring buffer, the AUX ring buffer needs to apply
|
||||
the memory synchronization mechanism as discussed in the section
|
||||
:ref:`memory_synchronization`. Since the AUX ring buffer is managed by the
|
||||
PMU driver, the barrier (B), which is a writing barrier to ensure the trace
|
||||
data is externally visible prior to updating the head pointer, is asked
|
||||
to be implemented in the PMU driver.
|
||||
|
||||
Then ``pmu::stop()`` can safely call the ``perf_aux_output_end()`` function to
|
||||
finish two things:
|
||||
|
||||
- It fills an event ``PERF_RECORD_AUX`` into the regular ring buffer, this
|
||||
event delivers the information of the start address and data size for a
|
||||
chunk of hardware trace data has been stored into the AUX ring buffer;
|
||||
|
||||
- Since the hardware trace driver has stored new trace data into the AUX
|
||||
ring buffer, the argument *size* indicates how many bytes have been
|
||||
consumed by the hardware tracing, thus ``perf_aux_output_end()`` updates the
|
||||
header pointer ``perf_buffer::aux_head`` to reflect the latest buffer usage.
|
||||
|
||||
At the end, the PMU driver will restart hardware tracing. During this
|
||||
temporary suspending period, it will lose hardware trace data, which
|
||||
will introduce a discontinuity during decoding phase.
|
||||
|
||||
The event ``PERF_RECORD_AUX`` presents an AUX event which is handled in the
|
||||
kernel, but it lacks the information for saving the AUX trace data in
|
||||
the perf file. When the perf tool copies the trace data from AUX ring
|
||||
buffer to the perf data file, it synthesizes a ``PERF_RECORD_AUXTRACE``
|
||||
event which is not a kernel ABI, it's defined by the perf tool to describe
|
||||
which portion of data in the AUX ring buffer is saved. Afterwards, the perf
|
||||
tool reads out the AUX trace data from the perf file based on the
|
||||
``PERF_RECORD_AUXTRACE`` events, and the ``PERF_RECORD_AUX`` event is used to
|
||||
decode a chunk of data by correlating with time order.
|
||||
|
||||
3.3 Snapshot mode
|
||||
-----------------
|
||||
|
||||
Perf supports snapshot mode for AUX ring buffer, in this mode, users
|
||||
only record AUX trace data at a specific time point which users are
|
||||
interested in. E.g. below gives an example of how to take snapshots
|
||||
with 1 second interval with Arm CoreSight::
|
||||
|
||||
perf record -e cs_etm/@tmc_etr0/u -S -a program &
|
||||
PERFPID=$!
|
||||
while true; do
|
||||
kill -USR2 $PERFPID
|
||||
sleep 1
|
||||
done
|
||||
|
||||
The main flow for snapshot mode is:
|
||||
|
||||
- Before a snapshot is taken, the AUX ring buffer acts in free run mode.
|
||||
During free run mode the perf doesn't record any of the AUX events and
|
||||
trace data;
|
||||
|
||||
- Once the perf tool receives the *USR2* signal, it triggers the callback
|
||||
function ``auxtrace_record::snapshot_start()`` to deactivate hardware
|
||||
tracing. The kernel driver then populates the AUX ring buffer with the
|
||||
hardware trace data, and the event ``PERF_RECORD_AUX`` is stored in the
|
||||
regular ring buffer;
|
||||
|
||||
- Then perf tool takes a snapshot, ``record__read_auxtrace_snapshot()``
|
||||
reads out the hardware trace data from the AUX ring buffer and saves it
|
||||
into perf data file;
|
||||
|
||||
- After the snapshot is finished, ``auxtrace_record::snapshot_finish()``
|
||||
restarts the PMU event for AUX tracing.
|
||||
|
||||
The perf only accesses the head pointer ``perf_event_mmap_page::aux_head``
|
||||
in snapshot mode and doesn’t touch tail pointer ``aux_tail``, this is
|
||||
because the AUX ring buffer can overflow in free run mode, the tail
|
||||
pointer is useless in this case. Alternatively, the callback
|
||||
``auxtrace_record::find_snapshot()`` is introduced for making the decision
|
||||
of whether the AUX ring buffer has been wrapped around or not, at the
|
||||
end it fixes up the AUX buffer's head which are used to calculate the
|
||||
trace data size.
|
||||
|
||||
As we know, the buffers' deployment can be per-thread mode, per-CPU
|
||||
mode, or system wide mode, and the snapshot can be applied to any of
|
||||
these modes. Below is an example of taking snapshot with system wide
|
||||
mode.
|
||||
|
||||
::
|
||||
|
||||
Snapshot is taken
|
||||
|
|
||||
v
|
||||
+------------------------+
|
||||
| AUX Ring buffer 0 | <- aux_head
|
||||
+------------------------+
|
||||
v
|
||||
+--------------------------------+
|
||||
| AUX Ring buffer 1 | <- aux_head
|
||||
+--------------------------------+
|
||||
v
|
||||
+--------------------------------------------+
|
||||
| AUX Ring buffer 2 | <- aux_head
|
||||
+--------------------------------------------+
|
||||
v
|
||||
+---------------------------------------+
|
||||
| AUX Ring buffer 3 | <- aux_head
|
||||
+---------------------------------------+
|
||||
|
||||
Figure 9. Snapshot with system wide mode
|
@ -24,7 +24,7 @@ Descriptions of section entries and preferred order
|
||||
filing info, a direct bug tracker link, or a mailto: URI.
|
||||
C: URI for *chat* protocol, server and channel where developers
|
||||
usually hang out, for example irc://server/channel.
|
||||
P: Subsystem Profile document for more details submitting
|
||||
P: *Subsystem Profile* document for more details submitting
|
||||
patches to the given subsystem. This is either an in-tree file,
|
||||
or a URI. See Documentation/maintainer/maintainer-entry-profile.rst
|
||||
for details.
|
||||
@ -6385,6 +6385,7 @@ L: linux-doc@vger.kernel.org
|
||||
S: Maintained
|
||||
F: Documentation/admin-guide/quickly-build-trimmed-linux.rst
|
||||
F: Documentation/admin-guide/reporting-issues.rst
|
||||
F: Documentation/admin-guide/verify-bugs-and-bisect-regressions.rst
|
||||
|
||||
DOCUMENTATION SCRIPTS
|
||||
M: Mauro Carvalho Chehab <mchehab@kernel.org>
|
||||
@ -14035,7 +14036,7 @@ F: include/uapi/rdma/mlx5-abi.h
|
||||
|
||||
MELLANOX MLX5 VDPA DRIVER
|
||||
M: Dragos Tatulea <dtatulea@nvidia.com>
|
||||
L: virtualization@lists.linux-foundation.org
|
||||
L: virtualization@lists.linux.dev
|
||||
S: Supported
|
||||
F: drivers/vdpa/mlx5/
|
||||
|
||||
@ -21540,7 +21541,7 @@ F: tools/testing/selftests/drivers/net/team/
|
||||
TECHNICAL ADVISORY BOARD PROCESS DOCS
|
||||
M: "Theodore Ts'o" <tytso@mit.edu>
|
||||
M: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
|
||||
L: tech-board-discuss@lists.linux-foundation.org
|
||||
L: tech-board-discuss@lists.linux.dev
|
||||
S: Maintained
|
||||
F: Documentation/process/contribution-maturity-model.rst
|
||||
F: Documentation/process/researcher-guidelines.rst
|
||||
@ -23123,7 +23124,7 @@ F: drivers/vfio/pci/mlx5/
|
||||
VFIO VIRTIO PCI DRIVER
|
||||
M: Yishai Hadas <yishaih@nvidia.com>
|
||||
L: kvm@vger.kernel.org
|
||||
L: virtualization@lists.linux-foundation.org
|
||||
L: virtualization@lists.linux.dev
|
||||
S: Maintained
|
||||
F: drivers/vfio/pci/virtio
|
||||
|
||||
|
2
README
2
README
@ -11,7 +11,7 @@ In order to build the documentation, use ``make htmldocs`` or
|
||||
https://www.kernel.org/doc/html/latest/
|
||||
|
||||
There are various text files in the Documentation/ subdirectory,
|
||||
several of them using the Restructured Text markup notation.
|
||||
several of them using the ReStructured Text markup notation.
|
||||
|
||||
Please read the Documentation/process/changes.rst file, as it contains the
|
||||
requirements for building and running the kernel, and information about
|
||||
|
@ -260,8 +260,7 @@ static u64 drm_gem_vram_pg_offset(struct drm_gem_vram_object *gbo)
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_gem_vram_offset() - \
|
||||
Returns a GEM VRAM object's offset in video memory
|
||||
* drm_gem_vram_offset() - Returns a GEM VRAM object's offset in video memory
|
||||
* @gbo: the GEM VRAM object
|
||||
*
|
||||
* This function returns the buffer object's offset in the device's video
|
||||
@ -470,14 +469,15 @@ void drm_gem_vram_vunmap(struct drm_gem_vram_object *gbo,
|
||||
EXPORT_SYMBOL(drm_gem_vram_vunmap);
|
||||
|
||||
/**
|
||||
* drm_gem_vram_fill_create_dumb() - \
|
||||
Helper for implementing &struct drm_driver.dumb_create
|
||||
* drm_gem_vram_fill_create_dumb() - Helper for implementing
|
||||
* &struct drm_driver.dumb_create
|
||||
*
|
||||
* @file: the DRM file
|
||||
* @dev: the DRM device
|
||||
* @pg_align: the buffer's alignment in multiples of the page size
|
||||
* @pitch_align: the scanline's alignment in powers of 2
|
||||
* @args: the arguments as provided to \
|
||||
&struct drm_driver.dumb_create
|
||||
* @args: the arguments as provided to
|
||||
* &struct drm_driver.dumb_create
|
||||
*
|
||||
* This helper function fills &struct drm_mode_create_dumb, which is used
|
||||
* by &struct drm_driver.dumb_create. Implementations of this interface
|
||||
@ -575,8 +575,7 @@ static int drm_gem_vram_bo_driver_move(struct drm_gem_vram_object *gbo,
|
||||
*/
|
||||
|
||||
/**
|
||||
* drm_gem_vram_object_free() - \
|
||||
Implements &struct drm_gem_object_funcs.free
|
||||
* drm_gem_vram_object_free() - Implements &struct drm_gem_object_funcs.free
|
||||
* @gem: GEM object. Refers to &struct drm_gem_vram_object.gem
|
||||
*/
|
||||
static void drm_gem_vram_object_free(struct drm_gem_object *gem)
|
||||
@ -591,12 +590,11 @@ static void drm_gem_vram_object_free(struct drm_gem_object *gem)
|
||||
*/
|
||||
|
||||
/**
|
||||
* drm_gem_vram_driver_dumb_create() - \
|
||||
Implements &struct drm_driver.dumb_create
|
||||
* drm_gem_vram_driver_dumb_create() - Implements &struct drm_driver.dumb_create
|
||||
* @file: the DRM file
|
||||
* @dev: the DRM device
|
||||
* @args: the arguments as provided to \
|
||||
&struct drm_driver.dumb_create
|
||||
* @args: the arguments as provided to
|
||||
* &struct drm_driver.dumb_create
|
||||
*
|
||||
* This function requires the driver to use @drm_device.vram_mm for its
|
||||
* instance of VRAM MM.
|
||||
@ -639,8 +637,8 @@ static void __drm_gem_vram_plane_helper_cleanup_fb(struct drm_plane *plane,
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_gem_vram_plane_helper_prepare_fb() - \
|
||||
* Implements &struct drm_plane_helper_funcs.prepare_fb
|
||||
* drm_gem_vram_plane_helper_prepare_fb() - Implements &struct
|
||||
* drm_plane_helper_funcs.prepare_fb
|
||||
* @plane: a DRM plane
|
||||
* @new_state: the plane's new state
|
||||
*
|
||||
@ -690,8 +688,8 @@ err_drm_gem_vram_unpin:
|
||||
EXPORT_SYMBOL(drm_gem_vram_plane_helper_prepare_fb);
|
||||
|
||||
/**
|
||||
* drm_gem_vram_plane_helper_cleanup_fb() - \
|
||||
* Implements &struct drm_plane_helper_funcs.cleanup_fb
|
||||
* drm_gem_vram_plane_helper_cleanup_fb() - Implements &struct
|
||||
* drm_plane_helper_funcs.cleanup_fb
|
||||
* @plane: a DRM plane
|
||||
* @old_state: the plane's old state
|
||||
*
|
||||
@ -717,8 +715,8 @@ EXPORT_SYMBOL(drm_gem_vram_plane_helper_cleanup_fb);
|
||||
*/
|
||||
|
||||
/**
|
||||
* drm_gem_vram_simple_display_pipe_prepare_fb() - \
|
||||
* Implements &struct drm_simple_display_pipe_funcs.prepare_fb
|
||||
* drm_gem_vram_simple_display_pipe_prepare_fb() - Implements &struct
|
||||
* drm_simple_display_pipe_funcs.prepare_fb
|
||||
* @pipe: a simple display pipe
|
||||
* @new_state: the plane's new state
|
||||
*
|
||||
@ -739,8 +737,8 @@ int drm_gem_vram_simple_display_pipe_prepare_fb(
|
||||
EXPORT_SYMBOL(drm_gem_vram_simple_display_pipe_prepare_fb);
|
||||
|
||||
/**
|
||||
* drm_gem_vram_simple_display_pipe_cleanup_fb() - \
|
||||
* Implements &struct drm_simple_display_pipe_funcs.cleanup_fb
|
||||
* drm_gem_vram_simple_display_pipe_cleanup_fb() - Implements &struct
|
||||
* drm_simple_display_pipe_funcs.cleanup_fb
|
||||
* @pipe: a simple display pipe
|
||||
* @old_state: the plane's old state
|
||||
*
|
||||
@ -761,8 +759,7 @@ EXPORT_SYMBOL(drm_gem_vram_simple_display_pipe_cleanup_fb);
|
||||
*/
|
||||
|
||||
/**
|
||||
* drm_gem_vram_object_pin() - \
|
||||
Implements &struct drm_gem_object_funcs.pin
|
||||
* drm_gem_vram_object_pin() - Implements &struct drm_gem_object_funcs.pin
|
||||
* @gem: The GEM object to pin
|
||||
*
|
||||
* Returns:
|
||||
@ -785,8 +782,7 @@ static int drm_gem_vram_object_pin(struct drm_gem_object *gem)
|
||||
}
|
||||
|
||||
/**
|
||||
* drm_gem_vram_object_unpin() - \
|
||||
Implements &struct drm_gem_object_funcs.unpin
|
||||
* drm_gem_vram_object_unpin() - Implements &struct drm_gem_object_funcs.unpin
|
||||
* @gem: The GEM object to unpin
|
||||
*/
|
||||
static void drm_gem_vram_object_unpin(struct drm_gem_object *gem)
|
||||
|
@ -33,8 +33,8 @@ struct vm_area_struct;
|
||||
* struct drm_gem_vram_object - GEM object backed by VRAM
|
||||
* @bo: TTM buffer object
|
||||
* @map: Mapping information for @bo
|
||||
* @placement: TTM placement information. Supported placements are \
|
||||
%TTM_PL_VRAM and %TTM_PL_SYSTEM
|
||||
* @placement: TTM placement information. Supported placements are %TTM_PL_VRAM
|
||||
* and %TTM_PL_SYSTEM
|
||||
* @placements: TTM placement information.
|
||||
*
|
||||
* The type struct drm_gem_vram_object represents a GEM object that is
|
||||
@ -126,8 +126,8 @@ drm_gem_vram_plane_helper_cleanup_fb(struct drm_plane *plane,
|
||||
struct drm_plane_state *old_state);
|
||||
|
||||
/**
|
||||
* DRM_GEM_VRAM_PLANE_HELPER_FUNCS -
|
||||
* Initializes struct drm_plane_helper_funcs for VRAM handling
|
||||
* DRM_GEM_VRAM_PLANE_HELPER_FUNCS - Initializes struct drm_plane_helper_funcs
|
||||
* for VRAM handling
|
||||
*
|
||||
* Drivers may use GEM BOs as VRAM helpers for the framebuffer memory. This
|
||||
* macro initializes struct drm_plane_helper_funcs to use the respective helper
|
||||
@ -150,8 +150,8 @@ void drm_gem_vram_simple_display_pipe_cleanup_fb(
|
||||
struct drm_plane_state *old_state);
|
||||
|
||||
/**
|
||||
* define DRM_GEM_VRAM_DRIVER - default callback functions for \
|
||||
&struct drm_driver
|
||||
* define DRM_GEM_VRAM_DRIVER - default callback functions for
|
||||
* &struct drm_driver
|
||||
*
|
||||
* Drivers that use VRAM MM and GEM VRAM can use this macro to initialize
|
||||
* &struct drm_driver with default functions.
|
||||
@ -185,8 +185,8 @@ struct drm_vram_mm {
|
||||
};
|
||||
|
||||
/**
|
||||
* drm_vram_mm_of_bdev() - \
|
||||
Returns the container of type &struct ttm_device for field bdev.
|
||||
* drm_vram_mm_of_bdev() - Returns the container of type &struct ttm_device for
|
||||
* field bdev.
|
||||
* @bdev: the TTM BO device
|
||||
*
|
||||
* Returns:
|
||||
|
2427
scripts/kernel-doc
2427
scripts/kernel-doc
File diff suppressed because it is too large
Load Diff
@ -280,8 +280,6 @@ sub get_sphinx_version($)
|
||||
|
||||
sub check_sphinx()
|
||||
{
|
||||
my $default_version;
|
||||
|
||||
open IN, $conf or die "Can't open $conf";
|
||||
while (<IN>) {
|
||||
if (m/^\s*needs_sphinx\s*=\s*[\'\"]([\d\.]+)[\'\"]/) {
|
||||
@ -293,18 +291,7 @@ sub check_sphinx()
|
||||
|
||||
die "Can't get needs_sphinx version from $conf" if (!$min_version);
|
||||
|
||||
open IN, $requirement_file or die "Can't open $requirement_file";
|
||||
while (<IN>) {
|
||||
if (m/^\s*Sphinx\s*==\s*([\d\.]+)$/) {
|
||||
$default_version=$1;
|
||||
last;
|
||||
}
|
||||
}
|
||||
close IN;
|
||||
|
||||
die "Can't get default sphinx version from $requirement_file" if (!$default_version);
|
||||
|
||||
$virtenv_dir = $virtenv_prefix . $default_version;
|
||||
$virtenv_dir = $virtenv_prefix . "latest";
|
||||
|
||||
my $sphinx = get_sphinx_fname();
|
||||
if ($sphinx eq "") {
|
||||
@ -318,8 +305,8 @@ sub check_sphinx()
|
||||
die "$sphinx didn't return its version" if (!$cur_version);
|
||||
|
||||
if ($cur_version lt $min_version) {
|
||||
printf "ERROR: Sphinx version is %s. It should be >= %s (recommended >= %s)\n",
|
||||
$cur_version, $min_version, $default_version;
|
||||
printf "ERROR: Sphinx version is %s. It should be >= %s\n",
|
||||
$cur_version, $min_version;
|
||||
$need_sphinx = 1;
|
||||
return;
|
||||
}
|
||||
@ -361,6 +348,7 @@ sub give_debian_hints()
|
||||
{
|
||||
my %map = (
|
||||
"python-sphinx" => "python3-sphinx",
|
||||
"yaml" => "python3-yaml",
|
||||
"ensurepip" => "python3-venv",
|
||||
"virtualenv" => "virtualenv",
|
||||
"dot" => "graphviz",
|
||||
@ -395,6 +383,7 @@ sub give_redhat_hints()
|
||||
{
|
||||
my %map = (
|
||||
"python-sphinx" => "python3-sphinx",
|
||||
"yaml" => "python3-pyyaml",
|
||||
"virtualenv" => "python3-virtualenv",
|
||||
"dot" => "graphviz",
|
||||
"convert" => "ImageMagick",
|
||||
@ -421,6 +410,7 @@ sub give_redhat_hints()
|
||||
#
|
||||
my $old = 0;
|
||||
my $rel;
|
||||
my $noto_sans_redhat = "google-noto-sans-cjk-ttc-fonts";
|
||||
$rel = $1 if ($system_release =~ /release\s+(\d+)/);
|
||||
|
||||
if (!($system_release =~ /Fedora/)) {
|
||||
@ -438,6 +428,9 @@ sub give_redhat_hints()
|
||||
if ($rel && $rel < 26) {
|
||||
$old = 1;
|
||||
}
|
||||
if ($rel && $rel >= 38) {
|
||||
$noto_sans_redhat = "google-noto-sans-cjk-fonts";
|
||||
}
|
||||
}
|
||||
if (!$rel) {
|
||||
printf("Couldn't identify release number\n");
|
||||
@ -446,8 +439,9 @@ sub give_redhat_hints()
|
||||
}
|
||||
|
||||
if ($pdf) {
|
||||
check_missing_file(["/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc"],
|
||||
"google-noto-sans-cjk-ttc-fonts", 2);
|
||||
check_missing_file(["/usr/share/fonts/google-noto-cjk/NotoSansCJK-Regular.ttc",
|
||||
"/usr/share/fonts/google-noto-sans-cjk-fonts/NotoSansCJK-Regular.ttc"],
|
||||
$noto_sans_redhat, 2);
|
||||
}
|
||||
|
||||
check_rpm_missing(\@fedora26_opt_pkgs, 2) if ($pdf && !$old);
|
||||
@ -472,6 +466,7 @@ sub give_opensuse_hints()
|
||||
{
|
||||
my %map = (
|
||||
"python-sphinx" => "python3-sphinx",
|
||||
"yaml" => "python3-pyyaml",
|
||||
"virtualenv" => "python3-virtualenv",
|
||||
"dot" => "graphviz",
|
||||
"convert" => "ImageMagick",
|
||||
@ -951,6 +946,7 @@ sub check_needs()
|
||||
|
||||
# Check for needed programs/tools
|
||||
check_perl_module("Pod::Usage", 0);
|
||||
check_python_module("yaml", 0);
|
||||
check_program("make", 0);
|
||||
check_program("gcc", 0);
|
||||
check_program("dot", 1);
|
||||
|
Loading…
Reference in New Issue
Block a user