diff options
| author | Stephen Rothwell <sfr@canb.auug.org.au> | 2022-06-28 10:43:09 +1000 |
|---|---|---|
| committer | Stephen Rothwell <sfr@canb.auug.org.au> | 2022-06-28 10:43:09 +1000 |
| commit | 8d6308e68cd854eb7bfdbd0133c62f3e84f4826c (patch) | |
| tree | f8b2adb5e716cc1f0d29039a92108124086655e9 | |
| parent | f8f47cbbd9d87668dec05557ee7672f1c6e5546e (diff) | |
| parent | 5372de4e4545dead978c53d9c03b7aee12d0f94d (diff) | |
| download | linux-next-8d6308e68cd854eb7bfdbd0133c62f3e84f4826c.tar.gz | |
Merge branch 'docs-next' of git://git.lwn.net/linux.git
33 files changed, 1167 insertions, 512 deletions
diff --git a/Documentation/Kconfig b/Documentation/Kconfig index e549a61f4d966..252bfc164dbdc 100644 --- a/Documentation/Kconfig +++ b/Documentation/Kconfig @@ -1,23 +1,22 @@ config WARN_MISSING_DOCUMENTS - bool "Warn if there's a missing documentation file" depends on COMPILE_TEST help - It is not uncommon that a document gets renamed. - This option makes the Kernel to check for missing dependencies, - warning when something is missing. Works only if the Kernel - is built from a git tree. + It is not uncommon that a document gets renamed. + This option makes the Kernel to check for missing dependencies, + warning when something is missing. Works only if the Kernel + is built from a git tree. - If unsure, select 'N'. + If unsure, select 'N'. config WARN_ABI_ERRORS bool "Warn if there are errors at ABI files" depends on COMPILE_TEST help - The files under Documentation/ABI should follow what's - described at Documentation/ABI/README. Yet, as they're manually - written, it would be possible that some of those files would - have errors that would break them for being parsed by - scripts/get_abi.pl. Add a check to verify them. + The files under Documentation/ABI should follow what's + described at Documentation/ABI/README. Yet, as they're manually + written, it would be possible that some of those files would + have errors that would break them for being parsed by + scripts/get_abi.pl. Add a check to verify them. - If unsure, select 'N'. + If unsure, select 'N'. diff --git a/Documentation/admin-guide/sysctl/kernel.rst b/Documentation/admin-guide/sysctl/kernel.rst index ddccd10774623..8ab042beeb765 100644 --- a/Documentation/admin-guide/sysctl/kernel.rst +++ b/Documentation/admin-guide/sysctl/kernel.rst @@ -38,8 +38,8 @@ acct If BSD-style process accounting is enabled these values control its behaviour. If free space on filesystem where the log lives -goes below ``lowwater``% accounting suspends. If free space gets -above ``highwater``% accounting resumes. ``frequency`` determines +goes below ``lowwater``\ % accounting suspends. If free space gets +above ``highwater``\ % accounting resumes. ``frequency`` determines how often do we check the amount of free space (value is in seconds). Default: diff --git a/Documentation/arm64/elf_hwcaps.rst b/Documentation/arm64/elf_hwcaps.rst index 3d116fb536c53..37f0116c39fa8 100644 --- a/Documentation/arm64/elf_hwcaps.rst +++ b/Documentation/arm64/elf_hwcaps.rst @@ -171,96 +171,73 @@ HWCAP_PACG Documentation/arm64/pointer-authentication.rst. HWCAP2_DCPODP - Functionality implied by ID_AA64ISAR1_EL1.DPB == 0b0010. HWCAP2_SVE2 - Functionality implied by ID_AA64ZFR0_EL1.SVEVer == 0b0001. HWCAP2_SVEAES - Functionality implied by ID_AA64ZFR0_EL1.AES == 0b0001. HWCAP2_SVEPMULL - Functionality implied by ID_AA64ZFR0_EL1.AES == 0b0010. HWCAP2_SVEBITPERM - Functionality implied by ID_AA64ZFR0_EL1.BitPerm == 0b0001. HWCAP2_SVESHA3 - Functionality implied by ID_AA64ZFR0_EL1.SHA3 == 0b0001. HWCAP2_SVESM4 - Functionality implied by ID_AA64ZFR0_EL1.SM4 == 0b0001. HWCAP2_FLAGM2 - Functionality implied by ID_AA64ISAR0_EL1.TS == 0b0010. HWCAP2_FRINT - Functionality implied by ID_AA64ISAR1_EL1.FRINTTS == 0b0001. HWCAP2_SVEI8MM - Functionality implied by ID_AA64ZFR0_EL1.I8MM == 0b0001. HWCAP2_SVEF32MM - Functionality implied by ID_AA64ZFR0_EL1.F32MM == 0b0001. HWCAP2_SVEF64MM - Functionality implied by ID_AA64ZFR0_EL1.F64MM == 0b0001. HWCAP2_SVEBF16 - Functionality implied by ID_AA64ZFR0_EL1.BF16 == 0b0001. HWCAP2_I8MM - Functionality implied by ID_AA64ISAR1_EL1.I8MM == 0b0001. HWCAP2_BF16 - Functionality implied by ID_AA64ISAR1_EL1.BF16 == 0b0001. HWCAP2_DGH - Functionality implied by ID_AA64ISAR1_EL1.DGH == 0b0001. HWCAP2_RNG - Functionality implied by ID_AA64ISAR0_EL1.RNDR == 0b0001. HWCAP2_BTI - Functionality implied by ID_AA64PFR0_EL1.BT == 0b0001. HWCAP2_MTE - Functionality implied by ID_AA64PFR1_EL1.MTE == 0b0010, as described by Documentation/arm64/memory-tagging-extension.rst. HWCAP2_ECV - Functionality implied by ID_AA64MMFR0_EL1.ECV == 0b0001. HWCAP2_AFP - Functionality implied by ID_AA64MFR1_EL1.AFP == 0b0001. HWCAP2_RPRES - Functionality implied by ID_AA64ISAR2_EL1.RPRES == 0b0001. HWCAP2_MTE3 - Functionality implied by ID_AA64PFR1_EL1.MTE == 0b0011, as described by Documentation/arm64/memory-tagging-extension.rst. diff --git a/Documentation/dev-tools/coccinelle.rst b/Documentation/dev-tools/coccinelle.rst index 9c454de5a7f74..d9976069ed126 100644 --- a/Documentation/dev-tools/coccinelle.rst +++ b/Documentation/dev-tools/coccinelle.rst @@ -66,7 +66,7 @@ The wiki documentation always refers to the linux-next version of the script. For Semantic Patch Language(SmPL) grammar documentation refer to: -http://coccinelle.lip6.fr/documentation.php +https://coccinelle.gitlabpages.inria.fr/website/docs/main_grammar.html Using Coccinelle on the Linux kernel ------------------------------------ diff --git a/Documentation/dev-tools/kselftest.rst b/Documentation/dev-tools/kselftest.rst index a833ecf12fbc1..ee6467ca8293f 100644 --- a/Documentation/dev-tools/kselftest.rst +++ b/Documentation/dev-tools/kselftest.rst @@ -208,6 +208,14 @@ In general, the rules for selftests are Contributing new tests (details) ================================ + * In your Makefile, use facilities from lib.mk by including it instead of + reinventing the wheel. Specify flags and binaries generation flags on + need basis before including lib.mk. :: + + CFLAGS = $(KHDR_INCLUDES) + TEST_GEN_PROGS := close_range_test + include ../lib.mk + * Use TEST_GEN_XXX if such binaries or files are generated during compiling. @@ -230,13 +238,30 @@ Contributing new tests (details) * First use the headers inside the kernel source and/or git repo, and then the system headers. Headers for the kernel release as opposed to headers installed by the distro on the system should be the primary focus to be able - to find regressions. + to find regressions. Use KHDR_INCLUDES in Makefile to include headers from + the kernel source. * If a test needs specific kernel config options enabled, add a config file in the test directory to enable them. e.g: tools/testing/selftests/android/config + * Create a .gitignore file inside test directory and add all generated objects + in it. + + * Add new test name in TARGETS in selftests/Makefile:: + + TARGETS += android + + * All changes should pass:: + + kselftest-{all,install,clean,gen_tar} + kselftest-{all,install,clean,gen_tar} O=abo_path + kselftest-{all,install,clean,gen_tar} O=rel_path + make -C tools/testing/selftests {all,install,clean,gen_tar} + make -C tools/testing/selftests {all,install,clean,gen_tar} O=abs_path + make -C tools/testing/selftests {all,install,clean,gen_tar} O=rel_path + Test Module =========== diff --git a/Documentation/doc-guide/kernel-doc.rst b/Documentation/doc-guide/kernel-doc.rst index a7cb2afd79900..9c779bd7a751d 100644 --- a/Documentation/doc-guide/kernel-doc.rst +++ b/Documentation/doc-guide/kernel-doc.rst @@ -1,3 +1,5 @@ +.. title:: Kernel-doc comments + =========================== Writing kernel-doc comments =========================== diff --git a/Documentation/doc-guide/sphinx.rst b/Documentation/doc-guide/sphinx.rst index 2ff1ab4158d48..edc4fa023986a 100644 --- a/Documentation/doc-guide/sphinx.rst +++ b/Documentation/doc-guide/sphinx.rst @@ -132,7 +132,8 @@ format-specific subdirectories under ``Documentation/output``. To generate documentation, Sphinx (``sphinx-build``) must obviously be installed. For prettier HTML output, the Read the Docs Sphinx theme (``sphinx_rtd_theme``) is used if available. For PDF output you'll also need -``XeLaTeX`` and ``convert(1)`` from ImageMagick (https://www.imagemagick.org). +``XeLaTeX`` and ``convert(1)`` from ImageMagick +(https://www.imagemagick.org).\ [#ink]_ All of these are widely available and packaged in distributions. To pass extra options to Sphinx, you can use the ``SPHINXOPTS`` make @@ -152,6 +153,10 @@ The Sphinx theme can be overridden by using the ``DOCS_THEME`` make variable. To remove the generated documentation, run ``make cleandocs``. +.. [#ink] Having ``inkscape(1)`` from Inkscape (https://inkscape.org) + as well would improve the quality of images embedded in PDF + documents, especially for kernel releases 5.18 and later. + Writing Documentation ===================== diff --git a/Documentation/driver-api/gpio/consumer.rst b/Documentation/driver-api/gpio/consumer.rst index 72bcf5f5e3a21..de6fc79ad6f03 100644 --- a/Documentation/driver-api/gpio/consumer.rst +++ b/Documentation/driver-api/gpio/consumer.rst @@ -114,7 +114,7 @@ For a function using multiple GPIOs all of those can be obtained with one call:: This function returns a struct gpio_descs which contains an array of descriptors. It also contains a pointer to a gpiolib private structure which, -if passed back to get/set array functions, may speed up I/O proocessing:: +if passed back to get/set array functions, may speed up I/O processing:: struct gpio_descs { struct gpio_array *info; diff --git a/Documentation/driver-api/gpio/driver.rst b/Documentation/driver-api/gpio/driver.rst index 70ff43ac4fcc1..6baaeab795344 100644 --- a/Documentation/driver-api/gpio/driver.rst +++ b/Documentation/driver-api/gpio/driver.rst @@ -119,7 +119,7 @@ GPIO lines with debounce support Debouncing is a configuration set to a pin indicating that it is connected to a mechanical switch or button, or similar that may bounce. Bouncing means the line is pulled high/low quickly at very short intervals for mechanical -reasons. This can result in the value being unstable or irqs fireing repeatedly +reasons. This can result in the value being unstable or irqs firing repeatedly unless the line is debounced. Debouncing in practice involves setting up a timer when something happens on @@ -219,7 +219,7 @@ use a trick: when a line is set as output, if the line is flagged as open drain, and the IN output value is low, it will be driven low as usual. But if the IN output value is set to high, it will instead *NOT* be driven high, instead it will be switched to input, as input mode is high impedance, thus -achieveing an "open drain emulation" of sorts: electrically the behaviour will +achieving an "open drain emulation" of sorts: electrically the behaviour will be identical, with the exception of possible hardware glitches when switching the mode of the line. @@ -642,7 +642,7 @@ In this case the typical set-up will look like this: As you can see pretty similar, but you do not supply a parent handler for the IRQ, instead a parent irqdomain, an fwnode for the hardware and -a funcion .child_to_parent_hwirq() that has the purpose of looking up +a function .child_to_parent_hwirq() that has the purpose of looking up the parent hardware irq from a child (i.e. this gpio chip) hardware irq. As always it is good to look at examples in the kernel tree for advice on how to find the required pieces. diff --git a/Documentation/driver-api/gpio/using-gpio.rst b/Documentation/driver-api/gpio/using-gpio.rst index 64c8d3f76c3a3..894d88855d73b 100644 --- a/Documentation/driver-api/gpio/using-gpio.rst +++ b/Documentation/driver-api/gpio/using-gpio.rst @@ -44,7 +44,7 @@ These devices will appear on the system as ``/dev/gpiochip0`` thru found in the kernel tree ``tools/gpio`` subdirectory. For structured and managed applications, we recommend that you make use of the -libgpiod_ library. This provides helper abstractions, command line utlities +libgpiod_ library. This provides helper abstractions, command line utilities and arbitration for multiple simultaneous consumers on the same GPIO chip. .. _libgpiod: https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git/ diff --git a/Documentation/filesystems/f2fs.rst b/Documentation/filesystems/f2fs.rst index 2965601e21bbe..d0c09663dae85 100644 --- a/Documentation/filesystems/f2fs.rst +++ b/Documentation/filesystems/f2fs.rst @@ -823,10 +823,11 @@ Compression implementation Instead, the main goal is to reduce data writes to flash disk as much as possible, resulting in extending disk life time as well as relaxing IO congestion. Alternatively, we've added ioctl(F2FS_IOC_RELEASE_COMPRESS_BLOCKS) - interface to reclaim compressed space and show it to user after putting the - immutable bit. Immutable bit, after release, it doesn't allow writing/mmaping - on the file, until reserving compressed space via - ioctl(F2FS_IOC_RESERVE_COMPRESS_BLOCKS) or truncating filesize to zero. + interface to reclaim compressed space and show it to user after setting a + special flag to the inode. Once the compressed space is released, the flag + will block writing data to the file until either the compressed space is + reserved via ioctl(F2FS_IOC_RESERVE_COMPRESS_BLOCKS) or the file size is + truncated to zero. Compress metadata layout:: @@ -835,12 +836,12 @@ Compress metadata layout:: | cluster 1 | cluster 2 | ......... | cluster N | +-----------------------------------------------+ . . . . - . . . . + . . . . . Compressed Cluster . . Normal Cluster . +----------+---------+---------+---------+ +---------+---------+---------+---------+ |compr flag| block 1 | block 2 | block 3 | | block 1 | block 2 | block 3 | block 4 | +----------+---------+---------+---------+ +---------+---------+---------+---------+ - . . + . . . . . . +-------------+-------------+----------+----------------------------+ diff --git a/Documentation/filesystems/overlayfs.rst b/Documentation/filesystems/overlayfs.rst index 7da6c30ed596a..4c76fda076454 100644 --- a/Documentation/filesystems/overlayfs.rst +++ b/Documentation/filesystems/overlayfs.rst @@ -607,7 +607,7 @@ can be removed. User xattr ---------- -The the "-o userxattr" mount option forces overlayfs to use the +The "-o userxattr" mount option forces overlayfs to use the "user.overlay." xattr namespace instead of "trusted.overlay.". This is useful for unprivileged mounting of overlayfs. diff --git a/Documentation/translations/zh_CN/admin-guide/reporting-issues.rst b/Documentation/translations/zh_CN/admin-guide/reporting-issues.rst index 6b4988da2c5a8..59e51e3539b43 100644 --- a/Documentation/translations/zh_CN/admin-guide/reporting-issues.rst +++ b/Documentation/translations/zh_CN/admin-guide/reporting-issues.rst @@ -1,13 +1,6 @@ .. SPDX-License-Identifier: (GPL-2.0+ OR CC-BY-4.0) -.. - If you want to distribute this text under CC-BY-4.0 only, please use 'The - Linux kernel developers' for author attribution and link this as source: - https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/Documentation/admin-guide/reporting-issues.rst -.. - Note: Only the content of this RST file as found in the Linux kernel sources - is available under CC-BY-4.0, as versions of this text that were processed - (for example by the kernel's build system) might contain content taken from - files which use a more restrictive license. +.. See the bottom of this file for additional redistribution information. + .. include:: ../disclaimer-zh_CN.rst @@ -29,7 +22,9 @@ 请搜索 `LKML内核邮件列表 <https://lore.kernel.org/lkml/>`_ 和 `Linux稳定版邮件列表 <https://lore.kernel.org/stable/>`_ 存档中匹配的报告并 加入讨论。如果找不到匹配的报告,请安装该系列的最新版本。如果它仍然出现问题, -报告给稳定版邮件列表(stable@vger.kernel.org)。 +请报告给稳定版邮件列表(stable@vger.kernel.org)并抄送回归邮件列表 +(regressions@lists.linux.dev);理想情况下,还可以抄送维护者和相关子系统的 +邮件列表。 在所有其他情况下,请尽可能猜测是哪个内核部分导致了问题。查看MAINTAINERS文件, 了解开发人员希望如何得知问题,大多数情况下,报告问题都是通过电子邮件和抄送 @@ -46,9 +41,10 @@ 有使用附加模块)。还要确保它是在一个正常的环境中构建和运行,并且在问题发生 之前没有被污染(tainted)。 -在编写报告时,要涵盖与问题相关的所有信息,如使用的内核和发行版。在碰见回归时, -尝试给出引入它的更改的提交ID,二分可以找到它。如果您同时面临Linux内核的多个 -问题,请分别报告每个问题。 +当你同时面临Linux内核的多个问题时,请分别报告。在编写报告时,要涵盖与问题 +相关的所有信息,如使用的内核和发行版。如果碰见回归,请把报告抄送回归邮件列表 +(regressions@lists.linux.dev)。也请试试用二分法找出源头;如果成功找到,请 +在报告中写上它的提交ID并抄送sign-off-by链中的所有人。 一旦报告发出,请回答任何出现的问题,并尽可能地提供帮助。这包括通过不时重新 测试新版本并发送状态更新来推动进展。 @@ -156,9 +152,10 @@ 存在问题,因为问题可能已经在那里被修复了。如果您第一次发现供应商内核的问题, 请检查已知最新版本的普通构建是否可以正常运行。 - * 向Linux稳定版邮件列表发送一个简短的问题报告(stable@vger.kernel.org)。大致 - 描述问题,并解释如何复现。讲清楚首个出现问题的版本和最后一个工作正常的版本。 - 然后等待进一步的指示。 + * 向Linux稳定版邮件列表发送一个简短的问题报告(stable@vger.kernel.org)并抄送 + Linux回归邮件列表(regressions@lists.linux.dev);如果你怀疑是由某子系统 + 引起的,请抄送其维护人员和子系统邮件列表。大致描述问题,并解释如何复现。 + 讲清楚首个出现问题的版本和最后一个工作正常的版本。然后等待进一步的指示。 下面的参考章节部分详细解释了这些步骤中的每一步。 @@ -296,17 +293,14 @@ Linus Torvalds和主要的Linux内核开发人员希望看到一些问题尽快� 报告过程中有一些“高优先级问题”的处理略有不同。有三种情况符合条件:回归、安全 问题和非常严重的问题。 -如果在旧版本的Linux内核中工作的东西不能在新版本的Linux内核中工作,或者某种 -程度上在新版本的Linux内核中工作得更差,那么你就需要处理“回归”。因此,当一个 -在Linux 5.7中表现良好的WiFi驱动程序在5.8中表现不佳或根本不能工作时,这是一 -种回归。如果应用程序在新的内核中出现不稳定的现象,这也是一种回归,这可能是 -由于内核和用户空间之间的接口(如procfs和sysfs)发生不兼容的更改造成的。显著 -的性能降低或功耗增加也可以称为回归。但是请记住:新内核需要使用与旧内核相似的 -配置来构建(参见下面如何实现这一点)。这是因为内核开发人员在实现新特性时有 -时无法避免不兼容性;但是为了避免回归,这些特性必须在构建配置期间显式地启用。 +如果某个应用程序或实际用例在原先的Linux内核上运行良好,但在使用类似配置编译的 +较新版本上效果更差、或者根本不能用,那么你就需要处理回归问题。 +Documentation/admin-guide/reporting-regressions.rst 对此进行了更详细的解释。 +它还提供了很多你可能想知道的关于回归的其他信息;例如,它解释了如何将您的问题 +添加到回归跟踪列表中,以确保它不会被忽略。 什么是安全问题留给您自己判断。在继续之前,请考虑阅读 -“Documentation/translations/zh_CN/admin-guide/security-bugs.rst”, +Documentation/translations/zh_CN/admin-guide/security-bugs.rst , 因为它提供了如何最恰当地处理安全问题的额外细节。 当发生了完全无法接受的糟糕事情时,此问题就是一个“非常严重的问题”。例如, @@ -390,7 +384,7 @@ Linux内核破坏了它处理的数据或损坏了它运行的硬件。当内核 核未被污染,那么它应该以“Not infected”结束;如果你看到“Tainted:”且后跟一些 空格和字母,那就被污染了。 -如果你的内核被污染了,请阅读“Documentation/translations/zh_CN/admin-guide/tainted-kernels.rst” +如果你的内核被污染了,请阅读 Documentation/translations/zh_CN/admin-guide/tainted-kernels.rst 以找出原因。设法消除污染因素。通常是由以下三种因素之一引起的: 1. 发生了一个可恢复的错误(“kernel Oops”),内核污染了自己,因为内核知道在 @@ -591,7 +585,8 @@ ath10k@lists.infradead.org”,将引导您到ath10k邮件列表的信息页, 搜索引擎,并添加类似“site:lists.infadead.org/pipermail/ath10k/”这 样的搜索条件,这会把结果限制在该链接中的档案。 -也请进一步搜索网络、LKML和bugzilla.kernel.org网站。 +也请进一步搜索网络、LKML和bugzilla.kernel.org网站。如果你的报告需要发送到缺陷 +跟踪器中,那么您可能还需要检查子系统的邮件列表存档,因为可能有人只在那里报告了它。 有关如何搜索以及在找到匹配报告时如何操作的详细信息,请参阅上面的“搜索现有报告 (第一部分)”。 @@ -802,10 +797,10 @@ Linux 首席开发者 Linus Torvalds 认为 Linux 内核永远不应恶化,这 重现它。 有一个叫做“二分”的过程可以来寻找变化,这在 -“Documentation/translations/zh_CN/admin-guide/bug-bisect.rst”文档中进行了详细 +Documentation/translations/zh_CN/admin-guide/bug-bisect.rst 文档中进行了详细 的描述,这个过程通常需要你构建十到二十个内核镜像,每次都尝试在构建下一个镜像 之前重现问题。是的,这需要花费一些时间,但不用担心,它比大多数人想象的要快得多。 -多亏了“binary search二进制搜索”,这将引导你在源代码管理系统中找到导致回归的提交。 +多亏了“binary search二分搜索”,这将引导你在源代码管理系统中找到导致回归的提交。 一旦你找到它,就在网上搜索其主题、提交ID和缩短的提交ID(提交ID的前12个字符)。 如果有的话,这将引导您找到关于它的现有报告。 @@ -823,10 +818,10 @@ Linux 首席开发者 Linus Torvalds 认为 Linux 内核永远不应恶化,这 当处理回归问题时,请确保你所面临的问题真的是由内核引起的,而不是由其他东西 引起的,如上文所述。 -在整个过程中,请记住:只有当旧内核和新内核的配置相似时,问题才算回归。最好 -的方法是:把配置文件(``.config``)从旧的工作内核直接复制到你尝试的每个新内 -核版本。之后运行 ``make oldnoconfig`` 来调整它以适应新版本的需要,而不启用 -任何新的功能,因为那些功能也可能导致回归。 +在整个过程中,请记住:只有当旧内核和新内核的配置相似时,问题才算回归。这可以 +通过 ``make olddefconfig`` 来实现,详细解释参见 +Documentation/admin-guide/reporting-regressions.rst ;它还提供了大量其他您 +可能希望了解的有关回归的信息。 撰写并发送报告 @@ -959,11 +954,19 @@ Linux 首席开发者 Linus Torvalds 认为 Linux 内核永远不应恶化,这 **非常严重的缺陷** :确保在主题或工单标题以及第一段中明显标出 severeness (非常严重的)。 -**回归** :如果问题是一个回归,请在邮件的主题或缺陷跟踪器的标题中添加 -[REGRESSION]。如果您没有进行二分,请至少注明您测试的最新主线版本(比如 5.7) -和出现问题的最新版本(比如 5.8)。如果您成功地进行了二分,请注明导致回归 -的提交ID和主题。也请添加该变更的作者到你的报告中;如果您需要将您的缺陷提交 -到缺陷跟踪器中,请将报告以私人邮件的形式转发给他,并注明报告提交地点。 +**回归** :报告的主题应以“[REGRESSION]”开头。 + +如果您成功用二分法定位了问题,请使用引入回归之更改的标题作为主题的第二部分。 +请在报告中写明“罪魁祸首”的提交ID。如果未能成功二分,请在报告中讲明最后一个 +正常工作的版本(例如5.7)和最先发生问题的版本(例如5.8-rc1)。 + +通过邮件发送报告时,请抄送Linux回归邮件列表(regressions@lists.linux.dev)。 +如果报告需要提交到某个web追踪器,请继续提交;并在提交后,通过邮件将报告转发 +至回归列表;抄送相关子系统的维护人员和邮件列表。请确保报告是内联转发的,不要 +把它作为附件。另外请在顶部添加一个简短的说明,在那里写上工单的网址。 + +在邮寄或转发报告时,如果成功二分,需要将“罪魁祸首”的作者添加到收件人中;同时 +抄送signed-off-by链中的每个人,您可以在提交消息的末尾找到。 **安全问题** :对于这种问题,你将必须评估:如果细节被公开披露,是否会对其他 用户产生短期风险。如果不会,只需按照所述继续报告问题。如果有此风险,你需要 @@ -980,7 +983,7 @@ Linux 首席开发者 Linus Torvalds 认为 Linux 内核永远不应恶化,这 报告,请将报告的文本转发到这些地址;但请在报告的顶部加上注释,表明您提交了 报告,并附上工单链接。 -更多信息请参见“Documentation/translations/zh_CN/admin-guide/security-bugs.rst”。 +更多信息请参见 Documentation/translations/zh_CN/admin-guide/security-bugs.rst 。 发布报告后的责任 @@ -1173,14 +1176,18 @@ FLOSS 问题报告的人看,询问他们的意见。同时征求他们关于� 报告回归 ~~~~~~~~~~ - *向Linux稳定版邮件列表发送一个简短的问题报告(stable@vger.kernel.org)。 - 大致描述问题,并解释如何复现。讲清楚首个出现问题的版本和最后一个工作正常 - 的版本。然后等待进一步的指示。* + *向Linux稳定版邮件列表发送一个简短的问题报告(stable@vger.kernel.org)并 + 抄送Linux回归邮件列表(regressions@lists.linux.dev);如果你怀疑是由某 + 子系统引起的,请抄送其维护人员和子系统邮件列表。大致描述问题,并解释如 + 何复现。讲清楚首个出现问题的版本和最后一个工作正常的版本。然后等待进一 + 步的指示。* 当报告在稳定版或长期支持内核线内发生的回归(例如在从5.10.4更新到5.10.5时), -一份简短的报告足以快速报告问题。因此只需要粗略的描述。 +一份简短的报告足以快速报告问题。因此只需向稳定版和回归邮件列表发送粗略的描述; +不过如果你怀疑某子系统导致此问题的话,请一并抄送其维护人员和子系统邮件列表, +这会加快进程。 -但是请注意,如果您能够指明引入问题的确切版本,这将对开发人员有很大帮助。因此 +请注意,如果您能够指明引入问题的确切版本,这将对开发人员有很大帮助。因此 如果有时间的话,请尝试使用普通内核找到该版本。让我们假设发行版发布Linux内核 5.10.5到5.10.8的更新时发生了故障。那么按照上面的指示,去检查该版本线中的最新 内核,比如5.10.9。如果问题出现,请尝试普通5.10.5,以确保供应商应用的补丁不会 @@ -1190,7 +1197,9 @@ FLOSS 问题报告的人看,询问他们的意见。同时征求他们关于� 前一段基本粗略地概述了“二分”方法。一旦报告出来,您可能会被要求做一个正确的 报告,因为它允许精确地定位导致问题的确切更改(然后很容易被恢复以快速修复问题)。 因此如果时间允许,考虑立即进行适当的二分。有关如何详细信息,请参阅“对回归的 -特别关照”部分和文档“Documentation/translations/zh_CN/admin-guide/bug-bisect.rst”。 +特别关照”部分和文档 Documentation/translations/zh_CN/admin-guide/bug-bisect.rst 。 +如果成功二分的话,请将“罪魁祸首”的作者添加到收件人中;同时抄送所有在 +signed-off-by链中的人,您可以在提交消息的末尾找到。 “报告仅在旧内核版本线中发生的问题”的参考 @@ -1207,7 +1216,7 @@ FLOSS 问题报告的人看,询问他们的意见。同时征求他们关于� 即使是微小的、看似明显的代码变化,有时也会带来新的、完全意想不到的问题。稳 定版和长期支持内核的维护者非常清楚这一点,因此他们只对这些内核进行符合 -“Documentation/translations/zh_CN/process/stable-kernel-rules.rst”中所列出的 +Documentation/translations/zh_CN/process/stable-kernel-rules.rst 中所列出的 规则的修改。 复杂或有风险的修改不符合条件,因此只能应用于主线。其他的修复很容易被回溯到 @@ -1333,3 +1342,27 @@ FLOSS 问题报告的人看,询问他们的意见。同时征求他们关于� 向 Linux 内核开发者报告问题是很难的:这个文档的长度和复杂性以及字里行间的内 涵都说明了这一点。但目前就是这样了。这篇文字的主要作者希望通过记录现状来为 以后改善这种状况打下一些基础。 + + +.. + end-of-content +.. + This English version of this document is maintained by Thorsten Leemhuis + <linux@leemhuis.info>. If you spot a typo or small mistake, feel free to + let him know directly and he'll fix it. For translation problems, please + contact with translators. You are free to do the same in a mostly informal + way if you want to contribute changes to the text, but for copyright + reasons please CC linux-doc@vger.kernel.org and "sign-off" your + contribution as Documentation/process/submitting-patches.rst outlines in + the section "Sign your work - the Developer's Certificate of Origin". +.. + This text is available under GPL-2.0+ or CC-BY-4.0, as stated at the top + of the file. If you want to distribute this text under CC-BY-4.0 only, + please use "The Linux kernel developers" for author attribution and link + this as source: + https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/Documentation/admin-guide/reporting-issues.rst +.. + Note: Only the content of this RST file as found in the Linux kernel sources + is available under CC-BY-4.0, as versions of this text that were processed + (for example by the kernel's build system) might contain content taken from + files which use a more restrictive license. diff --git a/Documentation/translations/zh_CN/core-api/cachetlb.rst b/Documentation/translations/zh_CN/core-api/cachetlb.rst index 6fee45fe5e807..b4a76ec75daa5 100644 --- a/Documentation/translations/zh_CN/core-api/cachetlb.rst +++ b/Documentation/translations/zh_CN/core-api/cachetlb.rst @@ -5,6 +5,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> :校译: @@ -278,6 +279,11 @@ HyperSparc cpu就是这样一个具有这种属性的cpu。 CPU上,因为它将cpu存储到页面上,使其变脏。同样,请看 sparc64关于如何处理这个问题的例子。 + ``void flush_dcache_folio(struct folio *folio)`` + + 该函数的调用情形与flush_dcache_page()相同。它允许架构针对刷新整个 + folio页面进行优化,而不是一次刷新一页。 + ``void copy_to_user_page(struct vm_area_struct *vma, struct page *page, unsigned long user_vaddr, void *dst, void *src, int len)`` ``void copy_from_user_page(struct vm_area_struct *vma, struct page *page, diff --git a/Documentation/translations/zh_CN/core-api/cpu_hotplug.rst b/Documentation/translations/zh_CN/core-api/cpu_hotplug.rst index 85a264287426a..4772a900c37a0 100644 --- a/Documentation/translations/zh_CN/core-api/cpu_hotplug.rst +++ b/Documentation/translations/zh_CN/core-api/cpu_hotplug.rst @@ -4,6 +4,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> :校译: @@ -15,12 +16,13 @@ 内核中的CPU热拔插 ================= -:时间: 2016年12月 +:时间: 2021年9月 :作者: Sebastian Andrzej Siewior <bigeasy@linutronix.de>, - Rusty Russell <rusty@rustcorp.com.au>, - Srivatsa Vaddagiri <vatsa@in.ibm.com>, - Ashok Raj <ashok.raj@intel.com>, - Joel Schopp <jschopp@austin.ibm.com> + Rusty Russell <rusty@rustcorp.com.au>, + Srivatsa Vaddagiri <vatsa@in.ibm.com>, + Ashok Raj <ashok.raj@intel.com>, + Joel Schopp <jschopp@austin.ibm.com>, + Thomas Gleixner <tglx@linutronix.de> 简介 ==== @@ -139,7 +141,7 @@ CPU的热拔插协作 下线情况 -------- -一旦CPU被逻辑关闭,注册的热插拔状态的清除回调将被调用,从 ``CPUHP_ONLINE`` 开始,在 +一旦CPU被逻辑关闭,注册的热插拔状态的清除回调将被调用,从 ``CPUHP_ONLINE`` 开始,到 ``CPUHP_OFFLINE`` 状态结束。这包括: * 如果任务因暂停操作而被冻结,那么 *cpuhp_tasks_frozen* 将被设置为true。 @@ -154,82 +156,399 @@ CPU的热拔插协作 * 一旦所有的服务被迁移,内核会调用一个特定的例程 ``__cpu_disable()`` 来进行特定的清 理。 -使用热插拔API -------------- +CPU热插拔API +============ + +CPU热拔插状态机 +--------------- + +CPU热插拔使用一个从CPUHP_OFFLINE到CPUHP_ONLINE的线性状态空间的普通状态机。每个状态都 +有一个startup和teardown的回调。 + +当一个CPU上线时,将按顺序调用startup回调,直到达到CPUHP_ONLINE状态。当设置状态的回调 +或将实例添加到多实例状态时,也可以调用它们。 + +当一个CPU下线时,将按相反的顺序依次调用teardown回调,直到达到CPUHP_OFFLINE状态。当删 +除状态的回调或从多实例状态中删除实例时,也可以调用它们。 + +如果某个使用场景只需要一个方向的热插拔操作回调(CPU上线或CPU下线),则在设置状态时, +可以将另一个不需要的回调设置为NULL。 + +状态空间被划分成三个阶段: + +* PREPARE阶段 + + PREPARE阶段涵盖了从CPUHP_OFFLINE到CPUHP_BRINGUP_CPU之间的状态空间。 + + 在该阶段中,startup回调在CPU上线操作启动CPU之前被调用,teardown回调在CPU下线操作使 + CPU功能失效之后被调用。 + + 这些回调是在控制CPU上调用的,因为它们显然不能在热插拔的CPU上运行,此时热插拔的CPU要 + 么还没有启动,要么已经功能失效。 + + startup回调用于设置CPU成功上线所需要的资源。teardown回调用于释放资源或在热插拔的CPU + 功能失效后,将待处理的工作转移到在线的CPU上。 + + 允许startup回调失败。如果回调失败,CPU上线操作被中止,CPU将再次被降到之前的状态(通 + 常是CPUHP_OFFLINE)。 + + 本阶段中的teardown回调不允许失败。 + +* STARTING阶段 + + STARTING阶段涵盖了CPUHP_BRINGUP_CPU + 1到CPUHP_AP_ONLINE之间的状态空间。 + + 该阶段中的startup回调是在早期CPU设置代码中的CPU上线操作期间,禁用中断的情况下在热拔 + 插的CPU上被调用。teardown回调是在CPU完全关闭前不久的CPU下线操作期间,禁用中断的情况 + 下在热拔插的CPU上被调用。 + + 该阶段中的回调不允许失败。 + + 回调用于低级别的硬件初始化/关机和核心子系统。 + +* ONLINE阶段 + + ONLINE阶段涵盖了CPUHP_AP_ONLINE + 1到CPUHP_ONLINE之间的状态空间。 + + 该阶段中的startup回调是在CPU上线时在热插拔的CPU上调用的。teardown回调是在CPU下线操 + 作时在热插拔CPU上调用的。 + + 回调是在每个CPU热插拔线程的上下文中调用的,该线程绑定在热插拔的CPU上。回调是在启用 + 中断和抢占的情况下调用的。 + + 允许回调失败。如果回调失败,CPU热插拔操作被中止,CPU将恢复到之前的状态。 + +CPU 上线/下线操作 +----------------- + +一个成功的上线操作如下:: + + [CPUHP_OFFLINE] + [CPUHP_OFFLINE + 1]->startup() -> 成功 + [CPUHP_OFFLINE + 2]->startup() -> 成功 + [CPUHP_OFFLINE + 3] -> 略过,因为startup == NULL + ... + [CPUHP_BRINGUP_CPU]->startup() -> 成功 + === PREPARE阶段结束 + [CPUHP_BRINGUP_CPU + 1]->startup() -> 成功 + ... + [CPUHP_AP_ONLINE]->startup() -> 成功 + === STARTUP阶段结束 + [CPUHP_AP_ONLINE + 1]->startup() -> 成功 + ... + [CPUHP_ONLINE - 1]->startup() -> 成功 + [CPUHP_ONLINE] + +一个成功的下线操作如下:: + + [CPUHP_ONLINE] + [CPUHP_ONLINE - 1]->teardown() -> 成功 + ... + [CPUHP_AP_ONLINE + 1]->teardown() -> 成功 + === STARTUP阶段开始 + [CPUHP_AP_ONLINE]->teardown() -> 成功 + ... + [CPUHP_BRINGUP_ONLINE - 1]->teardown() + ... + === PREPARE阶段开始 + [CPUHP_BRINGUP_CPU]->teardown() + [CPUHP_OFFLINE + 3]->teardown() + [CPUHP_OFFLINE + 2] -> 略过,因为teardown == NULL + [CPUHP_OFFLINE + 1]->teardown() + [CPUHP_OFFLINE] + +一个失败的上线操作如下:: + + [CPUHP_OFFLINE] + [CPUHP_OFFLINE + 1]->startup() -> 成功 + [CPUHP_OFFLINE + 2]->startup() -> 成功 + [CPUHP_OFFLINE + 3] -> 略过,因为startup == NULL + ... + [CPUHP_BRINGUP_CPU]->startup() -> 成功 + === PREPARE阶段结束 + [CPUHP_BRINGUP_CPU + 1]->startup() -> 成功 + ... + [CPUHP_AP_ONLINE]->startup() -> 成功 + === STARTUP阶段结束 + [CPUHP_AP_ONLINE + 1]->startup() -> 成功 + --- + [CPUHP_AP_ONLINE + N]->startup() -> 失败 + [CPUHP_AP_ONLINE + (N - 1)]->teardown() + ... + [CPUHP_AP_ONLINE + 1]->teardown() + === STARTUP阶段开始 + [CPUHP_AP_ONLINE]->teardown() + ... + [CPUHP_BRINGUP_ONLINE - 1]->teardown() + ... + === PREPARE阶段开始 + [CPUHP_BRINGUP_CPU]->teardown() + [CPUHP_OFFLINE + 3]->teardown() + [CPUHP_OFFLINE + 2] -> 略过,因为teardown == NULL + [CPUHP_OFFLINE + 1]->teardown() + [CPUHP_OFFLINE] + +一个失败的下线操作如下:: + + [CPUHP_ONLINE] + [CPUHP_ONLINE - 1]->teardown() -> 成功 + ... + [CPUHP_ONLINE - N]->teardown() -> 失败 + [CPUHP_ONLINE - (N - 1)]->startup() + ... + [CPUHP_ONLINE - 1]->startup() + [CPUHP_ONLINE] + +递归失败不能被合理地处理。 +请看下面的例子,由于下线操作失败而导致的递归失败:: + + [CPUHP_ONLINE] + [CPUHP_ONLINE - 1]->teardown() -> 成功 + ... + [CPUHP_ONLINE - N]->teardown() -> 失败 + [CPUHP_ONLINE - (N - 1)]->startup() -> 成功 + [CPUHP_ONLINE - (N - 2)]->startup() -> 失败 + +CPU热插拔状态机在此停止,且不再尝试回滚,因为这可能会导致死循环:: + + [CPUHP_ONLINE - (N - 1)]->teardown() -> 成功 + [CPUHP_ONLINE - N]->teardown() -> 失败 + [CPUHP_ONLINE - (N - 1)]->startup() -> 成功 + [CPUHP_ONLINE - (N - 2)]->startup() -> 失败 + [CPUHP_ONLINE - (N - 1)]->teardown() -> 成功 + [CPUHP_ONLINE - N]->teardown() -> 失败 + +周而复始,不断重复。在这种情况下,CPU留在该状态中:: + + [CPUHP_ONLINE - (N - 1)] + +这至少可以让系统取得进展,让用户有机会进行调试,甚至解决这个问题。 + +分配一个状态 +------------ + +有两种方式分配一个CPU热插拔状态: + +* 静态分配 + + 当子系统或驱动程序有相对于其他CPU热插拔状态的排序要求时,必须使用静态分配。例如, + 在CPU上线操作期间,PERF核心startup回调必须在PERF驱动startup回调之前被调用。在CPU + 下线操作中,驱动teardown回调必须在核心teardown回调之前调用。静态分配的状态由 + cpuhp_state枚举中的常量描述,可以在include/linux/cpuhotplug.h中找到。 + + 在适当的位置将状态插入枚举中,这样就满足了排序要求。状态常量必须被用于状态的设置 + 和移除。 + + 当状态回调不是在运行时设置的,并且是kernel/cpu.c中CPU热插拔状态数组初始化的一部分 + 时,也需要静态分配。 + +* 动态分配 + + 当对状态回调没有排序要求时,动态分配是首选方法。状态编号由setup函数分配,并在成功 + 后返回给调用者。 + + 只有PREPARE和ONLINE阶段提供了一个动态分配范围。STARTING阶段则没有,因为该部分的大多 + 数回调都有明确的排序要求。 + +CPU热插拔状态的设置 +------------------- + +核心代码提供了以下函数用来设置状态: + +* cpuhp_setup_state(state, name, startup, teardown) +* cpuhp_setup_state_nocalls(state, name, startup, teardown) +* cpuhp_setup_state_cpuslocked(state, name, startup, teardown) +* cpuhp_setup_state_nocalls_cpuslocked(state, name, startup, teardown) + +对于一个驱动程序或子系统有多个实例,并且每个实例都需要调用相同的CPU hotplug状态回 +调的情况,CPU hotplug核心提供多实例支持。与驱动程序特定的实例列表相比,其优势在于 +与实例相关的函数完全针对CPU hotplug操作进行序列化,并在添加和删除时提供状态回调的 +自动调用。要设置这样一个多实例状态,可以使用以下函数: + +* cpuhp_setup_state_multi(state, name, startup, teardown) + +@state参数要么是静态分配的状态,要么是动态分配状态(PUHP_PREPARE_DYN,CPUHP_ONLINE_DYN) +的常量之一, 具体取决于应该分配动态状态的状态阶段(PREPARE,ONLINE)。 + +@name参数用于sysfs输出和检测。命名惯例是"subsys:mode"或"subsys/driver:mode", +例如 "perf:mode"或"perf/x86:mode"。常见的mode名称有: + +======== ============================================ +prepare 对应PREPARE阶段中的状态 + +dead 对应PREPARE阶段中不提供startup回调的状态 + +starting 对应STARTING阶段中的状态 + +dying 对应STARTING阶段中不提供startup回调的状态 + +online 对应ONLINE阶段中的状态 + +offline 对应ONLINE阶段中不提供startup回调的状态 +======== ============================================ + +由于@name参数只用于sysfs和检测,如果其他mode描述符比常见的描述符更好地描述状态的性质, +也可以使用。 + +@name参数的示例:"perf/online", "perf/x86:prepare", "RCU/tree:dying", "sched/waitempty" + +@startup参数是一个指向回调的函数指针,在CPU上线操作时被调用。若应用不需要startup +回调,则将该指针设为NULL。 + +@teardown参数是一个指向回调的函数指针,在CPU下线操作时调用。若应用不需要teardown +回调,则将该指针设为NULL。 + +这些函数在处理已注册回调的方式上有所不同: + + * cpuhp_setup_state_nocalls(), cpuhp_setup_state_nocalls_cpuslocked()和 + cpuhp_setup_state_multi()只注册回调。 + + * cpuhp_setup_state()和cpuhp_setup_state_cpuslocked()注册回调,并对当前状态大于新 + 安装状态的所有在线CPU调用@startup回调(如果不是NULL)。根据状态阶段,回调要么在 + 当前的CPU上调用(PREPARE阶段),要么在CPU的热插拔线程中调用每个在线CPU(ONLINE阶段)。 + + 如果CPU N的回调失败,那么CPU 0...N-1的teardown回调被调用以回滚操作。状态设置失败, + 状态的回调没有被注册,在动态分配的情况下,分配的状态被释放。 + +状态设置和回调调用是针对CPU热拔插操作进行序列化的。如果设置函数必须从CPU热插拔的读 +锁定区域调用,那么必须使用_cpuslocked()变体。这些函数不能在CPU热拔插回调中使用。 + +函数返回值: + ======== ========================================================== + 0 静态分配的状态设置成功 + + >0 动态分配的状态设置成功 + + 返回的数值是被分配的状态编号。如果状态回调后来必须被移除, + 例如模块移除,那么这个数值必须由调用者保存,并作为状态移 + 除函数的@state参数。对于多实例状态,动态分配的状态编号也 + 需要作为实例添加/删除操作的@state参数。 + + <0 操作失败 + ======== ========================================================== + +移除CPU热拔插状态 +----------------- + +为了移除一个之前设置好的状态,提供了如下函数: + +* cpuhp_remove_state(state) +* cpuhp_remove_state_nocalls(state) +* cpuhp_remove_state_nocalls_cpuslocked(state) +* cpuhp_remove_multi_state(state) + +@state参数要么是静态分配的状态,要么是由cpuhp_setup_state*()在动态范围内分配 +的状态编号。如果状态在动态范围内,则状态编号被释放,可再次进行动态分配。 + +这些函数在处理已注册回调的方式上有所不同: + + * cpuhp_remove_state_nocalls(), cpuhp_remove_state_nocalls_cpuslocked() + 和 cpuhp_remove_multi_state()只删除回调。 + + * cpuhp_remove_state()删除回调,并调用所有当前状态大于被删除状态的在线CPU的 + teardown回调(如果不是NULL)。根据状态阶段,回调要么在当前的CPU上调用 + (PREPARE阶段),要么在CPU的热插拔线程中调用每个在线CPU(ONLINE阶段)。 + + 为了完成移除工作,teardown回调不能失败。 + +状态移除和回调调用是针对CPU热拔插操作进行序列化的。如果移除函数必须从CPU hotplug +读取锁定区域调用,那么必须使用_cpuslocked()变体。这些函数不能从CPU热插拔的回调中使用。 + +如果一个多实例的状态被移除,那么调用者必须先移除所有的实例。 + +多实例状态实例管理 +------------------ + +一旦多实例状态被建立,实例就可以被添加到状态中: -一旦一个CPU下线或上线,就有可能收到通知。这对某些需要根据可用CPU数量执行某种设置或清 -理功能的驱动程序来说可能很重要:: + * cpuhp_state_add_instance(state, node) + * cpuhp_state_add_instance_nocalls(state, node) - #include <linux/cpuhotplug.h> +@state参数是一个静态分配的状态或由cpuhp_setup_state_multi()在动态范围内分配的状 +态编号。 - ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "X/Y:online", - Y_online, Y_prepare_down); +@node参数是一个指向hlist_node的指针,它被嵌入到实例的数据结构中。这个指针被交给 +多实例状态的回调,可以被回调用来通过container_of()检索到实例。 -*X* 是子系统, *Y* 是特定的驱动程序。 *Y_online* 回调将在所有在线CPU的注册过程中被调用。 -如果在线回调期间发生错误, *Y_prepare_down* 回调将在所有之前调用过在线回调的CPU上调 -用。注册完成后,一旦有CPU上线, *Y_online* 回调将被调用,当CPU关闭时, *Y_prepare_down* -将被调用。所有之前在 *Y_online* 中分配的资源都应该在 *Y_prepare_down* 中释放。如果在 -注册过程中发生错误,返回值 *ret* 为负值。否则会返回一个正值,其中包含动态分配状态 -( *CPUHP_AP_ONLINE_DYN* )的分配热拔插。对于预定义的状态,它将返回0。 +这些函数在处理已注册回调的方式上有所不同: -该回调可以通过调用 ``cpuhp_remove_state()`` 来删除。如果是动态分配的状态 -( *CPUHP_AP_ONLINE_DYN* ),则使用返回的状态。在移除热插拔状态的过程中,将调用拆解回调。 + * cpuhp_state_add_instance_nocalls()只将实例添加到多实例状态的节点列表中。 -多个实例 -~~~~~~~~ + * cpuhp_state_add_instance()为所有当前状态大于@state的在线CPU添加实例并调用与 + @state相关的startup回调(如果不是NULL)。该回调只对将要添加的实例进行调用。 + 根据状态阶段,回调要么在当前的CPU上调用(PREPARE阶段),要么在CPU的热插拔线 + 程中调用每个在线CPU(ONLINE阶段)。 -如果一个驱动程序有多个实例,并且每个实例都需要独立执行回调,那么很可能应该使用 -``multi-state`` 。首先需要注册一个多状态的状态:: + 如果CPU N的回调失败,那么CPU 0 ... N-1的teardown回调被调用以回滚操作,该函数 + 失败,实例不会被添加到多实例状态的节点列表中。 - ret = cpuhp_setup_state_multi(CPUHP_AP_ONLINE_DYN, "X/Y:online, - Y_online, Y_prepare_down); - Y_hp_online = ret; +要从状态的节点列表中删除一个实例,可以使用这些函数: -``cpuhp_setup_state_multi()`` 的行为与 ``cpuhp_setup_state()`` 类似,只是它 -为多状态准备了回调,但不调用回调。这是一个一次性的设置。 -一旦分配了一个新的实例,你需要注册这个新实例:: + * cpuhp_state_remove_instance(state, node) + * cpuhp_state_remove_instance_nocalls(state, node) - ret = cpuhp_state_add_instance(Y_hp_online, &d->node); +参数与上述cpuhp_state_add_instance*()变体相同。 -这个函数将把这个实例添加到你先前分配的 ``Y_hp_online`` 状态,并在所有在线的 -CPU上调用先前注册的回调( ``Y_online`` )。 *node* 元素是你的每个实例数据结构 -中的一个 ``struct hlist_node`` 成员。 +这些函数在处理已注册回调的方式上有所不同: -在移除该实例时:: + * cpuhp_state_remove_instance_nocalls()只从状态的节点列表中删除实例。 - cpuhp_state_remove_instance(Y_hp_online, &d->node) + * cpuhp_state_remove_instance()删除实例并调用与@state相关的回调(如果不是NULL), + 用于所有当前状态大于@state的在线CPU。 该回调只对将要被移除的实例进行调用。 + 根据状态阶段,回调要么在当前的CPU上调用(PREPARE阶段),要么在CPU的热插拔 + 线程中调用每个在线CPU(ONLINE阶段)。 -应该被调用,这将在所有在线CPU上调用拆分回调。 + 为了完成移除工作,teardown回调不能失败。 -手动设置 -~~~~~~~~ +节点列表的添加/删除操作和回调调用是针对CPU热拔插操作进行序列化。这些函数不能在 +CPU hotplug回调和CPU hotplug读取锁定区域内使用。 -通常情况下,在注册或移除状态时调用setup和teamdown回调是很方便的,因为通常在CPU上线 -(下线)和驱动的初始设置(关闭)时需要执行该操作。然而,每个注册和删除功能也有一个 -_nocalls的后缀,如果不希望调用回调,则不调用所提供的回调。在手动设置(或关闭)期间, -应该使用 ``get_online_cpus()`` 和 ``put_online_cpus()`` 函数来抑制CPU热插拔操作。 +样例 +---- +在STARTING阶段设置和取消静态分配的状态,以获取上线和下线操作的通知:: -事件的顺序 ----------- + ret = cpuhp_setup_state(CPUHP_SUBSYS_STARTING, "subsys:starting", subsys_cpu_starting, subsys_cpu_dying); + if (ret < 0) + return ret; + .... + cpuhp_remove_state(CPUHP_SUBSYS_STARTING); -热插拔状态被定义在 ``include/linux/cpuhotplug.h``: +在ONLINE阶段设置和取消动态分配的状态,以获取下线操作的通知:: -* ``CPUHP_OFFLINE`` ... ``CPUHP_AP_OFFLINE`` 状态是在CPU启动前调用的。 + state = cpuhp_setup_state(CPUHP_ONLINE_DYN, "subsys:offline", NULL, subsys_cpu_offline); + if (state < 0) + return state; + .... + cpuhp_remove_state(state); -* ``CPUHP_AP_OFFLINE`` ... ``CPUHP_AP_ONLINE`` 状态是在CPU被启动后被调用的。 - 中断是关闭的,调度程序还没有在这个CPU上活动。从 ``CPUHP_AP_OFFLINE`` 开始, - 回调被调用到目标CPU上。 +在ONLINE阶段设置和取消动态分配的状态,以获取有关上线操作的通知,而无需调用回调:: -* ``CPUHP_AP_ONLINE_DYN`` 和 ``CPUHP_AP_ONLINE_DYN_END`` 之间的状态被保留 - 给动态分配。 + state = cpuhp_setup_state_nocalls(CPUHP_ONLINE_DYN, "subsys:online", subsys_cpu_online, NULL); + if (state < 0) + return state; + .... + cpuhp_remove_state_nocalls(state); -* 这些状态在CPU关闭时以相反的顺序调用,从 ``CPUHP_ONLINE`` 开始,在 ``CPUHP_OFFLINE`` - 停止。这里的回调是在将被关闭的CPU上调用的,直到 ``CPUHP_AP_OFFLINE`` 。 +在ONLINE阶段设置、使用和取消动态分配的多实例状态,以获得上线和下线操作的通知:: -通过 ``CPUHP_AP_ONLINE_DYN`` 动态分配的状态通常已经足够了。然而,如果在启动或关闭 -期间需要更早的调用,那么应该获得一个显式状态。如果热拔插事件需要相对于另一个热拔插事 -件的特定排序,也可能需要一个显式状态。 + state = cpuhp_setup_state_multi(CPUHP_ONLINE_DYN, "subsys:online", subsys_cpu_online, subsys_cpu_offline); + if (state < 0) + return state; + .... + ret = cpuhp_state_add_instance(state, &inst1->node); + if (ret) + return ret; + .... + ret = cpuhp_state_add_instance(state, &inst2->node); + if (ret) + return ret; + .... + cpuhp_remove_instance(state, &inst1->node); + .... + cpuhp_remove_instance(state, &inst2->node); + .... + remove_multi_state(state); 测试热拔插状态 ============== diff --git a/Documentation/translations/zh_CN/core-api/irq/irq-domain.rst b/Documentation/translations/zh_CN/core-api/irq/irq-domain.rst index 7d077742f7580..9174fce12c1b8 100644 --- a/Documentation/translations/zh_CN/core-api/irq/irq-domain.rst +++ b/Documentation/translations/zh_CN/core-api/irq/irq-domain.rst @@ -5,6 +5,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> .. _cn_irq-domain.rst: @@ -52,8 +53,18 @@ irq_domain和一个hwirq号作为参数。 如果hwirq的映射还不存在,� 一个新的Linux irq_desc,将其与hwirq关联起来,并调用.map()回调,这样驱动 程序就可以执行任何必要的硬件设置。 -当接收到一个中断时,应该使用irq_find_mapping()函数从hwirq号中找到 -Linux IRQ号。 +一旦建立了映射,可以通过多种方法检索或使用它: + +- irq_resolve_mapping()返回一个指向给定域和hwirq号的irq_desc结构指针, + 如果没有映射则返回NULL。 + +- irq_find_mapping()返回给定域和hwirq的Linux IRQ号,如果没有映射则返回0。 + +- irq_linear_revmap()现与irq_find_mapping()相同,已被废弃。 + +- generic_handle_domain_irq()处理一个由域和hwirq号描述的中断。 + +请注意,irq域的查找必须发生在与RCU读临界区兼容的上下文中。 在调用irq_find_mapping()之前,至少要调用一次irq_create_mapping()函数, 以免描述符不能被分配。 @@ -119,7 +130,8 @@ irq_domain_add_tree()和irq_domain_create_tree()在功能上是等价的,除� Linux IRQ号编入硬件本身,这样就不需要映射了。 调用irq_create_direct_mapping() 会分配一个Linux IRQ号,并调用.map()回调,这样驱动就可以将Linux IRQ号编入硬件中。 -大多数驱动程序不能使用这个映射。 +大多数驱动程序无法使用此映射,现在它由CONFIG_IRQ_DOMAIN_NOMAP选项控制。 +请不要引入此API的新用户。 传统映射类型 ------------ @@ -128,7 +140,6 @@ Linux IRQ号编入硬件本身,这样就不需要映射了。 调用irq_create irq_domain_add_simple() irq_domain_add_legacy() - irq_domain_add_legacy_isa() irq_domain_create_simple() irq_domain_create_legacy() @@ -137,6 +148,9 @@ Linux IRQ号编入硬件本身,这样就不需要映射了。 调用irq_create 一组用于IRQ号的定义(#define),这些定义被传递给struct设备注册。 在这种情况下, 不能动态分配Linux IRQ号,应该使用传统映射。 +顾名思义,\*_legacy()系列函数已被废弃,只是为了方便对古老平台的支持而存在。 +不应该增加新的用户。当\*_simple()系列函数的使用导致遗留行为时,他们也是如此。 + 传统映射假设已经为控制器分配了一个连续的IRQ号范围,并且可以通过向hwirq号添加一 个固定的偏移来计算IRQ号,反之亦然。 缺点是需要中断控制器管理IRQ分配,并且需要为每 个hwirq分配一个irq_desc,即使它没有被使用。 diff --git a/Documentation/translations/zh_CN/core-api/kernel-api.rst b/Documentation/translations/zh_CN/core-api/kernel-api.rst index e45fe80d1cd88..120c051f6fcbd 100644 --- a/Documentation/translations/zh_CN/core-api/kernel-api.rst +++ b/Documentation/translations/zh_CN/core-api/kernel-api.rst @@ -5,6 +5,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> .. _cn_kernel-api.rst: @@ -282,6 +283,8 @@ kernel/acct.c 该API在以下内核代码中: +include/linux/bio.h + block/blk-core.c block/blk-core.c diff --git a/Documentation/translations/zh_CN/core-api/mm-api.rst b/Documentation/translations/zh_CN/core-api/mm-api.rst index 0ea43dc679539..a732b0eebf167 100644 --- a/Documentation/translations/zh_CN/core-api/mm-api.rst +++ b/Documentation/translations/zh_CN/core-api/mm-api.rst @@ -5,6 +5,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> :校译: @@ -66,12 +67,24 @@ mm/vmalloc.c 该API在以下内核代码中: -mm/readahead.c +文件映射 +-------- mm/filemap.c +预读 +---- + +mm/readahead.c + +回写 +---- + mm/page-writeback.c +截断 +---- + mm/truncate.c include/linux/pagemap.h @@ -105,6 +118,14 @@ mm/mempolicy.c include/linux/mm_types.h +include/linux/mm_inline.h + +include/linux/page-flags.h + include/linux/mm.h +include/linux/page_ref.h + include/linux/mmzone.h + +mm/util.c diff --git a/Documentation/translations/zh_CN/core-api/printk-basics.rst b/Documentation/translations/zh_CN/core-api/printk-basics.rst index d574de3167c8a..59c6efb3fc41b 100644 --- a/Documentation/translations/zh_CN/core-api/printk-basics.rst +++ b/Documentation/translations/zh_CN/core-api/printk-basics.rst @@ -6,6 +6,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> .. _cn_printk-basics.rst: @@ -107,6 +108,4 @@ pr_debug()和pr_devel(),除非定义了 ``DEBUG`` (或者在pr_debug()的情� 该API在以下内核代码中: -kernel/printk/printk.c - include/linux/printk.h diff --git a/Documentation/translations/zh_CN/core-api/printk-formats.rst b/Documentation/translations/zh_CN/core-api/printk-formats.rst index ce39c788cf5ab..bd36d35eba4eb 100644 --- a/Documentation/translations/zh_CN/core-api/printk-formats.rst +++ b/Documentation/translations/zh_CN/core-api/printk-formats.rst @@ -5,6 +5,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> .. _cn_printk-formats.rst: @@ -548,7 +549,7 @@ nodemask_pr_args()来方便打印cpumask和nodemask。 :: - %pGp referenced|uptodate|lru|active|private|node=0|zone=2|lastcpupid=0x1fffff + %pGp 0x17ffffc0002036(referenced|uptodate|lru|active|private|node=0|zone=2|lastcpupid=0x1fffff) %pGg GFP_USER|GFP_DMA32|GFP_NOWARN %pGv read|exec|mayread|maywrite|mayexec|denywrite diff --git a/Documentation/translations/zh_CN/core-api/workqueue.rst b/Documentation/translations/zh_CN/core-api/workqueue.rst index e372fa5cf101f..f6567cf9d3fb3 100644 --- a/Documentation/translations/zh_CN/core-api/workqueue.rst +++ b/Documentation/translations/zh_CN/core-api/workqueue.rst @@ -6,6 +6,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> .. _cn_workqueue.rst: @@ -178,10 +179,6 @@ workqueue将自动创建与属性相匹配的后备工作者池。调节并发� 这个标志对于未绑定的wq来说是没有意义的。 -请注意,标志 ``WQ_NON_REENTRANT`` 不再存在,因为现在所有的工作 -队列都是不可逆的——任何工作项都保证在任何时间内最多被整个系统的一 -个工作者执行。 - ``max_active`` -------------- @@ -328,6 +325,22 @@ And with cmwq with ``@max_active`` >= 3, :: 工作项函数在堆栈追踪中应该是微不足道的。 +不可重入条件 +============ + +工作队列保证,如果在工作项排队后满足以下条件,则工作项不能重入: + + + 1. 工作函数没有被改变。 + 2. 没有人将该工作项排到另一个工作队列中。 + 3. 该工作项尚未被重新启动。 + +换言之,如果上述条件成立,则保证在任何给定时间最多由一个系统范围内的工作程序执行 +该工作项。 + +请注意,在self函数中将工作项重新排队(到同一队列)不会破坏这些条件,因此可以安全 +地执行此操作。否则在破坏工作函数内部的条件时需要小心。 + 内核内联文档参考 ================ diff --git a/Documentation/translations/zh_CN/core-api/xarray.rst b/Documentation/translations/zh_CN/core-api/xarray.rst index ff2d9bcb7c344..fb19324966cee 100644 --- a/Documentation/translations/zh_CN/core-api/xarray.rst +++ b/Documentation/translations/zh_CN/core-api/xarray.rst @@ -6,6 +6,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + 周彬彬 Binbin Zhou <zhoubinbin@loongson.cn> :校译: @@ -254,7 +255,8 @@ __xa_set_mark() 和 __xa_clear_mark() 函数也适用于你查找一个条目并 高级API是基于xa_state的。这是一个不透明的数据结构,你使用XA_STATE()宏在堆栈中声明。这个宏初始化了 xa_state,准备开始在XArray上移动。它被用作一个游标来保持在XArray中的位置,并让你把各种操作组合在一 -起,而不必每次都从头开始。 +起,而不必每次都从头开始。xa_state的内容受rcu_read_lock()或xas_lock()的保护。如果需要删除保护状态 +和树的这些锁中的任何一个,你必须调用xas_pause()以便将来的调用不会依赖于状态中未受保护的部分。 xa_state也被用来存储错误(store errors)。你可以调用xas_error()来检索错误。所有的操作在进行之前都 会检查xa_state是否处于错误状态,所以你没有必要在每次调用之后检查错误;你可以连续进行多次调用,只在 diff --git a/Documentation/translations/zh_CN/kernel-hacking/hacking.rst b/Documentation/translations/zh_CN/kernel-hacking/hacking.rst index f2bc154c5bcc8..bda79646bb1e4 100644 --- a/Documentation/translations/zh_CN/kernel-hacking/hacking.rst +++ b/Documentation/translations/zh_CN/kernel-hacking/hacking.rst @@ -81,7 +81,7 @@ 过硬件中断)的“软件中断”将运行( ``kernel/softirq.c`` )。 此处完成了许多真正的中断处理工作。在向SMP过渡的早期,只有“bottom halves下半 -部”(BHs)机制,无法利用多个CPU的优势。在从那些一团糟的就电脑切换过来后不久, +部”(BHs)机制,无法利用多个CPU的优势。在从那些一团糟的旧电脑切换过来后不久, 我们放弃了这个限制,转而使用“软中断”。 ``include/linux/interrupt.h`` 列出了不同的软中断。定时器软中断是一个非常重要 @@ -95,8 +95,7 @@ .. warning:: - “tasklet”这个名字是误导性的:它们与“任务”无关,可能更多与当时 - 阿列克谢·库兹涅佐夫享用的糟糕伏特加有关。 + “tasklet”这个名字是误导性的:它们与“任务”无关。 你可以使用 :c:func:`in_softirq()` 宏( ``include/linux/preempt.h`` )来确认 是否处于软中断(或子任务)中。 @@ -247,7 +246,7 @@ Provide mechanism not policy”。 与 :c:func:`put_user()` 和 :c:func:`get_user()` 不同,它们返回未复制的 数据量(即0仍然意味着成功)。 -【是的,这个愚蠢的接口真心让我尴尬。火爆的口水仗大概每年都会发生。 +【是的,这个讨厌的接口真心让我尴尬。火爆的口水仗大概每年都会发生。 —— Rusty Russell】 这些函数可以隐式睡眠。它不应该在用户上下文之外调用(没有意义)、调用时禁用中断 @@ -538,9 +537,9 @@ Documentation/core-api/symbol-namespaces.rst 。 Linus和其他开发人员有时会更改开发内核中的函数或结构体名称;这样做不仅是为了 让每个人都保持警惕,还反映了一个重大的更改(例如,不能再在打开中断的情况下 -调用,或者执行额外的检查,或者不执行以前捕获的检查)。通常这会附带一个linux -内核邮件列表中相当全面的注释;请搜索存档以查看。简单地对文件进行全局替换通常 -会让事情变得 **更糟** 。 +调用,或者执行额外的检查,或者不执行以前捕获的检查)。通常这会附带发送一个 +相当全面的注释到相应的内核邮件列表中;请搜索存档以查看。简单地对文件进行全局 +替换通常只会让事情变得 **更糟** 。 初始化结构体成员 ------------------ @@ -610,7 +609,7 @@ C++ 为了让你的东西更正式、补丁更整洁,还有一些工作要做: -- 搞清楚你在谁的地界儿上干活。查看源文件的顶部、 ``MAINTAINERS`` 文件以及 +- 搞清楚你修改的代码属于谁。查看源文件的根目录、 ``MAINTAINERS`` 文件以及 ``CREDITS`` 文件的最后一部分。你应该和此人协调,确保你没有重新发明轮子, 或者尝试一些已经被拒绝的东西。 @@ -629,9 +628,10 @@ C++ “obj-$(CONFIG_xxx) += xxx.o”。语法记录在 Documentation/kbuild/makefiles.rst 。 -- 如果你做了一些有意义的事情,那可以把自己放进 ``CREDITS`` ,通常不止一个 - 文件(无论如何你的名字都应该在源文件的顶部)。维护人员意味着您希望在对 - 子系统进行更改时得到询问,并了解缺陷;这意味着对某部分代码做出更多承诺。 +- 如果你认为自己做了一些有意义的事情,可以把自己放进 ``CREDITS`` ,通常不 + 止一个文件(无论如何你的名字都应该在源文件的顶部)。 ``MAINTAINERS`` + 意味着您希望在对子系统进行更改时得到询问,并了解缺陷;这意味着对某部分 + 代码做出更多承诺。 - 最后,别忘记去阅读 Documentation/process/submitting-patches.rst , 也许还有 Documentation/process/submitting-drivers.rst 。 diff --git a/Documentation/translations/zh_CN/riscv/index.rst b/Documentation/translations/zh_CN/riscv/index.rst index 614cde0c09972..131e405aa8575 100644 --- a/Documentation/translations/zh_CN/riscv/index.rst +++ b/Documentation/translations/zh_CN/riscv/index.rst @@ -19,7 +19,6 @@ RISC-V 体系结构 boot-image-header vm-layout - pmu patch-acceptance diff --git a/Documentation/translations/zh_CN/riscv/pmu.rst b/Documentation/translations/zh_CN/riscv/pmu.rst deleted file mode 100644 index 7ec801026c4d7..0000000000000 --- a/Documentation/translations/zh_CN/riscv/pmu.rst +++ /dev/null @@ -1,235 +0,0 @@ -.. include:: ../disclaimer-zh_CN.rst - -:Original: Documentation/riscv/pmu.rst - -:翻译: - - 司延腾 Yanteng Si <siyanteng@loongson.cn> - -.. _cn_riscv_pmu: - -======================== -RISC-V平台上对PMUs的支持 -======================== - -Alan Kao <alankao@andestech.com>, Mar 2018 - -简介 ------------- - -截止本文撰写时,在The RISC-V ISA Privileged Version 1.10中提到的 perf_event -相关特性如下: -(详情请查阅手册) - -* [m|s]counteren -* mcycle[h], cycle[h] -* minstret[h], instret[h] -* mhpeventx, mhpcounterx[h] - -仅有以上这些功能,移植perf需要做很多工作,究其原因是缺少以下通用架构的性能 -监测特性: - -* 启用/停用计数器 - 在我们这里,计数器一直在自由运行。 -* 计数器溢出引起的中断 - 规范中没有这种功能。 -* 中断指示器 - 不可能所有的计数器都有很多的中断端口,所以需要一个中断指示器让软件来判断 - 哪个计数器刚好溢出。 -* 写入计数器 - 由于内核不能修改计数器,所以会有一个SBI来支持这个功能[1]。 另外,一些厂商 - 考虑实现M-S-U型号机器的硬件扩展来直接写入计数器。 - -这篇文档旨在为开发者提供一个在内核中支持PMU的简要指南。下面的章节简要解释了 -perf' 机制和待办事项。 - -你可以在这里查看以前的讨论[1][2]。 另外,查看附录中的相关内核结构体可能会有 -帮助。 - - -1. 初始化 ---------- - -*riscv_pmu* 是一个类型为 *struct riscv_pmu* 的全局指针,它包含了根据perf内部 -约定的各种方法和PMU-specific参数。人们应该声明这样的实例来代表PMU。 默认情况 -下, *riscv_pmu* 指向一个常量结构体 *riscv_base_pmu* ,它对基准QEMU模型有非常 -基础的支持。 - - -然后他/她可以将实例的指针分配给 *riscv_pmu* ,这样就可以利用已经实现的最小逻 -辑,或者创建他/她自己的 *riscv_init_platform_pmu* 实现。 - -换句话说,现有的 *riscv_base_pmu* 源只是提供了一个参考实现。 开发者可以灵活地 -决定多少部分可用,在最极端的情况下,他们可以根据自己的需要定制每一个函数。 - - -2. Event Initialization ------------------------ - -当用户启动perf命令来监控一些事件时,首先会被用户空间的perf工具解释为多个 -*perf_event_open* 系统调用,然后进一步调用上一步分配的 *event_init* 成员函数 -的主体。 在 *riscv_base_pmu* 的情况下,就是 *riscv_event_init* 。 - -该功能的主要目的是将用户提供的事件翻译成映射图,从而可以直接对HW-related的控 -制寄存器或计数器进行操作。该翻译基于 *riscv_pmu* 中提供的映射和方法。 - -注意,有些功能也可以在这个阶段完成: - -(1) 中断设置,这个在下一节说; -(2) 特限级设置(仅用户空间、仅内核空间、两者都有); -(3) 析构函数设置。 通常应用 *riscv_destroy_event* 即可; -(4) 对非采样事件的调整,这将被函数应用,如 *perf_adjust_period* ,通常如下:: - - if (!is_sampling_event(event)) { - hwc->sample_period = x86_pmu.max_period; - hwc->last_period = hwc->sample_period; - local64_set(&hwc->period_left, hwc->sample_period); - } - - -在 *riscv_base_pmu* 的情况下,目前只提供了(3)。 - - -3. 中断 -------- - -3.1. 中断初始化 - -这种情况经常出现在 *event_init* 方案的开头。通常情况下,这应该是一个代码段,如:: - - int x86_reserve_hardware(void) - { - int err = 0; - - if (!atomic_inc_not_zero(&pmc_refcount)) { - mutex_lock(&pmc_reserve_mutex); - if (atomic_read(&pmc_refcount) == 0) { - if (!reserve_pmc_hardware()) - err = -EBUSY; - else - reserve_ds_buffers(); - } - if (!err) - atomic_inc(&pmc_refcount); - mutex_unlock(&pmc_reserve_mutex); - } - - return err; - } - -而神奇的是 *reserve_pmc_hardware* ,它通常做原子操作,使实现的IRQ可以从某个全局函 -数指针访问。 而 *release_pmc_hardware* 的作用正好相反,它用在上一节提到的事件分配 -器中。 - - (注:从所有架构的实现来看,*reserve/release* 对总是IRQ设置,所以 *pmc_hardware* - 似乎有些误导。 它并不处理事件和物理计数器之间的绑定,这一点将在下一节介绍。) - -3.2. IRQ结构体 - -基本上,一个IRQ运行以下伪代码:: - - for each hardware counter that triggered this overflow - - get the event of this counter - - // following two steps are defined as *read()*, - // check the section Reading/Writing Counters for details. - count the delta value since previous interrupt - update the event->count (# event occurs) by adding delta, and - event->hw.period_left by subtracting delta - - if the event overflows - sample data - set the counter appropriately for the next overflow - - if the event overflows again - too frequently, throttle this event - fi - fi - - end for - - 然而截至目前,没有一个RISC-V的实现为perf设计了中断,所以具体的实现要在未来完成。 - -4. Reading/Writing 计数 ------------------------ - -它们看似差不多,但perf对待它们的态度却截然不同。 对于读,在 *struct pmu* 中有一个 -*read* 接口,但它的作用不仅仅是读。 根据上下文,*read* 函数不仅要读取计数器的内容 -(event->count),还要更新左周期到下一个中断(event->hw.period_left)。 - - 但 perf 的核心不需要直接写计数器。 写计数器隐藏在以下两点的抽象化之后, - 1) *pmu->start* ,从字面上看就是开始计数,所以必须把计数器设置成一个合适的值,以 - 便下一次中断; - 2)在IRQ里面,应该把计数器设置成同样的合理值。 - -在RISC-V中,读操作不是问题,但写操作就需要费些力气了,因为S模式不允许写计数器。 - - -5. add()/del()/start()/stop() ------------------------------ - -基本思想: add()/del() 向PMU添加/删除事件,start()/stop() 启动/停止PMU中某个事件 -的计数器。 所有这些函数都使用相同的参数: *struct perf_event *event* 和 *int flag* 。 - -把 perf 看作一个状态机,那么你会发现这些函数作为这些状态之间的状态转换过程。 -定义了三种状态(event->hw.state): - -* PERF_HES_STOPPED: 计数停止 -* PERF_HES_UPTODATE: event->count是最新的 -* PERF_HES_ARCH: 依赖于体系结构的用法,。。。我们现在并不需要它。 - -这些状态转换的正常流程如下: - -* 用户启动一个 perf 事件,导致调用 *event_init* 。 -* 当被上下文切换进来的时候,*add* 会被 perf core 调用,并带有一个标志 PERF_EF_START, - 也就是说事件被添加后应该被启动。 在这个阶段,如果有的话,一般事件会被绑定到一个物 - 理计数器上。当状态变为PERF_HES_STOPPED和PERF_HES_UPTODATE,因为现在已经停止了, - (软件)事件计数不需要更新。 - - - 然后调用 *start* ,并启用计数器。 - 通过PERF_EF_RELOAD标志,它向计数器写入一个适当的值(详细情况请参考上一节)。 - 如果标志不包含PERF_EF_RELOAD,则不会写入任何内容。 - 现在状态被重置为none,因为它既没有停止也没有更新(计数已经开始)。 - -*当被上下文切换出来时被调用。 然后,它检查出PMU中的所有事件,并调用 *stop* 来更新它们 - 的计数。 - - - *stop* 被 *del* 和perf核心调用,标志为PERF_EF_UPDATE,它经常以相同的逻辑和 *read* - 共用同一个子程序。 - 状态又一次变为PERF_HES_STOPPED和PERF_HES_UPTODATE。 - - - 这两对程序的生命周期: *add* 和 *del* 在任务切换时被反复调用;*start* 和 *stop* 在 - perf核心需要快速停止和启动时也会被调用,比如在调整中断周期时。 - -目前的实现已经足够了,将来可以很容易地扩展到功能。 - -A. 相关结构体 -------------- - -* struct pmu: include/linux/perf_event.h -* struct riscv_pmu: arch/riscv/include/asm/perf_event.h - - 两个结构体都被设计为只读。 - - *struct pmu* 定义了一些函数指针接口,它们大多以 *struct perf_event* 作为主参数,根据 - perf的内部状态机处理perf事件(详情请查看kernel/events/core.c)。 - - *struct riscv_pmu* 定义了PMU的具体参数。 命名遵循所有其它架构的惯例。 - -* struct perf_event: include/linux/perf_event.h -* struct hw_perf_event - - 表示 perf 事件的通用结构体,以及硬件相关的细节。 - -* struct riscv_hw_events: arch/riscv/include/asm/perf_event.h - - 保存事件状态的结构有两个固定成员。 - 事件的数量和事件的数组。 - -参考文献 --------- - -[1] https://github.com/riscv/riscv-linux/pull/124 - -[2] https://groups.google.com/a/groups.riscv.org/forum/#!topic/sw-dev/f19TmCNP6yA diff --git a/Documentation/translations/zh_CN/riscv/vm-layout.rst b/Documentation/translations/zh_CN/riscv/vm-layout.rst index 585cb89317a3a..91884e2dfff8c 100644 --- a/Documentation/translations/zh_CN/riscv/vm-layout.rst +++ b/Documentation/translations/zh_CN/riscv/vm-layout.rst @@ -6,6 +6,7 @@ :翻译: 司延腾 Yanteng Si <siyanteng@loongson.cn> + Binbin Zhou <zhoubinbin@loongson.cn> ============================ RISC-V Linux上的虚拟内存布局 @@ -65,3 +66,39 @@ RISC-V Linux Kernel SV39 ffffffff00000000 | -4 GB | ffffffff7fffffff | 2 GB | modules, BPF ffffffff80000000 | -2 GB | ffffffffffffffff | 2 GB | kernel __________________|____________|__________________|_________|____________________________________________________________ + + +RISC-V Linux Kernel SV48 +------------------------ + +:: + + ======================================================================================================================== + 开始地址 | 偏移 | 结束地址 | 大小 | 虚拟内存区域描述 + ======================================================================================================================== + | | | | + 0000000000000000 | 0 | 00007fffffffffff | 128 TB | 用户空间虚拟内存,每个内存管理器不同 + __________________|____________|__________________|_________|___________________________________________________________ + | | | | + 0000800000000000 | +128 TB | ffff7fffffffffff | ~16M TB | ... 巨大的、几乎64位宽的直到内核映射的-128TB地方 + | | | | 开始偏移的非经典虚拟内存地址空洞。 + | | | | + __________________|____________|__________________|_________|___________________________________________________________ + | + | 内核空间的虚拟内存,在所有进程之间共享: + ____________________________________________________________|___________________________________________________________ + | | | | + ffff8d7ffee00000 | -114.5 TB | ffff8d7ffeffffff | 2 MB | fixmap + ffff8d7fff000000 | -114.5 TB | ffff8d7fffffffff | 16 MB | PCI io + ffff8d8000000000 | -114.5 TB | ffff8f7fffffffff | 2 TB | vmemmap + ffff8f8000000000 | -112.5 TB | ffffaf7fffffffff | 32 TB | vmalloc/ioremap space + ffffaf8000000000 | -80.5 TB | ffffef7fffffffff | 64 TB | 直接映射所有物理内存 + ffffef8000000000 | -16.5 TB | fffffffeffffffff | 16.5 TB | kasan + __________________|____________|__________________|_________|____________________________________________________________ + | + | 从此处开始,与39-bit布局相同: + ____________________________________________________________|____________________________________________________________ + | | | | + ffffffff00000000 | -4 GB | ffffffff7fffffff | 2 GB | modules, BPF + ffffffff80000000 | -2 GB | ffffffffffffffff | 2 GB | kernel + __________________|____________|__________________|_________|____________________________________________________________ diff --git a/Documentation/translations/zh_CN/vm/highmem.rst b/Documentation/translations/zh_CN/vm/highmem.rst index 018838e58c3e7..2003217746463 100644 --- a/Documentation/translations/zh_CN/vm/highmem.rst +++ b/Documentation/translations/zh_CN/vm/highmem.rst @@ -50,55 +50,55 @@ 临时虚拟映射 ============ -内核包含几种创建临时映射的方法。: +内核包含几种创建临时映射的方法。下面的列表按照使用的优先顺序显示了它们。 -* vmap(). 这可以用来将多个物理页长期映射到一个连续的虚拟空间。它需要synchronization - 来解除映射。 +* kmap_local_page()。这个函数是用来要求短期映射的。它可以从任何上下文(包括中断)中调用, + 但是映射只能在获取它们的上下文中使用。 -* kmap(). 这允许对单个页面进行短期映射。它需要synchronization,但在一定程度上被摊销。 - 当以嵌套方式使用时,它也很容易出现死锁,因此不建议在新代码中使用它。 + 在可行的情况下,这个函数应该比其他所有的函数优先使用。 -* kmap_atomic(). 这允许对单个页面进行非常短的时间映射。由于映射被限制在发布它的CPU上, - 它表现得很好,但发布任务因此被要求留在该CPU上直到它完成,以免其他任务取代它的映射。 - - kmap_atomic() 也可以由中断上下文使用,因为它不睡眠,而且调用者可能在调用kunmap_atomic() - 之后才睡眠。 - - 可以假设k[un]map_atomic()不会失败。 + 这些映射是线程本地和CPU本地的,这意味着映射只能从这个线程中访问,并且当映射处于活动状 + 态时,该线程与CPU绑定。即使线程被抢占了(因为抢占永远不会被函数禁用),CPU也不能通过 + CPU-hotplug从系统中拔出,直到映射被处理掉。 + 在本地的kmap区域中采取pagefaults是有效的,除非获取本地映射的上下文由于其他原因不允许 + 这样做。 -使用kmap_atomic -=============== + kmap_local_page()总是返回一个有效的虚拟地址,并且假定kunmap_local()不会失败。 -何时何地使用 kmap_atomic() 是很直接的。当代码想要访问一个可能从高内存(见__GFP_HIGHMEM) -分配的页面的内容时,例如在页缓存中的页面,就会使用它。该API有两个函数,它们的使用方式与 -下面类似:: + 嵌套kmap_local_page()和kmap_atomic()映射在一定程度上是允许的(最多到KMAP_TYPE_NR), + 但是它们的调用必须严格排序,因为映射的实现是基于堆栈的。关于如何管理嵌套映射的细节, + 请参见kmap_local_page() kdocs(包含在 "函数 "部分)。 - /* 找到感兴趣的页面。 */ - struct page *page = find_get_page(mapping, offset); - - /* 获得对该页内容的访问权。 */ - void *vaddr = kmap_atomic(page); +* kmap_atomic(). 这允许对单个页面进行非常短的时间映射。由于映射被限制在发布它的CPU上, + 它表现得很好,但发布的任务因此被要求留在该CPU上直到它完成,以免其他任务取代它的映射。 - /* 对该页的内容做一些处理。 */ - memset(vaddr, 0, PAGE_SIZE); + kmap_atomic()也可以被中断上下文使用,因为它不睡眠,调用者也可能在调用kunmap_atomic() + 后才睡眠。 - /* 解除该页面的映射。 */ - kunmap_atomic(vaddr); + 内核中对kmap_atomic()的每次调用都会创建一个不可抢占的段,并禁用缺页异常。这可能是 + 未预期延迟的来源之一。因此用户应该选择kmap_local_page()而不是kmap_atomic()。 -注意,kunmap_atomic()调用的是kmap_atomic()调用的结果而不是参数。 + 假设k[un]map_atomic()不会失败。 -如果你需要映射两个页面,因为你想从一个页面复制到另一个页面,你需要保持kmap_atomic调用严 -格嵌套,如:: +* kmap()。这应该被用来对单个页面进行短时间的映射,对抢占或迁移没有限制。它会带来开销, + 因为映射空间是受限制的,并且受到全局锁的保护,以实现同步。当不再需要映射时,必须用 + kunmap()释放该页被映射的地址。 - vaddr1 = kmap_atomic(page1); - vaddr2 = kmap_atomic(page2); + 映射变化必须广播到所有CPU(核)上,kmap()还需要在kmap的池被回绕(TLB项用光了,需要从第 + 一项复用)时进行全局TLB无效化,当映射空间被完全利用时,它可能会阻塞,直到有一个可用的 + 槽出现。因此,kmap()只能从可抢占的上下文中调用。 - memcpy(vaddr1, vaddr2, PAGE_SIZE); + 如果一个映射必须持续相对较长的时间,上述所有的工作都是必要的,但是内核中大部分的 + 高内存映射都是短暂的,而且只在一个地方使用。这意味着在这种情况下,kmap()的成本大 + 多被浪费了。kmap()并不是为长期映射而设计的,但是它已经朝着这个方向发展了,在较新 + 的代码中强烈不鼓励使用它,前面的函数集应该是首选。 - kunmap_atomic(vaddr2); - kunmap_atomic(vaddr1); + 在64位系统中,调用kmap_local_page()、kmap_atomic()和kmap()没有实际作用,因为64位 + 地址空间足以永久映射所有物理内存页面。 +* vmap()。这可以用来将多个物理页长期映射到一个连续的虚拟空间。它需要全局同步来解除 + 映射。 临时映射的成本 ============== @@ -126,3 +126,12 @@ i386 PAE 一般的建议是,你不要在32位机器上使用超过8GiB的空间--尽管更多的空间可能对你和你的工作 量有用,但你几乎是靠你自己--不要指望内核开发者真的会很关心事情的进展情况。 + +函数 +==== + +该API在以下内核代码中: + +include/linux/highmem.h + +include/linux/highmem-internal.h diff --git a/Documentation/translations/zh_CN/vm/index.rst b/Documentation/translations/zh_CN/vm/index.rst index a1c6d529b6ffc..c77a565538453 100644 --- a/Documentation/translations/zh_CN/vm/index.rst +++ b/Documentation/translations/zh_CN/vm/index.rst @@ -12,11 +12,27 @@ Linux内存管理文档 ================= -这是一个关于Linux内存管理(mm)子系统内部的文档集,其中有不同层次的细节,包括注释 -和邮件列表的回复,用于阐述数据结构和算法的基本情况。如果你正在寻找关于简单分配内存的建 -议,请参阅(Documentation/translations/zh_CN/core-api/memory-allocation.rst)。 -对于控制和调整指南,请参阅(Documentation/admin-guide/mm/index)。 -TODO:待引用文档集被翻译完毕后请及时修改此处) +这是一份关于了解Linux的内存管理子系统的指南。如果你正在寻找关于简单分配内存的 +建议,请参阅内存分配指南 +(Documentation/translations/zh_CN/core-api/memory-allocation.rst)。 +关于控制和调整的指南,请看管理指南 +(Documentation/translations/zh_CN/admin-guide/mm/index.rst)。 + + +.. toctree:: + :maxdepth: 1 + + highmem + +该处剩余文档待原始文档有内容后翻译。 + + +遗留文档 +======== + +这是一个关于Linux内存管理(MM)子系统内部的旧文档的集合,其中有不同层次的细节, +包括注释和邮件列表的回复,用于阐述数据结构和算法的描述。它应该被很好地整合到上述 +结构化的文档中,如果它已经完成了它的使命,可以删除。 .. toctree:: :maxdepth: 1 @@ -25,7 +41,6 @@ TODO:待引用文档集被翻译完毕后请及时修改此处) balance damon/index free_page_reporting - highmem ksm frontswap hmm @@ -36,10 +51,12 @@ TODO:待引用文档集被翻译完毕后请及时修改此处) numa overcommit-accounting page_frags + page_migration page_owner page_table_check remap_file_pages split_page_table_lock + vmalloced-kernel-stacks z3fold zsmalloc @@ -47,8 +64,6 @@ TODOLIST: * arch_pgtable_helpers * free_page_reporting * hugetlbfs_reserv -* page_migration * slub * transhuge * unevictable-lru -* vmalloced-kernel-stacks diff --git a/Documentation/translations/zh_CN/vm/page_migration.rst b/Documentation/translations/zh_CN/vm/page_migration.rst new file mode 100644 index 0000000000000..566880a41ea00 --- /dev/null +++ b/Documentation/translations/zh_CN/vm/page_migration.rst @@ -0,0 +1,228 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/vm/index.rst + +:翻译: + + 司延腾 Yanteng Si <siyanteng@loongson.cn> + +:校译: + +======== +页面迁移 +======== + +页面迁移允许在进程运行时在NUMA系统的节点之间移动页面的物理位置。这意味着进程所看到的虚拟地 +址并没有改变。然而,系统会重新安排这些页面的物理位置。 + +也可以参见 :ref: `<异构内存管理 (HMM)>` 以了解将页面迁移到设备私有内存或从设备私有内存中迁移。 + +页面迁移的主要目的是通过将页面移到访问该内存的进程所运行的处理器附近来减少内存访问的延迟。 + +页面迁移允许进程通过MF_MOVE和MF_MOVE_ALL选项手动重新定位其页面所在的节点,同时通过 +mbind()设置一个新的内存策略。一个进程的页面也可以通过sys_migrate_pages()函数调用从另 +一个进程重新定位。migrate_pages()函数调用接收两组节点,并将一个进程位于旧节点上的页面移 +动到目标节点上。页面迁移功能由Andi Kleen的numactl包提供(需要0.9.3以上的版本,其仓库 +地址https://github.com/numactl/numactl.git)。numactl提供了libnuma,它为页面迁移 +提供了与其他NUMA功能类似的接口。执行 cat ``/proc/<pid>/numa_maps`` 允许轻松查看进 +程的页面位置。参见proc(5)手册中的numa_maps文档。 + +如果调度程序将一个进程重新安置到一个遥远的节点上的处理器,手动迁移是很有用的。批量调度程序 +或管理员可以检测到这种情况,并将进程的页面移到新处理器附近。内核本身只提供手动的页迁移支持。 +自动的页面迁移可以通过用户空间的进程移动页面来实现。一个特殊的函数调用 "move_pages" 允许 +在一个进程中移动单个页面。例如,NUMA分析器可以获得一个显示频繁的节点外访问的日志,并可以使 +用这个结果将页面移动到更有利的位置。 + +较大型的设备通常使用cpusets将系统分割成若干个节点。Paul Jackson为cpusets配备了当任务被 +转移到另一个cpuset时移动页面的能力(见:ref:`CPUSETS <cpusets>`)。Cpusets允许进程定 +位的自动化。如果一个任务被移到一个新的cpuset上,那么它的所有页面也会随之移动,这样进程的 +性能就不会急剧下降。如果cpuset允许的内存节点发生变化,cpuset中的进程页也会被移动。 + +页面迁移允许为所有迁移技术保留一组节点中页面的相对位置,这将保留生成的特定内存分配模式即使 +进程已被迁移。为了保留内存延迟,这一点是必要的。迁移后的进程将以类似的性能运行。 + +页面迁移分几个步骤进行。首先为那些试图从内核中使用migrate_pages()的进程做一个高层次的 +描述(对于用户空间的使用,可以参考上面提到的Andi Kleen的numactl包),然后对低水平的细 +节工作做一个低水平描述。 + +在内核中使用 migrate_pages() +============================ + +1. 从LRU中移除页面。 + + 要迁移的页面列表是通过扫描页面并把它们移到列表中来生成的。这是通过调用 isolate_lru_page() + 来完成的。调用isolate_lru_page()增加了对该页的引用,这样在页面迁移发生时它就不会 + 消失。它还可以防止交换器或其他扫描器遇到该页。 + + +2. 我们需要有一个new_page_t类型的函数,可以传递给migrate_pages()。这个函数应该计算 + 出如何在给定的旧页面中分配正确的新页面。 + +3. migrate_pages()函数被调用,它试图进行迁移。它将调用该函数为每个被考虑迁移的页面分 + 配新的页面。 + +migrate_pages()如何工作 +======================= + +migrate_pages()对它的页面列表进行了多次处理。如果当时对一个页面的所有引用都可以被移除, +那么这个页面就会被移动。该页已经通过isolate_lru_page()从LRU中移除,并且refcount被 +增加,以便在页面迁移发生时不释放该页。 + +步骤: + +1. 锁定要迁移的页面。 + +2. 确保回写已经完成。 + +3. 锁定我们要迁移到的新页面。锁定它是为了在迁移过程中立即阻止对这个(尚未更新的)页面的 + 访问。 + +4. 所有对该页的页表引用都被转换为迁移条目。这就减少了一个页面的mapcount。如果产生的 + mapcount不是零,那么我们就不迁移该页。所有试图访问该页的用户空间进程现在将等待页 + 面锁或者等待迁移页表项被移除。 + +5. i_pages的锁被持有。这将导致所有试图通过映射访问该页的进程在自旋锁上阻塞。 + +6. 检查该页的Refcount,如果还有引用,我们就退出。否则,我们知道我们是唯一引用这个页 + 面的人。 + +7. 检查基数树,如果它不包含指向这个页面的指针,那么我们就退出,因为其他人修改了基数树。 + +8. 新的页面要用旧的页面的一些设置进行预处理,这样访问新的页面就会发现一个具有正确设置 + 的页面。 + +9. 基数树被改变以指向新的页面。 + +10. 旧页的引用计数被删除,因为地址空间的引用已经消失。对新页的引用被建立,因为新页被 + 地址空间引用。 + +11. i_pages锁被放弃。这样一来,在映射中的查找又变得可能了。进程将从在锁上自旋到在 + 被锁的新页上睡眠。 + +12. 页面内容被复制到新的页面上。 + +13. 剩余的页面标志被复制到新的页面上。 + +14. 旧的页面标志被清除,以表明该页面不再提供任何信息。 + +15. 新页面上的回写队列被触发了。 + +16. 如果迁移条目被插入到页表中,那么就用真正的ptes替换它们。这样做将使那些尚未等待页 + 锁的用户空间进程能够访问。 + +17. 页面锁从新旧页面上被撤销。等待页锁的进程将重做他们的缺页异常,并将到达新的页面。 + +18. 新的页面被移到LRU中,可以被交换器等再次扫描。 + +非LRU页面迁移 +============= + +尽管迁移最初的目的是为了减少NUMA的内存访问延迟,但压缩也使用迁移来创建高阶页面。 + +目前实现的问题是,它被设计为只迁移*LRU*页。然而,有一些潜在的非LRU页面可以在驱动中 +被迁移,例如,zsmalloc,virtio-balloon页面。 + +对于virtio-balloon页面,迁移代码路径的某些部分已经被钩住,并添加了virtio-balloon +的特定函数来拦截迁移逻辑。这对一个驱动来说太特殊了,所以其他想让自己的页面可移动的驱 +动就必须在迁移路径中添加自己的特定钩子。 + +为了克服这个问题,VM支持非LRU页面迁移,它为非LRU可移动页面提供了通用函数,而在迁移 +路径中没有特定的驱动程序钩子。 + +如果一个驱动程序想让它的页面可移动,它应该定义三个函数,这些函数是 +struct address_space_operations的函数指针。 + +1. ``bool (*isolate_page) (struct page *page, isolate_mode_t mode);`` + + VM对驱动的isolate_page()函数的期望是,如果驱动成功隔离了该页,则返回*true*。 + 返回true后,VM会将该页标记为PG_isolated,这样多个CPU的并发隔离就会跳过该 + 页进行隔离。如果驱动程序不能隔离该页,它应该返回*false*。 + + 一旦页面被成功隔离,VM就会使用page.lru字段,因此驱动程序不应期望保留这些字段的值。 + +2. ``int (*migratepage) (struct address_space *mapping,`` +| ``struct page *newpage, struct page *oldpage, enum migrate_mode);`` + + 隔离后,虚拟机用隔离的页面调用驱动的migratepage()。migratepage()的功能是将旧页 + 的内容移动到新页,并设置struct page newpage的字段。请记住,如果你成功迁移了旧页 + 并返回MIGRATEPAGE_SUCCESS,你应该通过page_lock下的__ClearPageMovable()向虚 + 拟机表明旧页不再可移动。如果驱动暂时不能迁移该页,驱动可以返回-EAGAIN。在-EAGAIN + 时,VM会在短时间内重试页面迁移,因为VM将-EAGAIN理解为 "临时迁移失败"。在返回除 + -EAGAIN以外的任何错误时,VM将放弃页面迁移而不重试。 + + 在migratepage()函数中,驱动程序不应该接触page.lru字段。 + +3. ``void (*putback_page)(struct page *);`` + + 如果在隔离页上迁移失败,VM应该将隔离页返回给驱动,因此VM用隔离页调用驱动的 + putback_page()。在这个函数中,驱动应该把隔离页放回自己的数据结构中。 + +非LRU可移动页标志 + + 有两个页面标志用于支持非LRU可移动页面。 + + * PG_movable + + 驱动应该使用下面的函数来使页面在page_lock下可移动。:: + + void __SetPageMovable(struct page *page, struct address_space *mapping) + + 它需要address_space的参数来注册将被VM调用的migration family函数。确切地说, + PG_movable不是struct page的一个真正的标志。相反,VM复用了page->mapping的低 + 位来表示它:: + + #define PAGE_MAPPING_MOVABLE 0x2 + page->mapping = page->mapping | PAGE_MAPPING_MOVABLE; + + 所以驱动不应该直接访问page->mapping。相反,驱动应该使用page_mapping(),它可 + 以在页面锁下屏蔽掉page->mapping的低2位,从而获得正确的struct address_space。 + + 对于非LRU可移动页面的测试,VM支持__PageMovable()函数。然而,它并不能保证识别 + 非LRU可移动页面,因为page->mapping字段与struct page中的其他变量是统一的。如 + 果驱动程序在被虚拟机隔离后释放了页面,尽管page->mapping设置了PAGE_MAPPING_MOVABLE, + 但它并没有一个稳定的值(看看__ClearPageMovable)。但是__PageMovable()在页 + 面被隔离后,无论页面是LRU还是非LRU可移动的,调用它开销都很低,因为LRU页面在 + page->mapping中不可能有PAGE_MAPPING_MOVABLE设置。在用pfn扫描中的lock_page() + 进行更大开销的检查来选择受害者之前,它也很适合只是瞥一眼来测试非LRU可移动的页面。 + + 为了保证非LRU的可移动页面,VM提供了PageMovable()函数。与__PageMovable()不 + 同,PageMovable()在lock_page()下验证page->mapping和 + mapping->a_ops->isolate_page。lock_page()可以防止突然破坏page->mapping。 + + 使用__SetPageMovable()的驱动应该在释放页面之前通过page_lock()下的 + __ClearMovablePage()清除该标志。 + + * PG_isolated + + 为了防止几个CPU同时进行隔离,VM在lock_page()下将隔离的页面标记为PG_isolated。 + 因此,如果一个CPU遇到PG_isolated非LRU可移动页面,它可以跳过它。驱动程序不需要 + 操作这个标志,因为VM会自动设置/清除它。请记住,如果驱动程序看到PG_isolated页, + 这意味着该页已经被VM隔离,所以它不应该碰page.lru字段。PG_isolated标志与 + PG_reclaim标志是同义的,所以驱动程序不应该为自己的目的使用PG_isolated。 + +监测迁移 +======== + +以下事件(计数器)可用于监控页面迁移。 + +1. PGMIGRATE_SUCCESS: 正常的页面迁移成功。每个计数器意味着一个页面被迁移了。如果该 + 页是一个非THP和非hugetlb页,那么这个计数器会增加1。如果该页面是一个THP或hugetlb + 页面,那么这个计数器会随着THP或hugetlb子页面的数量而增加。例如,迁移一个有4KB大小 + 的基础页(子页)的2MB THP,将导致这个计数器增加512。 + +2. PGMIGRATE_FAIL: 正常的页面迁移失败。与上面PGMIGRATE_SUCCESS的计数规则相同:如 + 果是THP或hugetlb,这个计数将被子页的数量增加。 + +3. THP_MIGRATION_SUCCESS: 一个THP被迁移而没有被分割。 + +4. THP_MIGRATION_FAIL: 一个THP不能被迁移,也不能被分割。 + +5. THP_MIGRATION_SPLIT: 一个THP被迁移了,但不是这样的:首先,这个THP必须被分割。 + 在拆分之后,对它的子页面进行了迁移重试。 + +THP_MIGRATION_* 事件也会更新相应的PGMIGRATE_SUCCESS或PGMIGRATE_FAIL事件。 +例如,一个THP迁移失败将导致THP_MIGRATION_FAIL和PGMIGRATE_FAIL增加。 + +Christoph Lameter,2006年5月8日。 + +Minchan Kim,2016年3月28日。 diff --git a/Documentation/translations/zh_CN/vm/page_owner.rst b/Documentation/translations/zh_CN/vm/page_owner.rst index 9e951fabba9d0..0034d2ae1cd57 100644 --- a/Documentation/translations/zh_CN/vm/page_owner.rst +++ b/Documentation/translations/zh_CN/vm/page_owner.rst @@ -96,21 +96,82 @@ page owner在默认情况下是禁用的。所以,如果你想使用它,你� 默认情况下, ``page_owner_sort`` 是根据buf的时间来排序的。如果你想 按buf的页数排序,请使用-m参数。详细的参数是: - 基本函数: + 基本函数:: - Sort: + 排序: -a 按内存分配时间排序 -m 按总内存排序 -p 按pid排序。 -P 按tgid排序。 + -n 按任务命令名称排序。 -r 按内存释放时间排序。 -s 按堆栈跟踪排序。 -t 按时间排序(默认)。 + --sort <order> 指定排序顺序。排序的语法是[+|-]key[,[+|-]key[,...]]。从 + **标准格式指定器**那一节选择一个键。"+"是可选的,因为默认的方向是数字或 + 词法的增加。允许混合使用缩写和完整格式的键。 - 其它函数: + 例子: + ./page_owner_sort <input> <output> --sort=n,+pid,-tgid + ./page_owner_sort <input> <output> --sort=at - Cull: - -c 通过比较堆栈跟踪而不是总块来进行剔除。 + 其它函数:: - Filter: + 剔除: + --cull <rules> + 指定剔除规则。剔除的语法是key[,key[,...]]。从**标准格式指定器** + 部分选择一个多字母键。 + <rules>是一个以逗号分隔的列表形式的单一参数,它提供了一种指定单个剔除规则的 + 方法。 识别的关键字在下面的**标准格式指定器**部分有描述。<规则>可以通过键的 + 序列k1,k2,...来指定,在下面的标准排序键部分有描述。允许混合使用简写和完整形 + 式的键。 + + Examples: + ./page_owner_sort <input> <output> --cull=stacktrace + ./page_owner_sort <input> <output> --cull=st,pid,name + ./page_owner_sort <input> <output> --cull=n,f + + 过滤: -f 过滤掉内存已被释放的块的信息。 + + 选择: + --pid <pidlist> 按pid选择。这将选择进程ID号出现在<pidlist>中的块。 + --tgid <tgidlist> 按tgid选择。这将选择其线程组ID号出现在<tgidlist> + 中的块。 + --name <cmdlist> 按任务命令名称选择。这将选择其任务命令名称出现在 + <cmdlist>中的区块。 + + <pidlist>, <tgidlist>, <cmdlist>是以逗号分隔的列表形式的单个参数, + 它提供了一种指定单个选择规则的方法。 + + + 例子: + ./page_owner_sort <input> <output> --pid=1 + ./page_owner_sort <input> <output> --tgid=1,2,3 + ./page_owner_sort <input> <output> --name name1,name2 + +标准格式指定器 +============== +:: + + --sort的选项: + + 键 键长 描述 + p pid 进程ID + tg tgid 线程组ID + n name 任务命令名称 + st stacktrace 页面分配的堆栈跟踪 + T txt 块的全文 + ft free_ts 页面发布时的时间戳 + at alloc_ts 页面被分配时的时间戳 + ator allocator 页面的内存分配器 + + --curl的选项: + + 键 键长 描述 + p pid 进程ID + tg tgid 线程组ID + n name 任务命令名称 + f free 该页是否已经发布 + st stacktrace 页面分配的堆栈跟踪 + ator allocator 页面的内存分配器 diff --git a/Documentation/translations/zh_CN/vm/vmalloced-kernel-stacks.rst b/Documentation/translations/zh_CN/vm/vmalloced-kernel-stacks.rst new file mode 100644 index 0000000000000..ad23f274f6d76 --- /dev/null +++ b/Documentation/translations/zh_CN/vm/vmalloced-kernel-stacks.rst @@ -0,0 +1,133 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/vm/vmalloced-kernel-stacks.rst + +:翻译: + + 司延腾 Yanteng Si <siyanteng@loongson.cn> + +:校译: + +==================== +支持虚拟映射的内核栈 +==================== + +:作者: Shuah Khan <skhan@linuxfoundation.org> + +.. contents:: :local: + +概览 +---- + +这是介绍 `虚拟映射内核栈功能 <https://lwn.net/Articles/694348/>` 的代码 +和原始补丁系列的信息汇总。 + +简介 +---- + +内核堆栈溢出通常难以调试,并使内核容易被(恶意)利用。问题可能在稍后的时间出现,使其难以 +隔离和究其根本原因。 + +带有保护页的虚拟映射内核堆栈如果溢出,会被立即捕获,而不会放任其导致难以诊断的损 +坏。 + +HAVE_ARCH_VMAP_STACK和VMAP_STACK配置选项能够支持带有保护页的虚拟映射堆栈。 +当堆栈溢出时,这个特性会引发可靠的异常。溢出后堆栈跟踪的可用性以及对溢出本身的 +响应取决于架构。 + +.. note:: + 截至本文撰写时, arm64, powerpc, riscv, s390, um, 和 x86 支持VMAP_STACK。 + +HAVE_ARCH_VMAP_STACK +-------------------- + +能够支持虚拟映射内核栈的架构应该启用这个bool配置选项。要求是: + +- vmalloc空间必须大到足以容纳许多内核堆栈。这可能排除了许多32位架构。 +- vmalloc空间的堆栈需要可靠地工作。例如,如果vmap页表是按需创建的,当堆栈指向 + 具有未填充页表的虚拟地址时,这种机制需要工作,或者架构代码(switch_to()和 + switch_mm(),很可能)需要确保堆栈的页表项在可能未填充的堆栈上运行之前已经填 + 充。 +- 如果堆栈溢出到一个保护页,就应该发生一些合理的事情。“合理”的定义是灵活的,但 + 在没有记录任何东西的情况下立即重启是不友好的。 + +VMAP_STACK +---------- + +VMAP_STACK bool配置选项在启用时分配虚拟映射的任务栈。这个选项依赖于 +HAVE_ARCH_VMAP_STACK。 + +- 如果你想使用带有保护页的虚拟映射的内核堆栈,请启用该选项。这将导致内核栈溢出 + 被立即捕获,而不是难以诊断的损坏。 + +.. note:: + + 使用KASAN的这个功能需要架构支持用真实的影子内存来支持虚拟映射,并且 + 必须启用KASAN_VMALLOC。 + +.. note:: + + 启用VMAP_STACK时,无法在堆栈分配的数据上运行DMA。 + +内核配置选项和依赖性不断变化。请参考最新的代码库: + +`Kconfig <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/arch/Kconfig>` + +分配方法 +-------- + +当一个新的内核线程被创建时,线程堆栈是由页级分配器分配的虚拟连续的内存页组成。这 +些页面被映射到有PAGE_KERNEL保护的连续的内核虚拟空间。 + +alloc_thread_stack_node()调用__vmalloc_node_range()来分配带有PAGE_KERNEL +保护的栈。 + +- 分配的堆栈被缓存起来,以后会被新的线程重用,所以在分配/释放堆栈给任务时,要手动 + 进行memcg核算。因此,__vmalloc_node_range被调用时没有__GFP_ACCOUNT。 +- vm_struct被缓存起来,以便能够找到在中断上下文中启动的空闲线程。 free_thread_stack() + 可以在中断上下文中调用。 +- 在arm64上,所有VMAP的堆栈都需要有相同的对齐方式,以确保VMAP的堆栈溢出检测正常 + 工作。架构特定的vmap堆栈分配器照顾到了这个细节。 +- 这并不涉及中断堆栈--参考原始补丁 + +线程栈分配是由clone()、fork()、vfork()、kernel_thread()通过kernel_clone() +启动的。留点提示在这,以便搜索代码库,了解线程栈何时以及如何分配。 + +大量的代码是在: +`kernel/fork.c <https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/kernel/fork.c>`. + +task_struct中的stack_vm_area指针可以跟踪虚拟分配的堆栈,一个非空的stack_vm_area +指针可以表明虚拟映射的内核堆栈已经启用。 + +:: + + struct vm_struct *stack_vm_area; + +堆栈溢出处理 +------------ + +前守护页和后守护页有助于检测堆栈溢出。当堆栈溢出到守护页时,处理程序必须小心不要再 +次溢出堆栈。当处理程序被调用时,很可能只留下很少的堆栈空间。 + +在x86上,这是通过处理表明内核堆栈溢出的双异常堆栈的缺页异常来实现的。 + +用守护页测试VMAP分配 +-------------------- + +我们如何确保VMAP_STACK在分配时确实有前守护页和后守护页的保护?下面的 lkdtm 测试 +可以帮助检测任何回归。 + +:: + + void lkdtm_STACK_GUARD_PAGE_LEADING() + void lkdtm_STACK_GUARD_PAGE_TRAILING() + +结论 +---- + +- vmalloced堆栈的percpu缓存似乎比高阶堆栈分配要快一些,至少在缓存命中时是这样。 +- THREAD_INFO_IN_TASK完全摆脱了arch-specific thread_info,并简单地将 + thread_info(仅包含标志)和'int cpu'嵌入task_struct中。 +- 一旦任务死亡,线程栈就可以被释放(无需等待RCU),然后,如果使用vmapped栈,就 + 可以将整个栈缓存起来,以便在同一cpu上重复使用。 diff --git a/Documentation/virt/uml/user_mode_linux_howto_v2.rst b/Documentation/virt/uml/user_mode_linux_howto_v2.rst index 863f67b72c05d..af2a974296921 100644 --- a/Documentation/virt/uml/user_mode_linux_howto_v2.rst +++ b/Documentation/virt/uml/user_mode_linux_howto_v2.rst @@ -322,7 +322,7 @@ Shared Options * ``v6=[0,1]`` to specify if a v6 connection is desired for all transports which operate over IP. Additionally, for transports that have some differences in the way they operate over v4 and v6 (for example - EoL2TPv3), sets the correct mode of operation. In the absense of this + EoL2TPv3), sets the correct mode of operation. In the absence of this option, the socket type is determined based on what do the src and dst arguments resolve/parse to. diff --git a/scripts/kernel-doc b/scripts/kernel-doc index 7516949bb049e..aea04365bc69d 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -427,6 +427,13 @@ sub print_lineno { print ".. LINENO " . $lineno . "\n"; } } + +sub emit_warning { + my $location = shift; + my $msg = shift; + print STDERR "$location: warning: $msg"; + ++$warnings; +} ## # dumps section contents to arrays/hashes intended for that purpose. # @@ -451,8 +458,7 @@ sub dump_section { if (defined($sections{$name}) && ($sections{$name} ne "")) { # Only warn on user specified duplicate section names. if ($name ne $section_default) { - print STDERR "${file}:$.: warning: duplicate section name '$name'\n"; - ++$warnings; + emit_warning("${file}:$.", "duplicate section name '$name'\n"); } $sections{$name} .= $contents; } else { @@ -1094,7 +1100,7 @@ sub dump_struct($$) { if ($members) { if ($identifier ne $declaration_name) { - print STDERR "${file}:$.: warning: expecting prototype for $decl_type $identifier. Prototype was for $decl_type $declaration_name instead\n"; + emit_warning("${file}:$.", "expecting prototype for $decl_type $identifier. Prototype was for $decl_type $declaration_name instead\n"); return; } @@ -1298,9 +1304,9 @@ sub dump_enum($$) { if ($members) { if ($identifier ne $declaration_name) { if ($identifier eq "") { - print STDERR "${file}:$.: warning: wrong kernel-doc identifier on line:\n"; + emit_warning("${file}:$.", "wrong kernel-doc identifier on line:\n"); } else { - print STDERR "${file}:$.: warning: expecting prototype for enum $identifier. Prototype was for enum $declaration_name instead\n"; + emit_warning("${file}:$.", "expecting prototype for enum $identifier. Prototype was for enum $declaration_name instead\n"); } return; } @@ -1316,7 +1322,7 @@ sub dump_enum($$) { if (!$parameterdescs{$arg}) { $parameterdescs{$arg} = $undescribed; if (show_warnings("enum", $declaration_name)) { - print STDERR "${file}:$.: warning: Enum value '$arg' not described in enum '$declaration_name'\n"; + emit_warning("${file}:$.", "Enum value '$arg' not described in enum '$declaration_name'\n"); } } $_members{$arg} = 1; @@ -1325,7 +1331,7 @@ sub dump_enum($$) { while (my ($k, $v) = each %parameterdescs) { if (!exists($_members{$k})) { if (show_warnings("enum", $declaration_name)) { - print STDERR "${file}:$.: warning: Excess enum value '$k' description in '$declaration_name'\n"; + emit_warning("${file}:$.", "Excess enum value '$k' description in '$declaration_name'\n"); } } } @@ -1367,7 +1373,7 @@ sub dump_typedef($$) { $return_type =~ s/^\s+//; if ($identifier ne $declaration_name) { - print STDERR "${file}:$.: warning: expecting prototype for typedef $identifier. Prototype was for typedef $declaration_name instead\n"; + emit_warning("${file}:$.", "expecting prototype for typedef $identifier. Prototype was for typedef $declaration_name instead\n"); return; } @@ -1398,7 +1404,7 @@ sub dump_typedef($$) { $declaration_name = $1; if ($identifier ne $declaration_name) { - print STDERR "${file}:$.: warning: expecting prototype for typedef $identifier. Prototype was for typedef $declaration_name instead\n"; + emit_warning("${file}:$.", "expecting prototype for typedef $identifier. Prototype was for typedef $declaration_name instead\n"); return; } @@ -1554,9 +1560,7 @@ sub push_parameter($$$$$) { $parameterdescs{$param} = $undescribed; if (show_warnings($type, $declaration_name) && $param !~ /\./) { - print STDERR - "${file}:$.: warning: Function parameter or member '$param' not described in '$declaration_name'\n"; - ++$warnings; + emit_warning("${file}:$.", "Function parameter or member '$param' not described in '$declaration_name'\n"); } } @@ -1604,11 +1608,10 @@ sub check_sections($$$$$) { } if ($err) { if ($decl_type eq "function") { - print STDERR "${file}:$.: warning: " . + emit_warning("${file}:$.", "Excess function parameter " . "'$sects[$sx]' " . - "description in '$decl_name'\n"; - ++$warnings; + "description in '$decl_name'\n"); } } } @@ -1629,10 +1632,9 @@ sub check_return_section { if (!defined($sections{$section_return}) || $sections{$section_return} eq "") { - print STDERR "${file}:$.: warning: " . + emit_warning("${file}:$.", "No description found for return value of " . - "'$declaration_name'\n"; - ++$warnings; + "'$declaration_name'\n"); } } @@ -1714,12 +1716,12 @@ sub dump_function($$) { create_parameterlist($args, ',', $file, $declaration_name); } else { - print STDERR "${file}:$.: warning: cannot understand function prototype: '$prototype'\n"; + emit_warning("${file}:$.", "cannot understand function prototype: '$prototype'\n"); return; } if ($identifier ne $declaration_name) { - print STDERR "${file}:$.: warning: expecting prototype for $identifier(). Prototype was for $declaration_name() instead\n"; + emit_warning("${file}:$.", "expecting prototype for $identifier(). Prototype was for $declaration_name() instead\n"); return; } @@ -1801,8 +1803,8 @@ sub tracepoint_munge($) { $tracepointargs = $1; } if (($tracepointname eq 0) || ($tracepointargs eq 0)) { - print STDERR "${file}:$.: warning: Unrecognized tracepoint format: \n". - "$prototype\n"; + emit_warning("${file}:$.", "Unrecognized tracepoint format: \n". + "$prototype\n"); } else { $prototype = "static inline void trace_$tracepointname($tracepointargs)"; $identifier = "trace_$identifier"; @@ -2027,22 +2029,16 @@ sub process_name($$) { } if (!$is_kernel_comment) { - print STDERR "${file}:$.: warning: This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n"; - print STDERR $_; - ++$warnings; + emit_warning("${file}:$.", "This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n$_"); $state = STATE_NORMAL; } if (($declaration_purpose eq "") && $verbose) { - print STDERR "${file}:$.: warning: missing initial short description on line:\n"; - print STDERR $_; - ++$warnings; + emit_warning("${file}:$.", "missing initial short description on line:\n$_"); } if ($identifier eq "" && $decl_type ne "enum") { - print STDERR "${file}:$.: warning: wrong kernel-doc identifier on line:\n"; - print STDERR $_; - ++$warnings; + emit_warning("${file}:$.", "wrong kernel-doc identifier on line:\n$_"); $state = STATE_NORMAL; } @@ -2050,9 +2046,7 @@ sub process_name($$) { print STDERR "${file}:$.: info: Scanning doc for $decl_type $identifier\n"; } } else { - print STDERR "${file}:$.: warning: Cannot understand $_ on line $.", - " - I thought it was a doc line\n"; - ++$warnings; + emit_warning("${file}:$.", "Cannot understand $_ on line $. - I thought it was a doc line\n"); $state = STATE_NORMAL; } } @@ -2071,8 +2065,7 @@ sub process_body($$) { $section =~ s/\.\.\.$//; if ($verbose) { - print STDERR "${file}:$.: warning: Variable macro arguments should be documented without dots\n"; - ++$warnings; + emit_warning("${file}:$.", "Variable macro arguments should be documented without dots\n"); } } @@ -2101,8 +2094,7 @@ sub process_body($$) { if (($contents ne "") && ($contents ne "\n")) { if (!$in_doc_sect && $verbose) { - print STDERR "${file}:$.: warning: contents before sections\n"; - ++$warnings; + emit_warning("${file}:$.", "contents before sections\n"); } dump_section($file, $section, $contents); $section = $section_default; @@ -2128,8 +2120,7 @@ sub process_body($$) { } # look for doc_com + <text> + doc_end: if ($_ =~ m'\s*\*\s*[a-zA-Z_0-9:\.]+\*/') { - print STDERR "${file}:$.: warning: suspicious ending line: $_"; - ++$warnings; + emit_warning("${file}:$.", "suspicious ending line: $_"); } $prototype = ""; @@ -2173,8 +2164,7 @@ sub process_body($$) { } } else { # i dont know - bad line? ignore. - print STDERR "${file}:$.: warning: bad line: $_"; - ++$warnings; + emit_warning("${file}:$.", "bad line: $_"); } } @@ -2268,9 +2258,7 @@ sub process_inline($$) { } } elsif ($inline_doc_state == STATE_INLINE_NAME) { $inline_doc_state = STATE_INLINE_ERROR; - print STDERR "${file}:$.: warning: "; - print STDERR "Incorrect use of kernel-doc format: $_"; - ++$warnings; + emit_warning("${file}:$.", "Incorrect use of kernel-doc format: $_"); } } } @@ -2319,11 +2307,11 @@ sub process_file($) { if ($initial_section_counter == $section_counter && $ output_mode ne "none") { if ($output_selection == OUTPUT_INCLUDE) { - print STDERR "${file}:1: warning: '$_' not found\n" + emit_warning("${file}:1", "'$_' not found\n") for keys %function_table; } else { - print STDERR "${file}:1: warning: no structured comments found\n"; + emit_warning("${file}:1", "no structured comments found\n"); } } close IN_FILE; |
