From 9d7c48506518684847bc17d4e3f0a103d83aa5c8 Mon Sep 17 00:00:00 2001 From: Andreas Rammhold Date: Fri, 16 Jul 2021 22:00:34 +0200 Subject: [PATCH 001/294] tools: cpupower: fix typo in cpupower-idle-set(1) manpage The tools name was wrong in the SYNTAX section of the manpage it should read "idle-set" instead of "idle-info". Signed-off-by: Andreas Rammhold Signed-off-by: Shuah Khan --- tools/power/cpupower/man/cpupower-idle-set.1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/power/cpupower/man/cpupower-idle-set.1 b/tools/power/cpupower/man/cpupower-idle-set.1 index 21916cff7516..8cef3c71e19e 100644 --- a/tools/power/cpupower/man/cpupower-idle-set.1 +++ b/tools/power/cpupower/man/cpupower-idle-set.1 @@ -4,7 +4,7 @@ cpupower\-idle\-set \- Utility to set cpu idle state specific kernel options .SH "SYNTAX" .LP -cpupower [ \-c cpulist ] idle\-info [\fIoptions\fP] +cpupower [ \-c cpulist ] idle\-set [\fIoptions\fP] .SH "DESCRIPTION" .LP The cpupower idle\-set subcommand allows to set cpu idle, also called cpu From 101025ff8e47d3c938ad2ae646a1794b9a8aa730 Mon Sep 17 00:00:00 2001 From: ozkanonur Date: Thu, 13 Jan 2022 00:04:21 +0300 Subject: [PATCH 002/294] tools/power/cpupower/{ToDo => TODO}: Rename the todo file Renamed the to-do file to 'TODO' instead of 'ToDo' to comply with the naming standard. Signed-off-by: ozkanonur Signed-off-by: Shuah Khan --- tools/power/cpupower/{ToDo => TODO} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename tools/power/cpupower/{ToDo => TODO} (100%) diff --git a/tools/power/cpupower/ToDo b/tools/power/cpupower/TODO similarity index 100% rename from tools/power/cpupower/ToDo rename to tools/power/cpupower/TODO From b1bbd3a57b94889cd17147f5594db7f0652275ef Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 17 Jan 2022 16:48:21 -0800 Subject: [PATCH 003/294] thermal: fix Documentation bullet list warning Fix bullet list indentation formatting to prevent a docs build warning. Use same indentation style as other files in this directory. Documentation/ABI/testing/sysfs-class-thermal:201: WARNING: Bullet list ends without a blank line; unexpected unindent. Fixes: 80da1b508f29 ("thermal: Move ABI documentation to Documentation/ABI") Signed-off-by: Randy Dunlap Reported-by: Stephen Rothwell Signed-off-by: Rafael J. Wysocki --- Documentation/ABI/testing/sysfs-class-thermal | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/ABI/testing/sysfs-class-thermal b/Documentation/ABI/testing/sysfs-class-thermal index 2c52bb1f864c..8eee37982b2a 100644 --- a/Documentation/ABI/testing/sysfs-class-thermal +++ b/Documentation/ABI/testing/sysfs-class-thermal @@ -203,7 +203,7 @@ Description: - for generic ACPI: should be "Fan", "Processor" or "LCD" - for memory controller device on intel_menlow platform: - should be "Memory controller". + should be "Memory controller". RO, Required From ae57857b9b6341096ddfd9c0cf26fb640c561160 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 11 Jan 2022 16:55:25 +0100 Subject: [PATCH 004/294] ACPICA: Use uintptr_t and offsetof() in Linux kernel builds To avoid "performing pointer subtraction with a null pointer has undefined behavior" compiler warnings, use uintptr_t and offsetof() that are always available during Linux kernel builds to define acpi_uintptr_t and the ACPI_TO_INTEGER() and ACPI_OFFSET() macros. Based on earlier proposal from Arnd Bergmann. Link: https://lore.kernel.org/linux-acpi/20210927121338.938994-1-arnd@kernel.org Signed-off-by: Rafael J. Wysocki Reviewed-by: Arnd Bergmann --- include/acpi/actypes.h | 4 ++++ include/acpi/platform/aclinux.h | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/include/acpi/actypes.h b/include/acpi/actypes.h index 69e89d572b9e..02c1fa16e638 100644 --- a/include/acpi/actypes.h +++ b/include/acpi/actypes.h @@ -507,8 +507,12 @@ typedef u64 acpi_integer; /* Pointer/Integer type conversions */ #define ACPI_TO_POINTER(i) ACPI_CAST_PTR (void, (acpi_size) (i)) +#ifndef ACPI_TO_INTEGER #define ACPI_TO_INTEGER(p) ACPI_PTR_DIFF (p, (void *) 0) +#endif +#ifndef ACPI_OFFSET #define ACPI_OFFSET(d, f) ACPI_PTR_DIFF (&(((d *) 0)->f), (void *) 0) +#endif #define ACPI_PTR_TO_PHYSADDR(i) ACPI_TO_INTEGER(i) /* Optimizations for 4-character (32-bit) acpi_name manipulation */ diff --git a/include/acpi/platform/aclinux.h b/include/acpi/platform/aclinux.h index b3ffb9bbf664..cec41e004ecf 100644 --- a/include/acpi/platform/aclinux.h +++ b/include/acpi/platform/aclinux.h @@ -114,6 +114,11 @@ #define acpi_raw_spinlock raw_spinlock_t * #define acpi_cpu_flags unsigned long +#define acpi_uintptr_t uintptr_t + +#define ACPI_TO_INTEGER(p) ((uintptr_t)(p)) +#define ACPI_OFFSET(d, f) offsetof(d, f) + /* Use native linux version of acpi_os_allocate_zeroed */ #define USE_NATIVE_ALLOCATE_ZEROED From 2e433a94dab0246fee706d18aaecd67007ead404 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 12 Jan 2022 19:31:48 +0100 Subject: [PATCH 005/294] ACPI: OSL: Fix and clean up acpi_os_read/write_port() First, remove type casts that make acpi_os_read_port() only work on little endian and are generally not needed. Second, avoid clearing the memory pointed to by the value return pointer in acpi_os_read_port() if it is the dummy on the stack (in which case clearing it is not necessary). Finally, prevent both acpi_os_read_port() and acpi_os_write_port() from crashing the kernel when they receive an unsupported width value and make them print a debug message and return an error instead. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/osl.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/drivers/acpi/osl.c b/drivers/acpi/osl.c index 45c5c0e45e33..7a70c4bfc23c 100644 --- a/drivers/acpi/osl.c +++ b/drivers/acpi/osl.c @@ -642,22 +642,24 @@ u64 acpi_os_get_timer(void) (ACPI_100NSEC_PER_SEC / HZ); } -acpi_status acpi_os_read_port(acpi_io_address port, u32 * value, u32 width) +acpi_status acpi_os_read_port(acpi_io_address port, u32 *value, u32 width) { u32 dummy; - if (!value) + if (value) + *value = 0; + else value = &dummy; - *value = 0; if (width <= 8) { - *(u8 *) value = inb(port); + *value = inb(port); } else if (width <= 16) { - *(u16 *) value = inw(port); + *value = inw(port); } else if (width <= 32) { - *(u32 *) value = inl(port); + *value = inl(port); } else { - BUG(); + pr_debug("%s: Access width %d not supported\n", __func__, width); + return AE_BAD_PARAMETER; } return AE_OK; @@ -674,7 +676,8 @@ acpi_status acpi_os_write_port(acpi_io_address port, u32 value, u32 width) } else if (width <= 32) { outl(value, port); } else { - BUG(); + pr_debug("%s: Access width %d not supported\n", __func__, width); + return AE_BAD_PARAMETER; } return AE_OK; From babc92da5928f81af951663fc436997352e02d3a Mon Sep 17 00:00:00 2001 From: Sakari Ailus Date: Fri, 14 Jan 2022 13:24:49 +0200 Subject: [PATCH 006/294] ACPI: properties: Consistently return -ENOENT if there are no more references __acpi_node_get_property_reference() is documented to return -ENOENT if the caller requests a property reference at an index that does not exist, not -EINVAL which it actually does. Fix this by returning -ENOENT consistenly, independently of whether the property value is a plain reference or a package. Fixes: c343bc2ce2c6 ("ACPI: properties: Align return codes of __acpi_node_get_property_reference()") Cc: 4.14+ # 4.14+ Signed-off-by: Sakari Ailus Signed-off-by: Rafael J. Wysocki --- drivers/acpi/property.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index d0986bda2964..3fceb4681ec9 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -685,7 +685,7 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, */ if (obj->type == ACPI_TYPE_LOCAL_REFERENCE) { if (index) - return -EINVAL; + return -ENOENT; device = acpi_fetch_acpi_dev(obj->reference.handle); if (!device) From c75c6a8add370cde88117901f21cf0d6eaf09a8f Mon Sep 17 00:00:00 2001 From: Tang Yizhou Date: Wed, 29 Dec 2021 10:42:10 +0800 Subject: [PATCH 007/294] docs/zh_CN: Cleanup index.rst Many */index in the Chinese index.rst are not in the same order as the English version. Put them to where they should be. Signed-off-by: Tang Yizhou Reviewed-by: Yanteng Si Link: https://lore.kernel.org/r/20211229024212.32752-2-tangyizhou@huawei.com Signed-off-by: Jonathan Corbet --- Documentation/translations/zh_CN/index.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/translations/zh_CN/index.rst b/Documentation/translations/zh_CN/index.rst index 46e14ec9963d..e7f5fa3e7502 100644 --- a/Documentation/translations/zh_CN/index.rst +++ b/Documentation/translations/zh_CN/index.rst @@ -104,13 +104,13 @@ TODOList: :maxdepth: 2 core-api/index + accounting/index cpu-freq/index iio/index + infiniband/index + virt/index sound/index filesystems/index - virt/index - infiniband/index - accounting/index scheduler/index TODOList: From 30e61d38f01d77d6d1f76c7566e70d46eff3724d Mon Sep 17 00:00:00 2001 From: Tang Yizhou Date: Wed, 29 Dec 2021 10:42:11 +0800 Subject: [PATCH 008/294] docs/zh_CN: Add power/index Chinese translation Translate power/index.rst into Chinese. Signed-off-by: Tang Yizhou Reviewed-by: Alex Shi Reviewed-by: Yanteng Si Link: https://lore.kernel.org/r/20211229024212.32752-3-tangyizhou@huawei.com Signed-off-by: Jonathan Corbet --- Documentation/translations/zh_CN/index.rst | 2 +- .../translations/zh_CN/power/index.rst | 55 +++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/power/index.rst diff --git a/Documentation/translations/zh_CN/index.rst b/Documentation/translations/zh_CN/index.rst index e7f5fa3e7502..08144252630c 100644 --- a/Documentation/translations/zh_CN/index.rst +++ b/Documentation/translations/zh_CN/index.rst @@ -108,6 +108,7 @@ TODOList: cpu-freq/index iio/index infiniband/index + power/index virt/index sound/index filesystems/index @@ -129,7 +130,6 @@ TODOList: * netlabel/index * networking/index * pcmcia/index -* power/index * target/index * timers/index * spi/index diff --git a/Documentation/translations/zh_CN/power/index.rst b/Documentation/translations/zh_CN/power/index.rst new file mode 100644 index 000000000000..fd379adfda8e --- /dev/null +++ b/Documentation/translations/zh_CN/power/index.rst @@ -0,0 +1,55 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/power/index.rst + +:翻译: + + 唐艺舟 Tang Yizhou + +======== +电源管理 +======== + +.. toctree:: + :maxdepth: 1 + +TODOList: + + * apm-acpi + * basic-pm-debugging + * charger-manager + * drivers-testing + * energy-model + * freezing-of-tasks + * opp + * pci + * pm_qos_interface + * power_supply_class + * runtime_pm + * s2ram + * suspend-and-cpuhotplug + * suspend-and-interrupts + * swsusp-and-swap-files + * swsusp-dmcrypt + * swsusp + * video + * tricks + + * userland-swsusp + + * powercap/powercap + * powercap/dtpm + + * regulator/consumer + * regulator/design + * regulator/machine + * regulator/overview + * regulator/regulator + +.. only:: subproject and html + + Indices + ======= + + * :ref:`genindex` From dd774a07ddfcb4dfe15778ea30cd5bb592ffab29 Mon Sep 17 00:00:00 2001 From: Tang Yizhou Date: Wed, 29 Dec 2021 10:42:12 +0800 Subject: [PATCH 009/294] docs/zh_CN: Add opp Chinese translation Translate power/opp.rst into Chinese. Signed-off-by: Tang Yizhou Reviewed-by: Alex Shi Reviewed-by: Yanteng Si Link: https://lore.kernel.org/r/20211229024212.32752-4-tangyizhou@huawei.com Signed-off-by: Jonathan Corbet --- .../translations/zh_CN/power/index.rst | 3 +- .../translations/zh_CN/power/opp.rst | 341 ++++++++++++++++++ 2 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/power/opp.rst diff --git a/Documentation/translations/zh_CN/power/index.rst b/Documentation/translations/zh_CN/power/index.rst index fd379adfda8e..ad80a9e80b7c 100644 --- a/Documentation/translations/zh_CN/power/index.rst +++ b/Documentation/translations/zh_CN/power/index.rst @@ -14,6 +14,8 @@ .. toctree:: :maxdepth: 1 + opp + TODOList: * apm-acpi @@ -22,7 +24,6 @@ TODOList: * drivers-testing * energy-model * freezing-of-tasks - * opp * pci * pm_qos_interface * power_supply_class diff --git a/Documentation/translations/zh_CN/power/opp.rst b/Documentation/translations/zh_CN/power/opp.rst new file mode 100644 index 000000000000..8d6e3f6f6202 --- /dev/null +++ b/Documentation/translations/zh_CN/power/opp.rst @@ -0,0 +1,341 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/power/opp.rst + +:翻译: + + 唐艺舟 Tang Yizhou + +====================== +操作性能值(OPP)库 +====================== + +(C) 2009-2010 Nishanth Menon , 德州仪器公司 + +.. 目录 + + 1. 简介 + 2. OPP链表初始注册 + 3. OPP搜索函数 + 4. OPP可用性控制函数 + 5. OPP数据检索函数 + 6. 数据结构 + +1. 简介 +======= + +1.1 何为操作性能值(OPP)? +------------------------------ + +当今复杂的单片系统(SoC)由多个子模块组成,这些子模块会联合工作。在一个执行不同用例 +的操作系统中,并不是SoC中的所有模块都需要一直以最高频率工作。为了促成这一点,SoC中 +的子模块被分组为不同域,允许一些域以较低的电压和频率运行,而其它域则以较高的“电压/ +频率对”运行。 + +设备按域支持的由频率电压对组成的离散的元组的集合,被称为操作性能值(组),或OPPs。 + +举例来说: + +让我们考虑一个支持下述频率、电压值的内存保护单元(MPU)设备: +{300MHz,最低电压为1V}, {800MHz,最低电压为1.2V}, {1GHz,最低电压为1.3V} + +我们能将它们表示为3个OPP,如下述{Hz, uV}元组(译注:频率的单位是赫兹,电压的单位是 +微伏)。 + +- {300000000, 1000000} +- {800000000, 1200000} +- {1000000000, 1300000} + +1.2 操作性能值库 +---------------- + +OPP库提供了一组辅助函数来组织和查询OPP信息。该库位于drivers/opp/目录下,其头文件 +位于include/linux/pm_opp.h中。OPP库可以通过开启CONFIG_PM_OPP来启用。某些SoC, +如德州仪器的OMAP框架允许在不需要cpufreq的情况下可选地在某一OPP下启动。 + +OPP库的典型用法如下:: + + (用户) -> 注册一个默认的OPP集合 -> (库) + (SoC框架) -> 在必要的情况下,对某些OPP进行修改 -> OPP layer + -> 搜索/检索信息的查询 -> + +OPP层期望每个域由一个唯一的设备指针来表示。SoC框架在OPP层为每个设备注册了一组初始 +OPP。这个链表的长度被期望是一个最优化的小数字,通常每个设备大约5个。初始链表包含了 +一个OPP集合,这个集合被期望能在系统中安全使能。 + +关于OPP可用性的说明 +^^^^^^^^^^^^^^^^^^^ + +随着系统的运行,SoC框架可能会基于各种外部因素选择让某些OPP在每个设备上可用或不可用, +示例:温度管理或其它异常场景中,SoC框架可能会选择禁用一个较高频率的OPP以安全地继续 +运行,直到该OPP被重新启用(如果可能)。 + +OPP库在它的实现中达成了这个概念。以下操作函数只能对可用的OPP使用: +dev_pm_opp_find_freq_{ceil, floor}, dev_pm_opp_get_voltage, +dev_pm_opp_get_freq, dev_pm_opp_get_opp_count。 + +dev_pm_opp_find_freq_exact是用来查找OPP指针的,该指针可被用在dev_pm_opp_enable/ +disable函数,使一个OPP在被需要时变为可用。 + +警告:如果对一个设备调用dev_pm_opp_enable/disable函数,OPP库的用户应该使用 +dev_pm_opp_get_opp_count来刷新OPP的可用性计数。触发这些的具体机制,或者对有依赖的 +子系统(比如cpufreq)的通知机制,都是由使用OPP库的SoC特定框架酌情处理的。在这些操作 +中,同样需要注意刷新cpufreq表。 + +2. OPP链表初始注册 +================== +SoC的实现会迭代调用dev_pm_opp_add函数来增加每个设备的OPP。预期SoC框架将以最优的 +方式注册OPP条目 - 典型的数字范围小于5。通过注册OPP生成的OPP链表,在整个设备运行过程 +中由OPP库维护。SoC框架随后可以使用dev_pm_opp_enable / disable函数动态地 +控制OPP的可用性。 + +dev_pm_opp_add + 为设备指针所指向的特定域添加一个新的OPP。OPP是用频率和电压定义的。一旦完成 + 添加,OPP被认为是可用的,可以用dev_pm_opp_enable/disable函数来控制其可用性。 + OPP库内部用dev_pm_opp结构体存储并管理这些信息。这个函数可以被SoC框架根据SoC + 的使用环境的需求来定义一个最优链表。 + + 警告: + 不要在中断上下文使用这个函数。 + + 示例:: + + soc_pm_init() + { + /* 做一些事情 */ + r = dev_pm_opp_add(mpu_dev, 1000000, 900000); + if (!r) { + pr_err("%s: unable to register mpu opp(%d)\n", r); + goto no_cpufreq; + } + /* 做一些和cpufreq相关的事情 */ + no_cpufreq: + /* 做剩余的事情 */ + } + +3. OPP搜索函数 +============== +cpufreq等高层框架对频率进行操作,为了将频率映射到相应的OPP,OPP库提供了便利的函数 +来搜索OPP库内部管理的OPP链表。这些搜索函数如果找到匹配的OPP,将返回指向该OPP的指针, +否则返回错误。这些错误预计由标准的错误检查,如IS_ERR()来处理,并由调用者采取适当的 +行动。 + +这些函数的调用者应在使用完OPP后调用dev_pm_opp_put()。否则,OPP的内存将永远不会 +被释放,并导致内存泄露。 + +dev_pm_opp_find_freq_exact + 根据 *精确的* 频率和可用性来搜索OPP。这个函数对默认不可用的OPP特别有用。 + 例子:在SoC框架检测到更高频率可用的情况下,它可以使用这个函数在调用 + dev_pm_opp_enable之前找到OPP:: + + opp = dev_pm_opp_find_freq_exact(dev, 1000000000, false); + dev_pm_opp_put(opp); + /* 不要操作指针.. 只是做有效性检查.. */ + if (IS_ERR(opp)) { + pr_err("frequency not disabled!\n"); + /* 触发合适的操作.. */ + } else { + dev_pm_opp_enable(dev,1000000000); + } + + 注意: + 这是唯一一个可以搜索不可用OPP的函数。 + +dev_pm_opp_find_freq_floor + 搜索一个 *最多* 提供指定频率的可用OPP。这个函数在搜索较小的匹配或按频率 + 递减的顺序操作OPP信息时很有用。 + 例子:要找的一个设备的最高OPP:: + + freq = ULONG_MAX; + opp = dev_pm_opp_find_freq_floor(dev, &freq); + dev_pm_opp_put(opp); + +dev_pm_opp_find_freq_ceil + 搜索一个 *最少* 提供指定频率的可用OPP。这个函数在搜索较大的匹配或按频率 + 递增的顺序操作OPP信息时很有用。 + 例1:找到一个设备最小的OPP:: + + freq = 0; + opp = dev_pm_opp_find_freq_ceil(dev, &freq); + dev_pm_opp_put(opp); + + 例: 一个SoC的cpufreq_driver->target的简易实现:: + + soc_cpufreq_target(..) + { + /* 做策略检查等操作 */ + /* 找到和请求最接近的频率 */ + opp = dev_pm_opp_find_freq_ceil(dev, &freq); + dev_pm_opp_put(opp); + if (!IS_ERR(opp)) + soc_switch_to_freq_voltage(freq); + else + /* 当不能满足请求时,要做的事 */ + /* 做其它事 */ + } + +4. OPP可用性控制函数 +==================== +在OPP库中注册的默认OPP链表也许无法满足所有可能的场景。OPP库提供了一套函数来修改 +OPP链表中的某个OPP的可用性。这使得SoC框架能够精细地动态控制哪一组OPP是可用于操作 +的。设计这些函数的目的是在诸如考虑温度时 *暂时地* 删除某个OPP(例如,在温度下降 +之前不要使用某OPP)。 + +警告: + 不要在中断上下文使用这些函数。 + +dev_pm_opp_enable + 使一个OPP可用于操作。 + 例子:假设1GHz的OPP只有在SoC温度低于某个阈值时才可用。SoC框架的实现可能 + 会选择做以下事情:: + + if (cur_temp < temp_low_thresh) { + /* 若1GHz未使能,则使能 */ + opp = dev_pm_opp_find_freq_exact(dev, 1000000000, false); + dev_pm_opp_put(opp); + /* 仅仅是错误检查 */ + if (!IS_ERR(opp)) + ret = dev_pm_opp_enable(dev, 1000000000); + else + goto try_something_else; + } + +dev_pm_opp_disable + 使一个OPP不可用于操作。 + 例子:假设1GHz的OPP只有在SoC温度高于某个阈值时才可用。SoC框架的实现可能 + 会选择做以下事情:: + + if (cur_temp > temp_high_thresh) { + /* 若1GHz已使能,则关闭 */ + opp = dev_pm_opp_find_freq_exact(dev, 1000000000, true); + dev_pm_opp_put(opp); + /* 仅仅是错误检查 */ + if (!IS_ERR(opp)) + ret = dev_pm_opp_disable(dev, 1000000000); + else + goto try_something_else; + } + +5. OPP数据检索函数 +================== +由于OPP库对OPP信息进行了抽象化处理,因此需要一组函数来从dev_pm_opp结构体中提取 +信息。一旦使用搜索函数检索到一个OPP指针,以下函数就可以被SoC框架用来检索OPP层 +内部描述的信息。 + +dev_pm_opp_get_voltage + 检索OPP指针描述的电压。 + 例子: 当cpufreq切换到到不同频率时,SoC框架需要用稳压器框架将OPP描述 + 的电压设置到提供电压的电源管理芯片中:: + + soc_switch_to_freq_voltage(freq) + { + /* 做一些事情 */ + opp = dev_pm_opp_find_freq_ceil(dev, &freq); + v = dev_pm_opp_get_voltage(opp); + dev_pm_opp_put(opp); + if (v) + regulator_set_voltage(.., v); + /* 做其它事 */ + } + +dev_pm_opp_get_freq + 检索OPP指针描述的频率。 + 例子:比方说,SoC框架使用了几个辅助函数,通过这些函数,我们可以将OPP + 指针传入,而不是传入额外的参数,用来处理一系列数据参数:: + + soc_cpufreq_target(..) + { + /* 做一些事情.. */ + max_freq = ULONG_MAX; + max_opp = dev_pm_opp_find_freq_floor(dev,&max_freq); + requested_opp = dev_pm_opp_find_freq_ceil(dev,&freq); + if (!IS_ERR(max_opp) && !IS_ERR(requested_opp)) + r = soc_test_validity(max_opp, requested_opp); + dev_pm_opp_put(max_opp); + dev_pm_opp_put(requested_opp); + /* 做其它事 */ + } + soc_test_validity(..) + { + if(dev_pm_opp_get_voltage(max_opp) < dev_pm_opp_get_voltage(requested_opp)) + return -EINVAL; + if(dev_pm_opp_get_freq(max_opp) < dev_pm_opp_get_freq(requested_opp)) + return -EINVAL; + /* 做一些事情.. */ + } + +dev_pm_opp_get_opp_count + 检索某个设备可用的OPP数量。 + 例子:假设SoC中的一个协处理器需要知道某个表中的可用频率,主处理器可以 + 按如下方式发出通知:: + + soc_notify_coproc_available_frequencies() + { + /* 做一些事情 */ + num_available = dev_pm_opp_get_opp_count(dev); + speeds = kzalloc(sizeof(u32) * num_available, GFP_KERNEL); + /* 按升序填充表 */ + freq = 0; + while (!IS_ERR(opp = dev_pm_opp_find_freq_ceil(dev, &freq))) { + speeds[i] = freq; + freq++; + i++; + dev_pm_opp_put(opp); + } + + soc_notify_coproc(AVAILABLE_FREQs, speeds, num_available); + /* 做其它事 */ + } + +6. 数据结构 +=========== +通常,一个SoC包含多个可变电压域。每个域由一个设备指针描述。和OPP之间的关系可以 +按以下方式描述:: + + SoC + |- device 1 + | |- opp 1 (availability, freq, voltage) + | |- opp 2 .. + ... ... + | `- opp n .. + |- device 2 + ... + `- device m + +OPP库维护着一个内部链表,SoC框架使用上文描述的各个函数来填充和访问。然而,描述 +真实OPP和域的结构体是OPP库自身的内部组成,以允许合适的抽象在不同系统中得到复用。 + +struct dev_pm_opp + OPP库的内部数据结构,用于表示一个OPP。除了频率、电压、可用性信息外, + 它还包含OPP库运行所需的内部统计信息。指向这个结构体的指针被提供给 + 用户(比如SoC框架)使用,在与OPP层的交互中作为OPP的标识符。 + + 警告: + 结构体dev_pm_opp的指针不应该由用户解析或修改。一个实例的默认值由 + dev_pm_opp_add填充,但OPP的可用性由dev_pm_opp_enable/disable函数 + 修改。 + +struct device + 这用于向OPP层标识一个域。设备的性质和它的实现是由OPP库的用户决定的, + 如SoC框架。 + +总体来说,以一个简化的视角看,对数据结构的操作可以描述为下面各图:: + + 初始化 / 修改: + +-----+ /- dev_pm_opp_enable + dev_pm_opp_add --> | opp | <------- + | +-----+ \- dev_pm_opp_disable + \-------> domain_info(device) + + 搜索函数: + /-- dev_pm_opp_find_freq_ceil ---\ +-----+ + domain_info<---- dev_pm_opp_find_freq_exact -----> | opp | + \-- dev_pm_opp_find_freq_floor ---/ +-----+ + + 检索函数: + +-----+ /- dev_pm_opp_get_voltage + | opp | <--- + +-----+ \- dev_pm_opp_get_freq + + domain_info <- dev_pm_opp_get_opp_count From bf026e2e316ba57135b70e8ce591276239c7b2cf Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Mon, 10 Jan 2022 11:50:43 +0100 Subject: [PATCH 010/294] Documentation: Fill the gaps about entry/noinstr constraints The entry/exit handling for exceptions, interrupts, syscalls and KVM is not really documented except for some comments. Fill the gaps. Signed-off-by: Thomas Gleixner Signed-off-by: Nicolas Saenz Julienne Reviewed-by: Mark Rutland Reviewed-by: Paul E. McKenney ---- Changes since v3: - s/nointr/noinstr/ Changes since v2: - No big content changes, just style corrections, so it should be pretty clean at this stage. In the light of this, I kept Mark's Reviewed-by. - Paul's style and paragraph re-writes - Randy's style comments - Add links to transition type sections Documentation/core-api/entry.rst | 261 +++++++++++++++++++++++++++++++ Documentation/core-api/index.rst | 8 + 2 files changed, 269 insertions(+) create mode 100644 Documentation/core-api/entry.rst Reviewed-by: Frederic Weisbecker Link: https://lore.kernel.org/r/20220110105044.94423-1-nsaenzju@redhat.com Signed-off-by: Jonathan Corbet --- Documentation/core-api/entry.rst | 261 +++++++++++++++++++++++++++++++ Documentation/core-api/index.rst | 8 + 2 files changed, 269 insertions(+) create mode 100644 Documentation/core-api/entry.rst diff --git a/Documentation/core-api/entry.rst b/Documentation/core-api/entry.rst new file mode 100644 index 000000000000..c6f8e22c88fe --- /dev/null +++ b/Documentation/core-api/entry.rst @@ -0,0 +1,261 @@ +Entry/exit handling for exceptions, interrupts, syscalls and KVM +================================================================ + +All transitions between execution domains require state updates which are +subject to strict ordering constraints. State updates are required for the +following: + + * Lockdep + * RCU / Context tracking + * Preemption counter + * Tracing + * Time accounting + +The update order depends on the transition type and is explained below in +the transition type sections: `Syscalls`_, `KVM`_, `Interrupts and regular +exceptions`_, `NMI and NMI-like exceptions`_. + +Non-instrumentable code - noinstr +--------------------------------- + +Most instrumentation facilities depend on RCU, so intrumentation is prohibited +for entry code before RCU starts watching and exit code after RCU stops +watching. In addition, many architectures must save and restore register state, +which means that (for example) a breakpoint in the breakpoint entry code would +overwrite the debug registers of the initial breakpoint. + +Such code must be marked with the 'noinstr' attribute, placing that code into a +special section inaccessible to instrumentation and debug facilities. Some +functions are partially instrumentable, which is handled by marking them +noinstr and using instrumentation_begin() and instrumentation_end() to flag the +instrumentable ranges of code: + +.. code-block:: c + + noinstr void entry(void) + { + handle_entry(); // <-- must be 'noinstr' or '__always_inline' + ... + + instrumentation_begin(); + handle_context(); // <-- instrumentable code + instrumentation_end(); + + ... + handle_exit(); // <-- must be 'noinstr' or '__always_inline' + } + +This allows verification of the 'noinstr' restrictions via objtool on +supported architectures. + +Invoking non-instrumentable functions from instrumentable context has no +restrictions and is useful to protect e.g. state switching which would +cause malfunction if instrumented. + +All non-instrumentable entry/exit code sections before and after the RCU +state transitions must run with interrupts disabled. + +Syscalls +-------- + +Syscall-entry code starts in assembly code and calls out into low-level C code +after establishing low-level architecture-specific state and stack frames. This +low-level C code must not be instrumented. A typical syscall handling function +invoked from low-level assembly code looks like this: + +.. code-block:: c + + noinstr void syscall(struct pt_regs *regs, int nr) + { + arch_syscall_enter(regs); + nr = syscall_enter_from_user_mode(regs, nr); + + instrumentation_begin(); + if (!invoke_syscall(regs, nr) && nr != -1) + result_reg(regs) = __sys_ni_syscall(regs); + instrumentation_end(); + + syscall_exit_to_user_mode(regs); + } + +syscall_enter_from_user_mode() first invokes enter_from_user_mode() which +establishes state in the following order: + + * Lockdep + * RCU / Context tracking + * Tracing + +and then invokes the various entry work functions like ptrace, seccomp, audit, +syscall tracing, etc. After all that is done, the instrumentable invoke_syscall +function can be invoked. The instrumentable code section then ends, after which +syscall_exit_to_user_mode() is invoked. + +syscall_exit_to_user_mode() handles all work which needs to be done before +returning to user space like tracing, audit, signals, task work etc. After +that it invokes exit_to_user_mode() which again handles the state +transition in the reverse order: + + * Tracing + * RCU / Context tracking + * Lockdep + +syscall_enter_from_user_mode() and syscall_exit_to_user_mode() are also +available as fine grained subfunctions in cases where the architecture code +has to do extra work between the various steps. In such cases it has to +ensure that enter_from_user_mode() is called first on entry and +exit_to_user_mode() is called last on exit. + + +KVM +--- + +Entering or exiting guest mode is very similar to syscalls. From the host +kernel point of view the CPU goes off into user space when entering the +guest and returns to the kernel on exit. + +kvm_guest_enter_irqoff() is a KVM-specific variant of exit_to_user_mode() +and kvm_guest_exit_irqoff() is the KVM variant of enter_from_user_mode(). +The state operations have the same ordering. + +Task work handling is done separately for guest at the boundary of the +vcpu_run() loop via xfer_to_guest_mode_handle_work() which is a subset of +the work handled on return to user space. + +Interrupts and regular exceptions +--------------------------------- + +Interrupts entry and exit handling is slightly more complex than syscalls +and KVM transitions. + +If an interrupt is raised while the CPU executes in user space, the entry +and exit handling is exactly the same as for syscalls. + +If the interrupt is raised while the CPU executes in kernel space the entry and +exit handling is slightly different. RCU state is only updated when the +interrupt is raised in the context of the CPU's idle task. Otherwise, RCU will +already be watching. Lockdep and tracing have to be updated unconditionally. + +irqentry_enter() and irqentry_exit() provide the implementation for this. + +The architecture-specific part looks similar to syscall handling: + +.. code-block:: c + + noinstr void interrupt(struct pt_regs *regs, int nr) + { + arch_interrupt_enter(regs); + state = irqentry_enter(regs); + + instrumentation_begin(); + + irq_enter_rcu(); + invoke_irq_handler(regs, nr); + irq_exit_rcu(); + + instrumentation_end(); + + irqentry_exit(regs, state); + } + +Note that the invocation of the actual interrupt handler is within a +irq_enter_rcu() and irq_exit_rcu() pair. + +irq_enter_rcu() updates the preemption count which makes in_hardirq() +return true, handles NOHZ tick state and interrupt time accounting. This +means that up to the point where irq_enter_rcu() is invoked in_hardirq() +returns false. + +irq_exit_rcu() handles interrupt time accounting, undoes the preemption +count update and eventually handles soft interrupts and NOHZ tick state. + +In theory, the preemption count could be updated in irqentry_enter(). In +practice, deferring this update to irq_enter_rcu() allows the preemption-count +code to be traced, while also maintaining symmetry with irq_exit_rcu() and +irqentry_exit(), which are described in the next paragraph. The only downside +is that the early entry code up to irq_enter_rcu() must be aware that the +preemption count has not yet been updated with the HARDIRQ_OFFSET state. + +Note that irq_exit_rcu() must remove HARDIRQ_OFFSET from the preemption count +before it handles soft interrupts, whose handlers must run in BH context rather +than irq-disabled context. In addition, irqentry_exit() might schedule, which +also requires that HARDIRQ_OFFSET has been removed from the preemption count. + +NMI and NMI-like exceptions +--------------------------- + +NMIs and NMI-like exceptions (machine checks, double faults, debug +interrupts, etc.) can hit any context and must be extra careful with +the state. + +State changes for debug exceptions and machine-check exceptions depend on +whether these exceptions happened in user-space (breakpoints or watchpoints) or +in kernel mode (code patching). From user-space, they are treated like +interrupts, while from kernel mode they are treated like NMIs. + +NMIs and other NMI-like exceptions handle state transitions without +distinguishing between user-mode and kernel-mode origin. + +The state update on entry is handled in irqentry_nmi_enter() which updates +state in the following order: + + * Preemption counter + * Lockdep + * RCU / Context tracking + * Tracing + +The exit counterpart irqentry_nmi_exit() does the reverse operation in the +reverse order. + +Note that the update of the preemption counter has to be the first +operation on enter and the last operation on exit. The reason is that both +lockdep and RCU rely on in_nmi() returning true in this case. The +preemption count modification in the NMI entry/exit case must not be +traced. + +Architecture-specific code looks like this: + +.. code-block:: c + + noinstr void nmi(struct pt_regs *regs) + { + arch_nmi_enter(regs); + state = irqentry_nmi_enter(regs); + + instrumentation_begin(); + nmi_handler(regs); + instrumentation_end(); + + irqentry_nmi_exit(regs); + } + +and for e.g. a debug exception it can look like this: + +.. code-block:: c + + noinstr void debug(struct pt_regs *regs) + { + arch_nmi_enter(regs); + + debug_regs = save_debug_regs(); + + if (user_mode(regs)) { + state = irqentry_enter(regs); + + instrumentation_begin(); + user_mode_debug_handler(regs, debug_regs); + instrumentation_end(); + + irqentry_exit(regs, state); + } else { + state = irqentry_nmi_enter(regs); + + instrumentation_begin(); + kernel_mode_debug_handler(regs, debug_regs); + instrumentation_end(); + + irqentry_nmi_exit(regs, state); + } + } + +There is no combined irqentry_nmi_if_kernel() function available as the +above cannot be handled in an exception-agnostic way. diff --git a/Documentation/core-api/index.rst b/Documentation/core-api/index.rst index 5de2c7a4b1b3..972d46a5ddf6 100644 --- a/Documentation/core-api/index.rst +++ b/Documentation/core-api/index.rst @@ -44,6 +44,14 @@ Library functionality that is used throughout the kernel. timekeeping errseq +Low level entry and exit +======================== + +.. toctree:: + :maxdepth: 1 + + entry + Concurrency primitives ====================== From e3aa43e936d854373d9a75372aefcefebfca208f Mon Sep 17 00:00:00 2001 From: Nicolas Saenz Julienne Date: Mon, 10 Jan 2022 11:50:44 +0100 Subject: [PATCH 011/294] Documentation: core-api: entry: Add comments about nesting The topic of nesting and reentrancy in the context of early entry code hasn't been addressed so far. So do it. Signed-off-by: Nicolas Saenz Julienne Reviewed-by: Frederic Weisbecker Reviewed-by: Paul E. McKenney Link: https://lore.kernel.org/r/20220110105044.94423-2-nsaenzju@redhat.com Signed-off-by: Jonathan Corbet --- Documentation/core-api/entry.rst | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Documentation/core-api/entry.rst b/Documentation/core-api/entry.rst index c6f8e22c88fe..e12f22ab33c7 100644 --- a/Documentation/core-api/entry.rst +++ b/Documentation/core-api/entry.rst @@ -105,6 +105,8 @@ has to do extra work between the various steps. In such cases it has to ensure that enter_from_user_mode() is called first on entry and exit_to_user_mode() is called last on exit. +Do not nest syscalls. Nested systcalls will cause RCU and/or context tracking +to print a warning. KVM --- @@ -121,6 +123,8 @@ Task work handling is done separately for guest at the boundary of the vcpu_run() loop via xfer_to_guest_mode_handle_work() which is a subset of the work handled on return to user space. +Do not nest KVM entry/exit transitions because doing so is nonsensical. + Interrupts and regular exceptions --------------------------------- @@ -180,6 +184,16 @@ before it handles soft interrupts, whose handlers must run in BH context rather than irq-disabled context. In addition, irqentry_exit() might schedule, which also requires that HARDIRQ_OFFSET has been removed from the preemption count. +Even though interrupt handlers are expected to run with local interrupts +disabled, interrupt nesting is common from an entry/exit perspective. For +example, softirq handling happens within an irqentry_{enter,exit}() block with +local interrupts enabled. Also, although uncommon, nothing prevents an +interrupt handler from re-enabling interrupts. + +Interrupt entry/exit code doesn't strictly need to handle reentrancy, since it +runs with local interrupts disabled. But NMIs can happen anytime, and a lot of +the entry code is shared between the two. + NMI and NMI-like exceptions --------------------------- @@ -259,3 +273,7 @@ and for e.g. a debug exception it can look like this: There is no combined irqentry_nmi_if_kernel() function available as the above cannot be handled in an exception-agnostic way. + +NMIs can happen in any context. For example, an NMI-like exception triggered +while handling an NMI. So NMI entry code has to be reentrant and state updates +need to handle nesting. From 9c3519d2b50968ded1373e872fcc34ca3d748007 Mon Sep 17 00:00:00 2001 From: Yanteng Si Date: Tue, 11 Jan 2022 10:13:14 +0800 Subject: [PATCH 012/294] docs/zh_CN: add vm/index translation Translate ../vm/index.rst into Chinese. Signed-off-by: Yanteng Si Reviewed-by: Tang Yizhou Reviewed-by: Alex Shi Link: https://lore.kernel.org/r/60c579b34792c4c76194c4843a695263a982b37d.1641866889.git.siyanteng@loongson.cn Signed-off-by: Jonathan Corbet --- Documentation/translations/zh_CN/index.rst | 2 +- Documentation/translations/zh_CN/vm/index.rst | 52 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/vm/index.rst diff --git a/Documentation/translations/zh_CN/index.rst b/Documentation/translations/zh_CN/index.rst index 08144252630c..f831887eacfb 100644 --- a/Documentation/translations/zh_CN/index.rst +++ b/Documentation/translations/zh_CN/index.rst @@ -113,6 +113,7 @@ TODOList: sound/index filesystems/index scheduler/index + vm/index TODOList: @@ -140,7 +141,6 @@ TODOList: * gpu/index * security/index * crypto/index -* vm/index * bpf/index * usb/index * PCI/index diff --git a/Documentation/translations/zh_CN/vm/index.rst b/Documentation/translations/zh_CN/vm/index.rst new file mode 100644 index 000000000000..0fda40c602ac --- /dev/null +++ b/Documentation/translations/zh_CN/vm/index.rst @@ -0,0 +1,52 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/vm/index.rst + +:翻译: + + 司延腾 Yanteng Si + +:校译: + +================= +Linux内存管理文档 +================= + +这是一个关于Linux内存管理(mm)子系统内部的文档集,其中有不同层次的细节,包括注释 +和邮件列表的回复,用于阐述数据结构和算法的基本情况。如果你正在寻找关于简单分配内存的建 +议,请参阅(Documentation/translations/zh_CN/core-api/memory-allocation.rst)。 +对于控制和调整指南,请参阅(Documentation/admin-guide/mm/index)。 +TODO:待引用文档集被翻译完毕后请及时修改此处) + +.. toctree:: + :maxdepth: 1 + + +TODOLIST: +* active_mm +* arch_pgtable_helpers +* balance +* damon/index +* free_page_reporting +* frontswap +* highmem +* hmm +* hwpoison +* hugetlbfs_reserv +* ksm +* memory-model +* mmu_notifier +* numa +* overcommit-accounting +* page_migration +* page_frags +* page_owner +* page_table_check +* remap_file_pages +* slub +* split_page_table_lock +* transhuge +* unevictable-lru +* vmalloced-kernel-stacks +* z3fold +* zsmalloc From 6f5dbb213c140f04fc6d49b70b178ee11333c5f3 Mon Sep 17 00:00:00 2001 From: Yanteng Si Date: Tue, 11 Jan 2022 10:13:15 +0800 Subject: [PATCH 013/294] docs_zh_CN: add active_mm translation Translatr .../vm/active_mm.rst into Chinese. Signed-off-by: Yanteng Si Reviewed-by: Alex Shi Reviewed-by: Tang Yizhou Link: https://lore.kernel.org/r/99ba014bdd9550bad57db6c21653cb7314d7c2d8.1641866889.git.siyanteng@loongson.cn Signed-off-by: Jonathan Corbet --- .../translations/zh_CN/vm/active_mm.rst | 85 +++++++++++++++++++ Documentation/translations/zh_CN/vm/index.rst | 2 +- 2 files changed, 86 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/vm/active_mm.rst diff --git a/Documentation/translations/zh_CN/vm/active_mm.rst b/Documentation/translations/zh_CN/vm/active_mm.rst new file mode 100644 index 000000000000..366609ea4f37 --- /dev/null +++ b/Documentation/translations/zh_CN/vm/active_mm.rst @@ -0,0 +1,85 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/vm/active_mm.rst + +:翻译: + + 司延腾 Yanteng Si + +:校译: + + +========= +Active MM +========= + +这是一封linux之父回复开发者的一封邮件,所以翻译时我尽量保持邮件格式的完整。 + +:: + + List: linux-kernel + Subject: Re: active_mm + From: Linus Torvalds + Date: 1999-07-30 21:36:24 + + 因为我并不经常写解释,所以已经抄送到linux-kernel邮件列表,而当我做这些, + 且更多的人在阅读它们时,我觉得棒极了。 + + 1999年7月30日 星期五, David Mosberger 写道: + > + > 是否有一个简短的描述,说明task_struct中的 + > "mm" 和 "active_mm"应该如何使用? (如果 + > 这个问题在邮件列表中讨论过,我表示歉意--我刚 + > 刚度假回来,有一段时间没能关注linux-kernel了)。 + + 基本上,新的设定是: + + - 我们有“真实地址空间”和“匿名地址空间”。区别在于,匿名地址空间根本不关心用 + 户级页表,所以当我们做上下文切换到匿名地址空间时,我们只是让以前的地址空间 + 处于活动状态。 + + 一个“匿名地址空间”的明显用途是任何不需要任何用户映射的线程--所有的内核线 + 程基本上都属于这一类,但即使是“真正的”线程也可以暂时说在一定时间内它们不 + 会对用户空间感兴趣,调度器不妨试着避免在切换VM状态上浪费时间。目前只有老 + 式的bdflush sync能做到这一点。 + + - “tsk->mm” 指向 “真实地址空间”。对于一个匿名进程来说,tsk->mm将是NULL, + 其逻辑原因是匿名进程实际上根本就 “没有” 真正的地址空间。 + + - 然而,我们显然需要跟踪我们为这样的匿名用户“偷用”了哪个地址空间。为此,我们 + 有 “tsk->active_mm”,它显示了当前活动的地址空间是什么。 + + 规则是,对于一个有真实地址空间的进程(即tsk->mm是 non-NULL),active_mm + 显然必须与真实的mm相同。 + + 对于一个匿名进程,tsk->mm == NULL,而tsk->active_mm是匿名进程运行时 + “借用”的mm。当匿名进程被调度走时,借用的地址空间被返回并清除。 + + 为了支持所有这些,“struct mm_struct”现在有两个计数器:一个是 “mm_users” + 计数器,即有多少 “真正的地址空间用户”,另一个是 “mm_count”计数器,即 “lazy” + 用户(即匿名用户)的数量,如果有任何真正的用户,则加1。 + + 通常情况下,至少有一个真正的用户,但也可能是真正的用户在另一个CPU上退出,而 + 一个lazy的用户仍在活动,所以你实际上得到的情况是,你有一个地址空间 **只** + 被lazy的用户使用。这通常是一个短暂的生命周期状态,因为一旦这个线程被安排给一 + 个真正的线程,这个 “僵尸” mm就会被释放,因为 “mm_count”变成了零。 + + 另外,一个新的规则是,**没有人** 再把 “init_mm” 作为一个真正的MM了。 + “init_mm”应该被认为只是一个 “没有其他上下文时的lazy上下文”,事实上,它主 + 要是在启动时使用,当时还没有真正的VM被创建。因此,用来检查的代码 + + if (current->mm == &init_mm) + + 一般来说,应该用 + + if (!current->mm) + + 取代上面的写法(这更有意义--测试基本上是 “我们是否有一个用户环境”,并且通常 + 由缺页异常处理程序和类似的东西来完成)。 + + 总之,我刚才在ftp.kernel.org上放了一个pre-patch-2.3.13-1,因为它稍微改 + 变了接口以适配alpha(谁会想到呢,但alpha体系结构上下文切换代码实际上最终是 + 最丑陋的之一--不像其他架构的MM和寄存器状态是分开的,alpha的PALcode将两者 + 连接起来,你需要同时切换两者)。 + + (文档来源 http://marc.info/?l=linux-kernel&m=93337278602211&w=2) diff --git a/Documentation/translations/zh_CN/vm/index.rst b/Documentation/translations/zh_CN/vm/index.rst index 0fda40c602ac..5fcdb75d8682 100644 --- a/Documentation/translations/zh_CN/vm/index.rst +++ b/Documentation/translations/zh_CN/vm/index.rst @@ -21,9 +21,9 @@ TODO:待引用文档集被翻译完毕后请及时修改此处) .. toctree:: :maxdepth: 1 + active_mm TODOLIST: -* active_mm * arch_pgtable_helpers * balance * damon/index From 88ba790d84e98b6f030c8efc8d3e9f042c0ab777 Mon Sep 17 00:00:00 2001 From: Yanteng Si Date: Tue, 11 Jan 2022 10:13:16 +0800 Subject: [PATCH 014/294] docs/zh_CN: add balance translation Translate .../vm/balance.rst into Chinese. Signed-off-by: Yanteng Si Reviewed-by: Alex Shi Reviewed-by: Tang Yizhou Link: https://lore.kernel.org/r/f1e5a6bcbee3e34fe65a56ee185d9b44daf01cab.1641866889.git.siyanteng@loongson.cn Signed-off-by: Jonathan Corbet --- .../translations/zh_CN/vm/balance.rst | 81 +++++++++++++++++++ Documentation/translations/zh_CN/vm/index.rst | 2 +- 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/vm/balance.rst diff --git a/Documentation/translations/zh_CN/vm/balance.rst b/Documentation/translations/zh_CN/vm/balance.rst new file mode 100644 index 000000000000..e98a47ef24a8 --- /dev/null +++ b/Documentation/translations/zh_CN/vm/balance.rst @@ -0,0 +1,81 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/vm/balance.rst + +:翻译: + + 司延腾 Yanteng Si + +:校译: + + +======== +内存平衡 +======== + +2000年1月开始,作者:Kanoj Sarcar + +对于 !__GFP_HIGH 和 !__GFP_KSWAPD_RECLAIM 以及非 __GFP_IO 的分配,需要进行 +内存平衡。 + +调用者避免回收的第一个原因是调用者由于持有自旋锁或处于中断环境中而无法睡眠。第二个 +原因可能是,调用者愿意在不产生页面回收开销的情况下分配失败。这可能发生在有0阶回退 +选项的机会主义高阶分配请求中。在这种情况下,调用者可能也希望避免唤醒kswapd。 + +__GFP_IO分配请求是为了防止文件系统死锁。 + +在没有非睡眠分配请求的情况下,做平衡似乎是有害的。页面回收可以被懒散地启动,也就是 +说,只有在需要的时候(也就是区域的空闲内存为0),而不是让它成为一个主动的过程。 + +也就是说,内核应该尝试从直接映射池中满足对直接映射页的请求,而不是回退到dma池中, +这样就可以保持dma池为dma请求(不管是不是原子的)所填充。类似的争论也适用于高内存 +和直接映射的页面。相反,如果有很多空闲的dma页,最好是通过从dma池中分配一个来满足 +常规的内存请求,而不是产生常规区域平衡的开销。 + +在2.2中,只有当空闲页总数低于总内存的1/64时,才会启动内存平衡/页面回收。如果dma +和常规内存的比例合适,即使dma区完全空了,也很可能不会进行平衡。2.2已经在不同内存 +大小的生产机器上运行,即使有这个问题存在,似乎也做得不错。在2.3中,由于HIGHMEM的 +存在,这个问题变得更加严重。 + +在2.3中,区域平衡可以用两种方式之一来完成:根据区域的大小(可能是低级区域的大小), +我们可以在初始化阶段决定在平衡任何区域时应该争取多少空闲页。好的方面是,在平衡的时 +候,我们不需要看低级区的大小,坏的方面是,我们可能会因为忽略低级区可能较低的使用率 +而做过于频繁的平衡。另外,只要对分配程序稍作修改,就有可能将memclass()宏简化为一 +个简单的等式。 + +另一个可能的解决方案是,我们只在一个区 **和** 其所有低级区的空闲内存低于该区及其 +低级区总内存的1/64时进行平衡。这就解决了2.2的平衡问题,并尽可能地保持了与2.2行为 +的接近。另外,平衡算法在各种架构上的工作方式也是一样的,这些架构有不同数量和类型的 +内存区。如果我们想变得更花哨一点,我们可以在未来为不同区域的自由页面分配不同的权重。 + +请注意,如果普通区的大小与dma区相比是巨大的,那么在决定是否平衡普通区的时候,考虑 +空闲的dma页就变得不那么重要了。那么第一个解决方案就变得更有吸引力。 + +所附的补丁实现了第二个解决方案。它还 “修复”了两个问题:首先,在低内存条件下,kswapd +被唤醒,就像2.2中的非睡眠分配。第二,HIGHMEM区也被平衡了,以便给replace_with_highmem() +一个争取获得HIGHMEM页的机会,同时确保HIGHMEM分配不会落回普通区。这也确保了HIGHMEM +页不会被泄露(例如,在一个HIGHMEM页在交换缓存中但没有被任何人使用的情况下)。 + +kswapd还需要知道它应该平衡哪些区。kswapd主要是在无法进行平衡的情况下需要的,可能 +是因为所有的分配请求都来自中断上下文,而所有的进程上下文都在睡眠。对于2.3, +kswapd并不真正需要平衡高内存区,因为中断上下文并不请求高内存页。kswapd看zone +结构体中的zone_wake_kswapd字段来决定一个区是否需要平衡。 + +如果从进程内存和shm中偷取页面可以减轻该页面节点中任何区的内存压力,而该区的内存压力 +已经低于其水位,则会进行偷取。 + +watemark[WMARK_MIN/WMARK_LOW/WMARK_HIGH]/low_on_memory/zone_wake_kswapd: +这些是每个区的字段,用于确定一个区何时需要平衡。当页面数低于水位[WMARK_MIN]时, +hysteric 的字段low_on_memory被设置。这个字段会一直被设置,直到空闲页数变成水位 +[WMARK_HIGH]。当low_on_memory被设置时,页面分配请求将尝试释放该区域的一些页面(如果 +请求中设置了GFP_WAIT)。与此相反的是,决定唤醒kswapd以释放一些区的页。这个决定不是基于 +hysteresis 的,而是当空闲页的数量低于watermark[WMARK_LOW]时就会进行;在这种情况下, +zone_wake_kswapd也被设置。 + + +我所听到的(超棒的)想法: + +1. 动态经历应该影响平衡:可以跟踪一个区的失败请求的数量,并反馈到平衡方案中(jalvo@mbay.net)。 + +2. 实现一个类似于replace_with_highmem()的replace_with_regular(),以保留dma页面。 + (lkd@tantalophile.demon.co.uk) diff --git a/Documentation/translations/zh_CN/vm/index.rst b/Documentation/translations/zh_CN/vm/index.rst index 5fcdb75d8682..fc39f0d60318 100644 --- a/Documentation/translations/zh_CN/vm/index.rst +++ b/Documentation/translations/zh_CN/vm/index.rst @@ -22,10 +22,10 @@ TODO:待引用文档集被翻译完毕后请及时修改此处) :maxdepth: 1 active_mm + balance TODOLIST: * arch_pgtable_helpers -* balance * damon/index * free_page_reporting * frontswap From 2701b511e491f95d829a340c2540d602db5c63d8 Mon Sep 17 00:00:00 2001 From: wangyong Date: Wed, 12 Jan 2022 08:04:42 -0800 Subject: [PATCH 015/294] docs/zh_CN: Update zh_CN/accounting/delay-accounting.rst Update zh_CN/accounting/delay-accounting.rst. The document modification has been merged which refers to the following link: https://lore.kernel.org/all/1639583021-92977-1-git-send-email-wang.yong12@zte.com.cn/ Signed-off-by: wangyong Reviewed-by: Yang Yang Reviewed-by: Alex Shi Link: https://lore.kernel.org/r/1642003482-48935-1-git-send-email-wang.yong12@zte.com.cn Signed-off-by: Jonathan Corbet --- .../zh_CN/accounting/delay-accounting.rst | 54 +++++++++---------- 1 file changed, 26 insertions(+), 28 deletions(-) diff --git a/Documentation/translations/zh_CN/accounting/delay-accounting.rst b/Documentation/translations/zh_CN/accounting/delay-accounting.rst index 67d5606e5401..f1849411018e 100644 --- a/Documentation/translations/zh_CN/accounting/delay-accounting.rst +++ b/Documentation/translations/zh_CN/accounting/delay-accounting.rst @@ -17,6 +17,8 @@ a) 等待一个CPU(任务为可运行) b) 完成由该任务发起的块I/O同步请求 c) 页面交换 d) 内存回收 +e) 页缓存抖动 +f) 直接规整 并将这些统计信息通过taskstats接口提供给用户空间。 @@ -37,10 +39,10 @@ d) 内存回收 向用户态返回一个通用数据结构,对应每pid或每tgid的统计信息。延时计数功能填写 该数据结构的特定字段。见 - include/linux/taskstats.h + include/uapi/linux/taskstats.h 其描述了延时计数相关字段。系统通常以计数器形式返回 CPU、同步块 I/O、交换、内存 -回收等的累积延时。 +回收、页缓存抖动、直接规整等的累积延时。 取任务某计数器两个连续读数的差值,将得到任务在该时间间隔内等待对应资源的总延时。 @@ -72,40 +74,36 @@ kernel.task_delayacct进行开关。注意,只有在启用延时计数后启 getdelays命令的一般格式:: - getdelays [-t tgid] [-p pid] [-c cmd...] + getdelays [-dilv] [-t tgid] [-p pid] 获取pid为10的任务从系统启动后的延时信息:: - # ./getdelays -p 10 + # ./getdelays -d -p 10 (输出信息和下例相似) 获取所有tgid为5的任务从系统启动后的总延时信息:: - # ./getdelays -t 5 + # ./getdelays -d -t 5 + print delayacct stats ON + TGID 5 - CPU count real total virtual total delay total - 7876 92005750 100000000 24001500 - IO count delay total - 0 0 - SWAP count delay total - 0 0 - RECLAIM count delay total - 0 0 + CPU count real total virtual total delay total delay average + 8 7000000 6872122 3382277 0.423ms + IO count delay total delay average + 0 0 0ms + SWAP count delay total delay average + 0 0 0ms + RECLAIM count delay total delay average + 0 0 0ms + THRASHING count delay total delay average + 0 0 0ms + COMPACT count delay total delay average + 0 0 0ms -获取指定简单命令运行时的延时信息:: +获取pid为1的IO计数,它只和-p一起使用:: + # ./getdelays -i -p 1 + printing IO accounting + linuxrc: read=65536, write=0, cancelled_write=0 - # ./getdelays -c ls / - - bin data1 data3 data5 dev home media opt root srv sys usr - boot data2 data4 data6 etc lib mnt proc sbin subdomain tmp var - - - CPU count real total virtual total delay total - 6 4000250 4000000 0 - IO count delay total - 0 0 - SWAP count delay total - 0 0 - RECLAIM count delay total - 0 0 +上面的命令与-v一起使用,可以获取更多调试信息。 From e2d99027da08a816ba63d0b8335dbde0ec1152c6 Mon Sep 17 00:00:00 2001 From: Yanteng Si Date: Sat, 15 Jan 2022 15:15:00 +0800 Subject: [PATCH 016/294] docs/zh_CN: add damon index tronslation 1) Translate .../vm/damon/index.rst into Chinese. 2) add damon into .../zh_CN/vm/index.rst Signed-off-by: Yanteng Si Reviewed-by: Alex Shi Link: https://lore.kernel.org/r/8e8c7651785f1ce20766bc1b3a4fc44faedb84bb.1642230669.git.siyanteng@loongson.cn Signed-off-by: Jonathan Corbet --- .../translations/zh_CN/vm/damon/index.rst | 33 +++++++++++++++++++ Documentation/translations/zh_CN/vm/index.rst | 2 +- 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/vm/damon/index.rst diff --git a/Documentation/translations/zh_CN/vm/damon/index.rst b/Documentation/translations/zh_CN/vm/damon/index.rst new file mode 100644 index 000000000000..9a7b4ea91cb8 --- /dev/null +++ b/Documentation/translations/zh_CN/vm/damon/index.rst @@ -0,0 +1,33 @@ +.. SPDX-License-Identifier: GPL-2.0 + +:Original: Documentation/vm/damon/index.rst + +:翻译: + + 司延腾 Yanteng Si + +:校译: + + +========================== +DAMON:数据访问监视器 +========================== + +DAMON是Linux内核的一个数据访问监控框架子系统。DAMON的核心机制使其成为 +(该核心机制详见(Documentation/translations/zh_CN/vm/damon/design.rst)) + + - *准确度* (监测输出对DRAM级别的内存管理足够有用;但可能不适合CPU Cache级别), + - *轻量级* (监控开销低到可以在线应用),以及 + - *可扩展* (无论目标工作负载的大小,开销的上限值都在恒定范围内)。 + +因此,利用这个框架,内核的内存管理机制可以做出高级决策。会导致高数据访问监控开销的实 +验性内存管理优化工作可以再次进行。同时,在用户空间,有一些特殊工作负载的用户可以编写 +个性化的应用程序,以便更好地了解和优化他们的工作负载和系统。 + +.. toctree:: + :maxdepth: 2 + +TODOLIST: +* faq +* design +* api diff --git a/Documentation/translations/zh_CN/vm/index.rst b/Documentation/translations/zh_CN/vm/index.rst index fc39f0d60318..432bc3ef1c18 100644 --- a/Documentation/translations/zh_CN/vm/index.rst +++ b/Documentation/translations/zh_CN/vm/index.rst @@ -23,10 +23,10 @@ TODO:待引用文档集被翻译完毕后请及时修改此处) active_mm balance + damon/index TODOLIST: * arch_pgtable_helpers -* damon/index * free_page_reporting * frontswap * highmem From 3fd8816219311289eaec2eb9bc389146a553fe4e Mon Sep 17 00:00:00 2001 From: Yanteng Si Date: Sat, 15 Jan 2022 15:15:01 +0800 Subject: [PATCH 017/294] docs/zh_CN: add damon faq translation Translate .../vm/damon/faq.rst into Chinese. Signed-off-by: Yanteng Si Reviewed-by: Alex Shi Link: https://lore.kernel.org/r/30507f807a835360f57bb9498c37f4c3644b33b7.1642230669.git.siyanteng@loongson.cn Signed-off-by: Jonathan Corbet --- .../translations/zh_CN/vm/damon/faq.rst | 48 +++++++++++++++++++ .../translations/zh_CN/vm/damon/index.rst | 4 +- 2 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/vm/damon/faq.rst diff --git a/Documentation/translations/zh_CN/vm/damon/faq.rst b/Documentation/translations/zh_CN/vm/damon/faq.rst new file mode 100644 index 000000000000..07b4ac19407d --- /dev/null +++ b/Documentation/translations/zh_CN/vm/damon/faq.rst @@ -0,0 +1,48 @@ +.. SPDX-License-Identifier: GPL-2.0 + +:Original: Documentation/vm/damon/faq.rst + +:翻译: + + 司延腾 Yanteng Si + +:校译: + + +======== +常见问题 +======== + +为什么是一个新的子系统,而不是扩展perf或其他用户空间工具? +========================================================== + +首先,因为它需要尽可能的轻量级,以便可以在线使用,所以应该避免任何不必要的开销,如内核-用户 +空间的上下文切换成本。第二,DAMON的目标是被包括内核在内的其他程序所使用。因此,对特定工具 +(如perf)的依赖性是不可取的。这就是DAMON在内核空间实现的两个最大的原因。 + + +“闲置页面跟踪” 或 “perf mem” 可以替代DAMON吗? +============================================== + +闲置页跟踪是物理地址空间访问检查的一个低层次的原始方法。“perf mem”也是类似的,尽管它可以 +使用采样来减少开销。另一方面,DAMON是一个更高层次的框架,用于监控各种地址空间。它专注于内 +存管理优化,并提供复杂的精度/开销处理机制。因此,“空闲页面跟踪” 和 “perf mem” 可以提供 +DAMON输出的一个子集,但不能替代DAMON。 + + +DAMON是否只支持虚拟内存? +========================= + +不,DAMON的核心是独立于地址空间的。用户可以在DAMON核心上实现和配置特定地址空间的低级原始 +部分,包括监测目标区域的构造和实际的访问检查。通过这种方式,DAMON用户可以用任何访问检查技 +术来监测任何地址空间。 + +尽管如此,DAMON默认为虚拟内存和物理内存提供了基于vma/rmap跟踪和PTE访问位检查的地址空间 +相关功能的实现,以供参考和方便使用。 + + +我可以简单地监测页面的粒度吗? +============================== + +是的,你可以通过设置 ``min_nr_regions`` 属性高于工作集大小除以页面大小的值来实现。 +因为监视目标区域的大小被强制为 ``>=page size`` ,所以区域分割不会产生任何影响。 diff --git a/Documentation/translations/zh_CN/vm/damon/index.rst b/Documentation/translations/zh_CN/vm/damon/index.rst index 9a7b4ea91cb8..dfa82244cbe8 100644 --- a/Documentation/translations/zh_CN/vm/damon/index.rst +++ b/Documentation/translations/zh_CN/vm/damon/index.rst @@ -27,7 +27,9 @@ DAMON是Linux内核的一个数据访问监控框架子系统。DAMON的核心 .. toctree:: :maxdepth: 2 + faq + TODOLIST: -* faq + * design * api From 18e74934dc4a65ff1dfb24b83f9778ded0f247dd Mon Sep 17 00:00:00 2001 From: Yanteng Si Date: Sat, 15 Jan 2022 15:15:02 +0800 Subject: [PATCH 018/294] docs/zh_CN: add damon design translation Translate .../vm/damon/design.rst into Chinese. Signed-off-by: Yanteng Si Reviewed-by: Alex Shi Link: https://lore.kernel.org/r/2685ed7d446620b260c20158685728c3adb5e0fe.1642230669.git.siyanteng@loongson.cn Signed-off-by: Jonathan Corbet --- .../translations/zh_CN/vm/damon/design.rst | 139 ++++++++++++++++++ .../translations/zh_CN/vm/damon/index.rst | 2 +- 2 files changed, 140 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/vm/damon/design.rst diff --git a/Documentation/translations/zh_CN/vm/damon/design.rst b/Documentation/translations/zh_CN/vm/damon/design.rst new file mode 100644 index 000000000000..05f66c02740a --- /dev/null +++ b/Documentation/translations/zh_CN/vm/damon/design.rst @@ -0,0 +1,139 @@ +.. SPDX-License-Identifier: GPL-2.0 + +:Original: Documentation/vm/damon/design.rst + +:翻译: + + 司延腾 Yanteng Si + +:校译: + + +==== +设计 +==== + +可配置的层 +========== + +DAMON提供了数据访问监控功能,同时使其准确性和开销可控。基本的访问监控需要依赖于目标地址空间 +并为之优化的基元。另一方面,作为DAMON的核心,准确性和开销的权衡机制是在纯逻辑空间中。DAMON +将这两部分分离在不同的层中,并定义了它的接口,以允许各种低层次的基元实现与核心逻辑的配置。 + +由于这种分离的设计和可配置的接口,用户可以通过配置核心逻辑和适当的低级基元实现来扩展DAMON的 +任何地址空间。如果没有提供合适的,用户可以自己实现基元。 + +例如,物理内存、虚拟内存、交换空间、那些特定的进程、NUMA节点、文件和支持的内存设备将被支持。 +另外,如果某些架构或设备支持特殊的优化访问检查基元,这些基元将很容易被配置。 + + +特定地址空间基元的参考实现 +========================== + +基本访问监测的低级基元被定义为两部分。: + +1. 确定地址空间的监测目标地址范围 +2. 目标空间中特定地址范围的访问检查。 + +DAMON目前为物理和虚拟地址空间提供了基元的实现。下面两个小节描述了这些工作的方式。 + + +基于VMA的目标地址范围构造 +------------------------- + +这仅仅是针对虚拟地址空间基元的实现。对于物理地址空间,只是要求用户手动设置监控目标地址范围。 + +在进程的超级巨大的虚拟地址空间中,只有小部分被映射到物理内存并被访问。因此,跟踪未映射的地 +址区域只是一种浪费。然而,由于DAMON可以使用自适应区域调整机制来处理一定程度的噪声,所以严 +格来说,跟踪每一个映射并不是必须的,但在某些情况下甚至会产生很高的开销。也就是说,监测目标 +内部过于巨大的未映射区域应该被移除,以不占用自适应机制的时间。 + +出于这个原因,这个实现将复杂的映射转换为三个不同的区域,覆盖地址空间的每个映射区域。这三个 +区域之间的两个空隙是给定地址空间中两个最大的未映射区域。这两个最大的未映射区域是堆和最上面 +的mmap()区域之间的间隙,以及在大多数情况下最下面的mmap()区域和堆之间的间隙。因为这些间隙 +在通常的地址空间中是异常巨大的,排除这些间隙就足以做出合理的权衡。下面详细说明了这一点:: + + + + + (small mmap()-ed regions and munmap()-ed regions) + + + + + +基于PTE访问位的访问检查 +----------------------- + +物理和虚拟地址空间的实现都使用PTE Accessed-bit进行基本访问检查。唯一的区别在于从地址中 +找到相关的PTE访问位的方式。虚拟地址的实现是为该地址的目标任务查找页表,而物理地址的实现则 +是查找与该地址有映射关系的每一个页表。通过这种方式,实现者找到并清除下一个采样目标地址的位, +并检查该位是否在一个采样周期后再次设置。这可能会干扰其他使用访问位的内核子系统,即空闲页跟 +踪和回收逻辑。为了避免这种干扰,DAMON使其与空闲页面跟踪相互排斥,并使用 ``PG_idle`` 和 +``PG_young`` 页面标志来解决与回收逻辑的冲突,就像空闲页面跟踪那样。 + + +独立于地址空间的核心机制 +======================== + +下面四个部分分别描述了DAMON的核心机制和五个监测属性,即 ``采样间隔`` 、 ``聚集间隔`` 、 +``区域更新间隔`` 、 ``最小区域数`` 和 ``最大区域数`` 。 + + +访问频率监测 +------------ + +DAMON的输出显示了在给定的时间内哪些页面的访问频率是多少。访问频率的分辨率是通过设置 +``采样间隔`` 和 ``聚集间隔`` 来控制的。详细地说,DAMON检查每个 ``采样间隔`` 对每 +个页面的访问,并将结果汇总。换句话说,计算每个页面的访问次数。在每个 ``聚合间隔`` 过 +去后,DAMON调用先前由用户注册的回调函数,以便用户可以阅读聚合的结果,然后再清除这些结 +果。这可以用以下简单的伪代码来描述:: + + while monitoring_on: + for page in monitoring_target: + if accessed(page): + nr_accesses[page] += 1 + if time() % aggregation_interval == 0: + for callback in user_registered_callbacks: + callback(monitoring_target, nr_accesses) + for page in monitoring_target: + nr_accesses[page] = 0 + sleep(sampling interval) + +这种机制的监测开销将随着目标工作负载规模的增长而任意增加。 + + +基于区域的抽样调查 +------------------ + +为了避免开销的无限制增加,DAMON将假定具有相同访问频率的相邻页面归入一个区域。只要保持 +这个假设(一个区域内的页面具有相同的访问频率),该区域内就只需要检查一个页面。因此,对 +于每个 ``采样间隔`` ,DAMON在每个区域中随机挑选一个页面,等待一个 ``采样间隔`` ,检 +查该页面是否同时被访问,如果被访问则增加该区域的访问频率。因此,监测开销是可以通过设置 +区域的数量来控制的。DAMON允许用户设置最小和最大的区域数量来进行权衡。 + +然而,如果假设没有得到保证,这个方案就不能保持输出的质量。 + + +适应性区域调整 +-------------- + +即使最初的监测目标区域被很好地构建以满足假设(同一区域内的页面具有相似的访问频率),数 +据访问模式也会被动态地改变。这将导致监测质量下降。为了尽可能地保持假设,DAMON根据每个 +区域的访问频率自适应地进行合并和拆分。 + +对于每个 ``聚集区间`` ,它比较相邻区域的访问频率,如果频率差异较小,就合并这些区域。 +然后,在它报告并清除每个区域的聚合接入频率后,如果区域总数不超过用户指定的最大区域数, +它将每个区域拆分为两个或三个区域。 + +通过这种方式,DAMON提供了其最佳的质量和最小的开销,同时保持了用户为其权衡设定的界限。 + + +动态目标空间更新处理 +-------------------- + +监测目标地址范围可以动态改变。例如,虚拟内存可以动态地被映射和解映射。物理内存可以被 +热插拔。 + +由于在某些情况下变化可能相当频繁,DAMON检查动态内存映射的变化,并仅在用户指定的时间 +间隔( ``区域更新间隔`` )内将其应用于抽象的目标区域。 diff --git a/Documentation/translations/zh_CN/vm/damon/index.rst b/Documentation/translations/zh_CN/vm/damon/index.rst index dfa82244cbe8..077db7e4326f 100644 --- a/Documentation/translations/zh_CN/vm/damon/index.rst +++ b/Documentation/translations/zh_CN/vm/damon/index.rst @@ -28,8 +28,8 @@ DAMON是Linux内核的一个数据访问监控框架子系统。DAMON的核心 :maxdepth: 2 faq + design TODOLIST: -* design * api From 722cc663d79c80051cbd57de0336582c8e3cbf93 Mon Sep 17 00:00:00 2001 From: Yanteng Si Date: Sat, 15 Jan 2022 15:15:03 +0800 Subject: [PATCH 019/294] docs/zh_CN: add damon api translation Translate .../vm/damon/api.rst into Chinese. Signed-off-by: Yanteng Si Reviewed-by: Alex Shi Link: https://lore.kernel.org/r/3d81dbc79e0f982edd68fb9dfee6f0ccb47f7710.1642230669.git.siyanteng@loongson.cn Signed-off-by: Jonathan Corbet --- .../translations/zh_CN/vm/damon/api.rst | 32 +++++++++++++++++++ .../translations/zh_CN/vm/damon/index.rst | 4 +-- 2 files changed, 33 insertions(+), 3 deletions(-) create mode 100644 Documentation/translations/zh_CN/vm/damon/api.rst diff --git a/Documentation/translations/zh_CN/vm/damon/api.rst b/Documentation/translations/zh_CN/vm/damon/api.rst new file mode 100644 index 000000000000..21143eea4ebe --- /dev/null +++ b/Documentation/translations/zh_CN/vm/damon/api.rst @@ -0,0 +1,32 @@ +.. SPDX-License-Identifier: GPL-2.0 + +:Original: Documentation/vm/damon/api.rst + +:翻译: + + 司延腾 Yanteng Si + +:校译: + + +======= +API参考 +======= + +内核空间的程序可以使用下面的API来使用DAMON的每个功能。你所需要做的就是引用 ``damon.h`` , +它位于源代码树的include/linux/。 + +结构体 +====== + +该API在以下内核代码中: + +include/linux/damon.h + + +函数 +==== + +该API在以下内核代码中: + +mm/damon/core.c diff --git a/Documentation/translations/zh_CN/vm/damon/index.rst b/Documentation/translations/zh_CN/vm/damon/index.rst index 077db7e4326f..84d36d90c9b0 100644 --- a/Documentation/translations/zh_CN/vm/damon/index.rst +++ b/Documentation/translations/zh_CN/vm/damon/index.rst @@ -29,7 +29,5 @@ DAMON是Linux内核的一个数据访问监控框架子系统。DAMON的核心 faq design + api -TODOLIST: - -* api From adeacecbd36cb820f65e93194baba17decdded32 Mon Sep 17 00:00:00 2001 From: Yanteng Si Date: Thu, 27 Jan 2022 09:47:53 +0800 Subject: [PATCH 020/294] docs/zh_CN: add free_page_reporting translation Translate .../vm/free_page_reporting.rst into Chinese. Signed-off-by: Yanteng Si Reviewed-by: Alex Shi Link: https://lore.kernel.org/r/4ef77c1be8319fc45b18e9f4c41986095d794562.1643246827.git.siyanteng@loongson.cn Signed-off-by: Jonathan Corbet --- .../zh_CN/vm/free_page_reporting.rst | 38 +++++++++++++++++++ Documentation/translations/zh_CN/vm/index.rst | 2 +- 2 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/vm/free_page_reporting.rst diff --git a/Documentation/translations/zh_CN/vm/free_page_reporting.rst b/Documentation/translations/zh_CN/vm/free_page_reporting.rst new file mode 100644 index 000000000000..31d6c34b956b --- /dev/null +++ b/Documentation/translations/zh_CN/vm/free_page_reporting.rst @@ -0,0 +1,38 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/vm/_free_page_reporting.rst + +:翻译: + + 司延腾 Yanteng Si + +:校译: + +========== +空闲页报告 +========== + +空闲页报告是一个API,设备可以通过它来注册接收系统当前未使用的页面列表。这在虚拟 +化的情况下是很有用的,客户机能够使用这些数据来通知管理器它不再使用内存中的某些页 +面。 + +对于驱动,通常是气球驱动要使用这个功能,它将分配和初始化一个page_reporting_dev_info +结构体。它要填充的结构体中的字段是用于处理散点列表的 "report" 函数指针。它还必 +须保证每次调用该函数时能处理至少相当于PAGE_REPORTING_CAPACITY的散点列表条目。 +假设没有其他页面报告设备已经注册, 对page_reporting_register的调用将向报告框 +架注册页面报告接口。 + +一旦注册,页面报告API将开始向驱动报告成批的页面。API将在接口被注册后2秒开始报告 +页面,并在任何足够高的页面被释放之后2秒继续报告。 + +报告的页面将被存储在传递给报告函数的散列表中,最后一个条目的结束位被设置在条目 +nent-1中。 当页面被报告函数处理时,分配器将无法访问它们。一旦报告函数完成,这些 +页将被返回到它们所获得的自由区域。 + +在移除使用空闲页报告的驱动之前,有必要调用page_reporting_unregister,以移除 +目前被空闲页报告使用的page_reporting_dev_info结构体。这样做将阻止进一步的报 +告通过该接口发出。如果另一个驱动或同一驱动被注册,它就有可能恢复前一个驱动在报告 +空闲页方面的工作。 + + +Alexander Duyck, 2019年12月04日 diff --git a/Documentation/translations/zh_CN/vm/index.rst b/Documentation/translations/zh_CN/vm/index.rst index 432bc3ef1c18..13df8321f242 100644 --- a/Documentation/translations/zh_CN/vm/index.rst +++ b/Documentation/translations/zh_CN/vm/index.rst @@ -24,6 +24,7 @@ TODO:待引用文档集被翻译完毕后请及时修改此处) active_mm balance damon/index + free_page_reporting TODOLIST: * arch_pgtable_helpers @@ -33,7 +34,6 @@ TODOLIST: * hmm * hwpoison * hugetlbfs_reserv -* ksm * memory-model * mmu_notifier * numa From 4c97fdb06b9884da8682c869303b659a25c0b952 Mon Sep 17 00:00:00 2001 From: Yanteng Si Date: Thu, 27 Jan 2022 09:47:54 +0800 Subject: [PATCH 021/294] docs/zh_CN: add highmem translation Translate .../vm/highmem.rst into Chenese. Signed-off-by: Yanteng Si Reviewed-by: Alex Shi Link: https://lore.kernel.org/r/51e088d1e8659b9411534a5a3ad03d88c79a5297.1643246827.git.siyanteng@loongson.cn Signed-off-by: Jonathan Corbet --- .../translations/zh_CN/vm/highmem.rst | 128 ++++++++++++++++++ Documentation/translations/zh_CN/vm/index.rst | 2 +- 2 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/vm/highmem.rst diff --git a/Documentation/translations/zh_CN/vm/highmem.rst b/Documentation/translations/zh_CN/vm/highmem.rst new file mode 100644 index 000000000000..018838e58c3e --- /dev/null +++ b/Documentation/translations/zh_CN/vm/highmem.rst @@ -0,0 +1,128 @@ +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/vm/highmem.rst + +:翻译: + + 司延腾 Yanteng Si + +:校译: + +========== +高内存处理 +========== + +作者: Peter Zijlstra + +.. contents:: :local: + +高内存是什么? +============== + +当物理内存的大小接近或超过虚拟内存的最大大小时,就会使用高内存(highmem)。在这一点上,内 +核不可能在任何时候都保持所有可用的物理内存的映射。这意味着内核需要开始使用它想访问的物理内 +存的临时映射。 + +没有被永久映射覆盖的那部分(物理)内存就是我们所说的 "高内存"。对于这个边界的确切位置,有 +各种架构上的限制。 + +例如,在i386架构中,我们选择将内核映射到每个进程的虚拟空间,这样我们就不必为内核的进入/退 +出付出全部的TLB作废代价。这意味着可用的虚拟内存空间(i386上为4GiB)必须在用户和内核空间之 +间进行划分。 + +使用这种方法的架构的传统分配方式是3:1,3GiB用于用户空间,顶部的1GiB用于内核空间。:: + + +--------+ 0xffffffff + | Kernel | + +--------+ 0xc0000000 + | | + | User | + | | + +--------+ 0x00000000 + +这意味着内核在任何时候最多可以映射1GiB的物理内存,但是由于我们需要虚拟地址空间来做其他事 +情--包括访问其余物理内存的临时映射--实际的直接映射通常会更少(通常在~896MiB左右)。 + +其他有mm上下文标签的TLB的架构可以有独立的内核和用户映射。然而,一些硬件(如一些ARM)在使 +用mm上下文标签时,其虚拟空间有限。 + + +临时虚拟映射 +============ + +内核包含几种创建临时映射的方法。: + +* vmap(). 这可以用来将多个物理页长期映射到一个连续的虚拟空间。它需要synchronization + 来解除映射。 + +* kmap(). 这允许对单个页面进行短期映射。它需要synchronization,但在一定程度上被摊销。 + 当以嵌套方式使用时,它也很容易出现死锁,因此不建议在新代码中使用它。 + +* kmap_atomic(). 这允许对单个页面进行非常短的时间映射。由于映射被限制在发布它的CPU上, + 它表现得很好,但发布任务因此被要求留在该CPU上直到它完成,以免其他任务取代它的映射。 + + kmap_atomic() 也可以由中断上下文使用,因为它不睡眠,而且调用者可能在调用kunmap_atomic() + 之后才睡眠。 + + 可以假设k[un]map_atomic()不会失败。 + + +使用kmap_atomic +=============== + +何时何地使用 kmap_atomic() 是很直接的。当代码想要访问一个可能从高内存(见__GFP_HIGHMEM) +分配的页面的内容时,例如在页缓存中的页面,就会使用它。该API有两个函数,它们的使用方式与 +下面类似:: + + /* 找到感兴趣的页面。 */ + struct page *page = find_get_page(mapping, offset); + + /* 获得对该页内容的访问权。 */ + void *vaddr = kmap_atomic(page); + + /* 对该页的内容做一些处理。 */ + memset(vaddr, 0, PAGE_SIZE); + + /* 解除该页面的映射。 */ + kunmap_atomic(vaddr); + +注意,kunmap_atomic()调用的是kmap_atomic()调用的结果而不是参数。 + +如果你需要映射两个页面,因为你想从一个页面复制到另一个页面,你需要保持kmap_atomic调用严 +格嵌套,如:: + + vaddr1 = kmap_atomic(page1); + vaddr2 = kmap_atomic(page2); + + memcpy(vaddr1, vaddr2, PAGE_SIZE); + + kunmap_atomic(vaddr2); + kunmap_atomic(vaddr1); + + +临时映射的成本 +============== + +创建临时映射的代价可能相当高。体系架构必须操作内核的页表、数据TLB和/或MMU的寄存器。 + +如果CONFIG_HIGHMEM没有被设置,那么内核会尝试用一点计算来创建映射,将页面结构地址转换成 +指向页面内容的指针,而不是去捣鼓映射。在这种情况下,解映射操作可能是一个空操作。 + +如果CONFIG_MMU没有被设置,那么就不可能有临时映射和高内存。在这种情况下,也将使用计算方法。 + + +i386 PAE +======== + +在某些情况下,i386 架构将允许你在 32 位机器上安装多达 64GiB 的内存。但这有一些后果: + +* Linux需要为系统中的每个页面建立一个页帧结构,而且页帧需要驻在永久映射中,这意味着: + +* 你最多可以有896M/sizeof(struct page)页帧;由于页结构体是32字节的,所以最终会有 + 112G的页;然而,内核需要在内存中存储更多的页帧...... + +* PAE使你的页表变大--这使系统变慢,因为更多的数据需要在TLB填充等方面被访问。一个好处 + 是,PAE有更多的PTE位,可以提供像NX和PAT这样的高级功能。 + +一般的建议是,你不要在32位机器上使用超过8GiB的空间--尽管更多的空间可能对你和你的工作 +量有用,但你几乎是靠你自己--不要指望内核开发者真的会很关心事情的进展情况。 diff --git a/Documentation/translations/zh_CN/vm/index.rst b/Documentation/translations/zh_CN/vm/index.rst index 13df8321f242..2f9834eb9475 100644 --- a/Documentation/translations/zh_CN/vm/index.rst +++ b/Documentation/translations/zh_CN/vm/index.rst @@ -25,12 +25,12 @@ TODO:待引用文档集被翻译完毕后请及时修改此处) balance damon/index free_page_reporting + highmem TODOLIST: * arch_pgtable_helpers * free_page_reporting * frontswap -* highmem * hmm * hwpoison * hugetlbfs_reserv From 869f496e1aa6d2b9a8653d65aa7040970f76627a Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 27 Jan 2022 18:32:58 +0200 Subject: [PATCH 022/294] docs: process: submitting-patches: Clarify the Reported-by usage It's unclear from "Submitting Patches" documentation that Reported-by is not supposed to be used against new features. (It's more clear in the section 5.4 "Patch formatting and changelogs" of the "A guide to the Kernel Development Process", where it suggests that change should fix something existing in the kernel. Clarify the Reported-by usage in the "Submitting Patches". Reported-by: Florian Eckert Signed-off-by: Andy Shevchenko Acked-by: Randy Dunlap Link: https://lore.kernel.org/r/20220127163258.48482-1-andriy.shevchenko@linux.intel.com Signed-off-by: Jonathan Corbet --- Documentation/process/submitting-patches.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Documentation/process/submitting-patches.rst b/Documentation/process/submitting-patches.rst index 31ea120ce531..fb496b2ebfd3 100644 --- a/Documentation/process/submitting-patches.rst +++ b/Documentation/process/submitting-patches.rst @@ -495,7 +495,8 @@ Using Reported-by:, Tested-by:, Reviewed-by:, Suggested-by: and Fixes: The Reported-by tag gives credit to people who find bugs and report them and it hopefully inspires them to help us again in the future. Please note that if the bug was reported in private, then ask for permission first before using the -Reported-by tag. +Reported-by tag. The tag is intended for bugs; please do not use it to credit +feature requests. A Tested-by: tag indicates that the patch has been successfully tested (in some environment) by the person named. This tag informs maintainers that From dc6cdced47ce1edbca79bdc5a3cd4e435b5358dd Mon Sep 17 00:00:00 2001 From: Tang Yizhou Date: Sat, 25 Dec 2021 09:51:46 +0800 Subject: [PATCH 023/294] docs/zh_CN: Add rbtree Chinese translation Translate core-api/rbtree.rst into Chinese. Signed-off-by: Tang Yizhou Reviewed-by: Alex Shi Link: https://lore.kernel.org/r/20211225015146.1535-1-tangyizhou@huawei.com Signed-off-by: Jonathan Corbet --- .../translations/zh_CN/core-api/index.rst | 2 +- .../translations/zh_CN/core-api/rbtree.rst | 391 ++++++++++++++++++ 2 files changed, 392 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/core-api/rbtree.rst diff --git a/Documentation/translations/zh_CN/core-api/index.rst b/Documentation/translations/zh_CN/core-api/index.rst index d10191c45cf1..26d9913fc8b6 100644 --- a/Documentation/translations/zh_CN/core-api/index.rst +++ b/Documentation/translations/zh_CN/core-api/index.rst @@ -42,6 +42,7 @@ kref assoc_array xarray + rbtree Todolist: @@ -49,7 +50,6 @@ Todolist: idr circular-buffers - rbtree generic-radix-tree packing bus-virt-phys-mapping diff --git a/Documentation/translations/zh_CN/core-api/rbtree.rst b/Documentation/translations/zh_CN/core-api/rbtree.rst new file mode 100644 index 000000000000..a3e1555cb974 --- /dev/null +++ b/Documentation/translations/zh_CN/core-api/rbtree.rst @@ -0,0 +1,391 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/core-api/rbtree.rst + +:翻译: + + 唐艺舟 Tang Yizhou + +========================= +Linux中的红黑树(rbtree) +========================= + + +:日期: 2007年1月18日 +:作者: Rob Landley + +何为红黑树,它们有什么用? +-------------------------- + +红黑树是一种自平衡二叉搜索树,被用来存储可排序的键/值数据对。这与基数树(被用来高效 +存储稀疏数组,因此使用长整型下标来插入/访问/删除结点)和哈希表(没有保持排序因而无法 +容易地按序遍历,同时必须调节其大小和哈希函数,然而红黑树可以优雅地伸缩以便存储任意 +数量的键)不同。 + +红黑树和AVL树类似,但在插入和删除时提供了更快的实时有界的最坏情况性能(分别最多两次 +旋转和三次旋转,来平衡树),查询时间轻微变慢(但时间复杂度仍然是O(log n))。 + +引用Linux每周新闻(Linux Weekly News): + + 内核中有多处红黑树的使用案例。最后期限调度器和完全公平排队(CFQ)I/O调度器利用 + 红黑树跟踪请求;数据包CD/DVD驱动程序也是如此。高精度时钟代码使用一颗红黑树组织 + 未完成的定时器请求。ext3文件系统用红黑树跟踪目录项。虚拟内存区域(VMAs)、epoll + 文件描述符、密码学密钥和在“分层令牌桶”调度器中的网络数据包都由红黑树跟踪。 + +本文档涵盖了对Linux红黑树实现的使用方法。更多关于红黑树的性质和实现的信息,参见: + + Linux每周新闻关于红黑树的文章 + https://lwn.net/Articles/184495/ + + 维基百科红黑树词条 + https://en.wikipedia.org/wiki/Red-black_tree + +红黑树的Linux实现 +----------------- + +Linux的红黑树实现在文件“lib/rbtree.c”中。要使用它,需要“#include ”。 + +Linux的红黑树实现对速度进行了优化,因此比传统的实现少一个间接层(有更好的缓存局部性)。 +每个rb_node结构体的实例嵌入在它管理的数据结构中,因此不需要靠指针来分离rb_node和它 +管理的数据结构。用户应该编写他们自己的树搜索和插入函数,来调用已提供的红黑树函数, +而不是使用一个比较回调函数指针。加锁代码也留给红黑树的用户编写。 + +创建一颗红黑树 +-------------- + +红黑树中的数据结点是包含rb_node结构体成员的结构体:: + + struct mytype { + struct rb_node node; + char *keystring; + }; + +当处理一个指向内嵌rb_node结构体的指针时,包住rb_node的结构体可用标准的container_of() +宏访问。此外,个体成员可直接用rb_entry(node, type, member)访问。 + +每颗红黑树的根是一个rb_root数据结构,它由以下方式初始化为空: + + struct rb_root mytree = RB_ROOT; + +在一颗红黑树中搜索值 +-------------------- + +为你的树写一个搜索函数是相当简单的:从树根开始,比较每个值,然后根据需要继续前往左边或 +右边的分支。 + +示例:: + + struct mytype *my_search(struct rb_root *root, char *string) + { + struct rb_node *node = root->rb_node; + + while (node) { + struct mytype *data = container_of(node, struct mytype, node); + int result; + + result = strcmp(string, data->keystring); + + if (result < 0) + node = node->rb_left; + else if (result > 0) + node = node->rb_right; + else + return data; + } + return NULL; + } + +在一颗红黑树中插入数据 +---------------------- + +在树中插入数据的步骤包括:首先搜索插入新结点的位置,然后插入结点并对树再平衡 +("recoloring")。 + +插入的搜索和上文的搜索不同,它要找到嫁接新结点的位置。新结点也需要一个指向它的父节点 +的链接,以达到再平衡的目的。 + +示例:: + + int my_insert(struct rb_root *root, struct mytype *data) + { + struct rb_node **new = &(root->rb_node), *parent = NULL; + + /* Figure out where to put new node */ + while (*new) { + struct mytype *this = container_of(*new, struct mytype, node); + int result = strcmp(data->keystring, this->keystring); + + parent = *new; + if (result < 0) + new = &((*new)->rb_left); + else if (result > 0) + new = &((*new)->rb_right); + else + return FALSE; + } + + /* Add new node and rebalance tree. */ + rb_link_node(&data->node, parent, new); + rb_insert_color(&data->node, root); + + return TRUE; + } + +在一颗红黑树中删除或替换已经存在的数据 +-------------------------------------- + +若要从树中删除一个已经存在的结点,调用:: + + void rb_erase(struct rb_node *victim, struct rb_root *tree); + +示例:: + + struct mytype *data = mysearch(&mytree, "walrus"); + + if (data) { + rb_erase(&data->node, &mytree); + myfree(data); + } + +若要用一个新结点替换树中一个已经存在的键值相同的结点,调用:: + + void rb_replace_node(struct rb_node *old, struct rb_node *new, + struct rb_root *tree); + +通过这种方式替换结点不会对树做重排序:如果新结点的键值和旧结点不同,红黑树可能被 +破坏。 + +(按排序的顺序)遍历存储在红黑树中的元素 +---------------------------------------- + +我们提供了四个函数,用于以排序的方式遍历一颗红黑树的内容。这些函数可以在任意红黑树 +上工作,并且不需要被修改或包装(除非加锁的目的):: + + struct rb_node *rb_first(struct rb_root *tree); + struct rb_node *rb_last(struct rb_root *tree); + struct rb_node *rb_next(struct rb_node *node); + struct rb_node *rb_prev(struct rb_node *node); + +要开始迭代,需要使用一个指向树根的指针调用rb_first()或rb_last(),它将返回一个指向 +树中第一个或最后一个元素所包含的节点结构的指针。要继续的话,可以在当前结点上调用 +rb_next()或rb_prev()来获取下一个或上一个结点。当没有剩余的结点时,将返回NULL。 + +迭代器函数返回一个指向被嵌入的rb_node结构体的指针,由此,包住rb_node的结构体可用 +标准的container_of()宏访问。此外,个体成员可直接用rb_entry(node, type, member) +访问。 + +示例:: + + struct rb_node *node; + for (node = rb_first(&mytree); node; node = rb_next(node)) + printk("key=%s\n", rb_entry(node, struct mytype, node)->keystring); + +带缓存的红黑树 +-------------- + +计算最左边(最小的)结点是二叉搜索树的一个相当常见的任务,例如用于遍历,或用户根据 +他们自己的逻辑依赖一个特定的顺序。为此,用户可以使用'struct rb_root_cached'来优化 +时间复杂度为O(logN)的rb_first()的调用,以简单地获取指针,避免了潜在的昂贵的树迭代。 +维护操作的额外运行时间开销可忽略,不过内存占用较大。 + +和rb_root结构体类似,带缓存的红黑树由以下方式初始化为空:: + + struct rb_root_cached mytree = RB_ROOT_CACHED; + +带缓存的红黑树只是一个常规的rb_root,加上一个额外的指针来缓存最左边的节点。这使得 +rb_root_cached可以存在于rb_root存在的任何地方,并且只需增加几个接口来支持带缓存的 +树:: + + struct rb_node *rb_first_cached(struct rb_root_cached *tree); + void rb_insert_color_cached(struct rb_node *, struct rb_root_cached *, bool); + void rb_erase_cached(struct rb_node *node, struct rb_root_cached *); + +操作和删除也有对应的带缓存的树的调用:: + + void rb_insert_augmented_cached(struct rb_node *node, struct rb_root_cached *, + bool, struct rb_augment_callbacks *); + void rb_erase_augmented_cached(struct rb_node *, struct rb_root_cached *, + struct rb_augment_callbacks *); + + +对增强型红黑树的支持 +-------------------- + +增强型红黑树是一种在每个结点里存储了“一些”附加数据的红黑树,其中结点N的附加数据 +必须是以N为根的子树中所有结点的内容的函数。它是建立在红黑树基础设施之上的可选特性。 +想要使用这个特性的红黑树用户,插入和删除结点时必须调用增强型接口并提供增强型回调函数。 + +实现增强型红黑树操作的C文件必须包含而不是。 +注意,linux/rbtree_augmented.h暴露了一些红黑树实现的细节而你不应依赖它们,请坚持 +使用文档记录的API,并且不要在头文件中包含,以最小化你的 +用户意外地依赖这些实现细节的可能。 + +插入时,用户必须更新通往被插入节点的路径上的增强信息,然后像往常一样调用rb_link_node(), +然后是rb_augment_inserted()而不是平时的rb_insert_color()调用。如果 +rb_augment_inserted()再平衡了红黑树,它将回调至一个用户提供的函数来更新受影响的 +子树上的增强信息。 + +删除一个结点时,用户必须调用rb_erase_augmented()而不是rb_erase()。 +rb_erase_augmented()回调至一个用户提供的函数来更新受影响的子树上的增强信息。 + +在两种情况下,回调都是通过rb_augment_callbacks结构体提供的。必须定义3个回调: + +- 一个传播回调,它更新一个给定结点和它的祖先们的增强数据,直到一个给定的停止点 + (如果是NULL,将更新一路更新到树根)。 + +- 一个复制回调,它将一颗给定子树的增强数据复制到一个新指定的子树树根。 + +- 一个树旋转回调,它将一颗给定的子树的增强值复制到新指定的子树树根上,并重新计算 + 先前的子树树根的增强值。 + +rb_erase_augmented()编译后的代码可能会内联传播、复制回调,这将导致函数体积更大, +因此每个增强型红黑树的用户应该只有一个rb_erase_augmented()的调用点,以限制编译后 +的代码大小。 + + +使用示例 +^^^^^^^^ + +区间树是增强型红黑树的一个例子。参考Cormen,Leiserson,Rivest和Stein写的 +《算法导论》。区间树的更多细节: + +经典的红黑树只有一个键,它不能直接用来存储像[lo:hi]这样的区间范围,也不能快速查找 +与新的lo:hi重叠的部分,或者查找是否有与新的lo:hi完全匹配的部分。 + +然而,红黑树可以被增强,以一种结构化的方式来存储这种区间范围,从而使高效的查找和 +精确匹配成为可能。 + +这个存储在每个节点中的“额外信息”是其所有后代结点中的最大hi(max_hi)值。这个信息 +可以保持在每个结点上,只需查看一下该结点和它的直系子结点们。这将被用于时间复杂度 +为O(log n)的最低匹配查找(所有可能的匹配中最低的起始地址),就像这样:: + + struct interval_tree_node * + interval_tree_first_match(struct rb_root *root, + unsigned long start, unsigned long last) + { + struct interval_tree_node *node; + + if (!root->rb_node) + return NULL; + node = rb_entry(root->rb_node, struct interval_tree_node, rb); + + while (true) { + if (node->rb.rb_left) { + struct interval_tree_node *left = + rb_entry(node->rb.rb_left, + struct interval_tree_node, rb); + if (left->__subtree_last >= start) { + /* + * Some nodes in left subtree satisfy Cond2. + * Iterate to find the leftmost such node N. + * If it also satisfies Cond1, that's the match + * we are looking for. Otherwise, there is no + * matching interval as nodes to the right of N + * can't satisfy Cond1 either. + */ + node = left; + continue; + } + } + if (node->start <= last) { /* Cond1 */ + if (node->last >= start) /* Cond2 */ + return node; /* node is leftmost match */ + if (node->rb.rb_right) { + node = rb_entry(node->rb.rb_right, + struct interval_tree_node, rb); + if (node->__subtree_last >= start) + continue; + } + } + return NULL; /* No match */ + } + } + +插入/删除是通过以下增强型回调来定义的:: + + static inline unsigned long + compute_subtree_last(struct interval_tree_node *node) + { + unsigned long max = node->last, subtree_last; + if (node->rb.rb_left) { + subtree_last = rb_entry(node->rb.rb_left, + struct interval_tree_node, rb)->__subtree_last; + if (max < subtree_last) + max = subtree_last; + } + if (node->rb.rb_right) { + subtree_last = rb_entry(node->rb.rb_right, + struct interval_tree_node, rb)->__subtree_last; + if (max < subtree_last) + max = subtree_last; + } + return max; + } + + static void augment_propagate(struct rb_node *rb, struct rb_node *stop) + { + while (rb != stop) { + struct interval_tree_node *node = + rb_entry(rb, struct interval_tree_node, rb); + unsigned long subtree_last = compute_subtree_last(node); + if (node->__subtree_last == subtree_last) + break; + node->__subtree_last = subtree_last; + rb = rb_parent(&node->rb); + } + } + + static void augment_copy(struct rb_node *rb_old, struct rb_node *rb_new) + { + struct interval_tree_node *old = + rb_entry(rb_old, struct interval_tree_node, rb); + struct interval_tree_node *new = + rb_entry(rb_new, struct interval_tree_node, rb); + + new->__subtree_last = old->__subtree_last; + } + + static void augment_rotate(struct rb_node *rb_old, struct rb_node *rb_new) + { + struct interval_tree_node *old = + rb_entry(rb_old, struct interval_tree_node, rb); + struct interval_tree_node *new = + rb_entry(rb_new, struct interval_tree_node, rb); + + new->__subtree_last = old->__subtree_last; + old->__subtree_last = compute_subtree_last(old); + } + + static const struct rb_augment_callbacks augment_callbacks = { + augment_propagate, augment_copy, augment_rotate + }; + + void interval_tree_insert(struct interval_tree_node *node, + struct rb_root *root) + { + struct rb_node **link = &root->rb_node, *rb_parent = NULL; + unsigned long start = node->start, last = node->last; + struct interval_tree_node *parent; + + while (*link) { + rb_parent = *link; + parent = rb_entry(rb_parent, struct interval_tree_node, rb); + if (parent->__subtree_last < last) + parent->__subtree_last = last; + if (start < parent->start) + link = &parent->rb.rb_left; + else + link = &parent->rb.rb_right; + } + + node->__subtree_last = last; + rb_link_node(&node->rb, rb_parent, link); + rb_insert_augmented(&node->rb, root, &augment_callbacks); + } + + void interval_tree_remove(struct interval_tree_node *node, + struct rb_root *root) + { + rb_erase_augmented(&node->rb, root, &augment_callbacks); + } From 5dbbc145d24ac68fc481fc6fe0e4ec3d7a4b8007 Mon Sep 17 00:00:00 2001 From: Tang Yizhou Date: Tue, 21 Dec 2021 11:18:18 +0800 Subject: [PATCH 024/294] docs: scheduler: Fix outdated parameter of rebalance_domains According to the function prototype of rebalance_domains(), its first parameter is *rq* and the document need to be updated. Signed-off-by: Tang Yizhou Link: https://lore.kernel.org/r/20211221031818.23186-1-tangyizhou@huawei.com Signed-off-by: Jonathan Corbet --- Documentation/scheduler/sched-domains.rst | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Documentation/scheduler/sched-domains.rst b/Documentation/scheduler/sched-domains.rst index 84dcdcd2911c..e57ad28301bd 100644 --- a/Documentation/scheduler/sched-domains.rst +++ b/Documentation/scheduler/sched-domains.rst @@ -37,10 +37,10 @@ rebalancing event for the current runqueue has arrived. The actual load balancing workhorse, run_rebalance_domains()->rebalance_domains(), is then run in softirq context (SCHED_SOFTIRQ). -The latter function takes two arguments: the current CPU and whether it was idle -at the time the scheduler_tick() happened and iterates over all sched domains -our CPU is on, starting from its base domain and going up the ->parent chain. -While doing that, it checks to see if the current domain has exhausted its +The latter function takes two arguments: the runqueue of current CPU and whether +the CPU was idle at the time the scheduler_tick() happened and iterates over all +sched domains our CPU is on, starting from its base domain and going up the ->parent +chain. While doing that, it checks to see if the current domain has exhausted its rebalance interval. If so, it runs load_balance() on that domain. It then checks the parent sched_domain (if it exists), and the parent of the parent and so forth. From 4d266c247d56751c2c97e0c411212b59e90922fc Mon Sep 17 00:00:00 2001 From: Neeraj Upadhyay Date: Mon, 13 Dec 2021 11:40:24 +0530 Subject: [PATCH 025/294] rcu/exp: Fix check for idle context in rcu_exp_handler For PREEMPT_RCU, the rcu_exp_handler() function checks whether the current CPU is in idle, by calling rcu_dynticks_curr_cpu_in_eqs(). However, rcu_exp_handler() is called in IPI handler context. So, it should be checking the idle context using rcu_is_cpu_rrupt_from_idle(). Fix this by using rcu_is_cpu_rrupt_from_idle() instead of rcu_dynticks_curr_cpu_in_eqs(). Non-preempt configuration already uses the correct check. Reviewed-by: Frederic Weisbecker Signed-off-by: Neeraj Upadhyay Signed-off-by: Paul E. McKenney --- kernel/rcu/tree_exp.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/rcu/tree_exp.h b/kernel/rcu/tree_exp.h index 237a79989aba..1568c8ef185b 100644 --- a/kernel/rcu/tree_exp.h +++ b/kernel/rcu/tree_exp.h @@ -656,7 +656,7 @@ static void rcu_exp_handler(void *unused) */ if (!depth) { if (!(preempt_count() & (PREEMPT_MASK | SOFTIRQ_MASK)) || - rcu_dynticks_curr_cpu_in_eqs()) { + rcu_is_cpu_rrupt_from_idle()) { rcu_report_exp_rdp(rdp); } else { WRITE_ONCE(rdp->cpu_no_qs.b.exp, true); From 63c564da11cbed96ec6cf0b5faf6af0b7e3624d2 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 13 Dec 2021 21:00:02 -0800 Subject: [PATCH 026/294] rcu: Mark ->expmask access in synchronize_rcu_expedited_wait() This commit adds a READ_ONCE() to an access to the rcu_node structure's ->expmask field to prevent compiler mischief. Detected by KCSAN. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree_exp.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/kernel/rcu/tree_exp.h b/kernel/rcu/tree_exp.h index 1568c8ef185b..60197ea24ceb 100644 --- a/kernel/rcu/tree_exp.h +++ b/kernel/rcu/tree_exp.h @@ -502,7 +502,8 @@ static void synchronize_rcu_expedited_wait(void) if (synchronize_rcu_expedited_wait_once(1)) return; rcu_for_each_leaf_node(rnp) { - for_each_leaf_node_cpu_mask(rnp, cpu, rnp->expmask) { + mask = READ_ONCE(rnp->expmask); + for_each_leaf_node_cpu_mask(rnp, cpu, mask) { rdp = per_cpu_ptr(&rcu_data, cpu); if (rdp->rcu_forced_tick_exp) continue; From a47f9f131dfe4f765e385fa90e13032eadb00bac Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 13 Dec 2021 11:05:07 -0800 Subject: [PATCH 027/294] rcu: Mark accesses to boost_starttime The boost_starttime shared variable has conflicting unmarked C-language accesses, which are dangerous at best. This commit therefore adds appropriate marking. This was found by KCSAN. Signed-off-by: Paul E. McKenney --- kernel/rcu/rcutorture.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 422f7e4cc08d..829ae0b7d3c0 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -997,7 +997,7 @@ static int rcu_torture_boost(void *arg) goto checkwait; /* Wait for the next test interval. */ - oldstarttime = boost_starttime; + oldstarttime = READ_ONCE(boost_starttime); while (time_before(jiffies, oldstarttime)) { schedule_timeout_interruptible(oldstarttime - jiffies); if (stutter_wait("rcu_torture_boost")) @@ -1041,10 +1041,11 @@ static int rcu_torture_boost(void *arg) * interval. Besides, we are running at RT priority, * so delays should be relatively rare. */ - while (oldstarttime == boost_starttime && !kthread_should_stop()) { + while (oldstarttime == READ_ONCE(boost_starttime) && !kthread_should_stop()) { if (mutex_trylock(&boost_mutex)) { if (oldstarttime == boost_starttime) { - boost_starttime = jiffies + test_boost_interval * HZ; + WRITE_ONCE(boost_starttime, + jiffies + test_boost_interval * HZ); n_rcu_torture_boosts++; } mutex_unlock(&boost_mutex); From 02e3024175274ed4bf7912e7a1281b300cec76b5 Mon Sep 17 00:00:00 2001 From: Neeraj Upadhyay Date: Sat, 11 Dec 2021 22:31:39 +0530 Subject: [PATCH 028/294] rcu/nocb: Handle concurrent nocb kthreads creation When multiple CPUs in the same nocb gp/cb group concurrently come online, they might try to concurrently create the same rcuog kthread. Fix this by using nocb gp CPU's spawn mutex to provide mutual exclusion for the rcuog kthread creation code. [ paulmck: Whitespace fixes per kernel test robot feedback. ] Acked-by: David Woodhouse Signed-off-by: Neeraj Upadhyay Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.h | 2 ++ kernel/rcu/tree_nocb.h | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 486fc901bd08..24dd4b0d805f 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -203,6 +203,8 @@ struct rcu_data { int nocb_defer_wakeup; /* Defer wakeup of nocb_kthread. */ struct timer_list nocb_timer; /* Enforce finite deferral. */ unsigned long nocb_gp_adv_time; /* Last call_rcu() CB adv (jiffies). */ + struct mutex nocb_gp_kthread_mutex; /* Exclusion for nocb gp kthread */ + /* spawning */ /* The following fields are used by call_rcu, hence own cacheline. */ raw_spinlock_t nocb_bypass_lock ____cacheline_internodealigned_in_smp; diff --git a/kernel/rcu/tree_nocb.h b/kernel/rcu/tree_nocb.h index eeafb546a7a0..1e40519d1a05 100644 --- a/kernel/rcu/tree_nocb.h +++ b/kernel/rcu/tree_nocb.h @@ -1226,6 +1226,7 @@ static void __init rcu_boot_init_nocb_percpu_data(struct rcu_data *rdp) raw_spin_lock_init(&rdp->nocb_gp_lock); timer_setup(&rdp->nocb_timer, do_nocb_deferred_wakeup_timer, 0); rcu_cblist_init(&rdp->nocb_bypass); + mutex_init(&rdp->nocb_gp_kthread_mutex); } /* @@ -1248,13 +1249,17 @@ static void rcu_spawn_cpu_nocb_kthread(int cpu) /* If we didn't spawn the GP kthread first, reorganize! */ rdp_gp = rdp->nocb_gp_rdp; + mutex_lock(&rdp_gp->nocb_gp_kthread_mutex); if (!rdp_gp->nocb_gp_kthread) { t = kthread_run(rcu_nocb_gp_kthread, rdp_gp, "rcuog/%d", rdp_gp->cpu); - if (WARN_ONCE(IS_ERR(t), "%s: Could not start rcuo GP kthread, OOM is now expected behavior\n", __func__)) + if (WARN_ONCE(IS_ERR(t), "%s: Could not start rcuo GP kthread, OOM is now expected behavior\n", __func__)) { + mutex_unlock(&rdp_gp->nocb_gp_kthread_mutex); return; + } WRITE_ONCE(rdp_gp->nocb_gp_kthread, t); } + mutex_unlock(&rdp_gp->nocb_gp_kthread_mutex); /* Spawn the kthread for this CPU. */ t = kthread_run(rcu_nocb_cb_kthread, rdp, From eae9f147a4b02e132187a2d88a403b9ccc28212a Mon Sep 17 00:00:00 2001 From: Neeraj Upadhyay Date: Mon, 13 Dec 2021 12:32:09 +0530 Subject: [PATCH 029/294] rcu: Remove unused rcu_state.boost Signed-off-by: Neeraj Upadhyay Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.h | 5 ++--- kernel/rcu/tree_plugin.h | 2 -- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 24dd4b0d805f..e9990945483f 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -304,9 +304,8 @@ struct rcu_state { /* The following fields are guarded by the root rcu_node's lock. */ - u8 boost ____cacheline_internodealigned_in_smp; - /* Subject to priority boost. */ - unsigned long gp_seq; /* Grace-period sequence #. */ + unsigned long gp_seq ____cacheline_internodealigned_in_smp; + /* Grace-period sequence #. */ unsigned long gp_max; /* Maximum GP duration in */ /* jiffies. */ struct task_struct *gp_kthread; /* Task for grace periods. */ diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index c5b45c2f68a1..109429e70a64 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -1175,8 +1175,6 @@ static void rcu_spawn_one_boost_kthread(struct rcu_node *rnp) if (rnp->boost_kthread_task || !rcu_scheduler_fully_active) return; - rcu_state.boost = 1; - t = kthread_create(rcu_boost_kthread, (void *)rnp, "rcub/%d", rnp_index); if (WARN_ON_ONCE(IS_ERR(t))) From 4b4399b2450de38916718ba9947e6cdb69c99c55 Mon Sep 17 00:00:00 2001 From: Zqiang Date: Wed, 29 Dec 2021 00:05:10 +0800 Subject: [PATCH 030/294] rcu: Create per-cpu rcuc kthreads only when rcutree.use_softirq=0 The per-CPU "rcuc" kthreads are used only by kernels booted with rcutree.use_softirq=0, but they are nevertheless unconditionally created by kernels built with CONFIG_RCU_BOOST=y. This results in "rcuc" kthreads being created that are never actually used. This commit therefore refrains from creating these kthreads unless the kernel is actually booted with rcutree.use_softirq=0. Acked-by: Sebastian Andrzej Siewior Signed-off-by: Zqiang Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index a4c25a6283b0..4e5e37e5ee3c 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -2894,7 +2894,7 @@ static int __init rcu_spawn_core_kthreads(void) for_each_possible_cpu(cpu) per_cpu(rcu_data.rcu_cpu_has_work, cpu) = 0; - if (!IS_ENABLED(CONFIG_RCU_BOOST) && use_softirq) + if (use_softirq) return 0; WARN_ONCE(smpboot_register_percpu_thread(&rcu_cpu_thread_spec), "%s: Could not start rcuc kthread, OOM is now expected behavior\n", __func__); From c8db27dd0ea8071d2ea29a1a401c4ccc611ec6c1 Mon Sep 17 00:00:00 2001 From: Alison Chaiken Date: Tue, 11 Jan 2022 15:32:50 -0800 Subject: [PATCH 031/294] rcu: Move kthread_prio bounds-check to a separate function Move the bounds-check of the kthread_prio cmdline parameter to a new function in order to faciliate a different callsite. Signed-off-by: Alison Chaiken Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 4e5e37e5ee3c..5bf0312f6676 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -4440,26 +4440,10 @@ static int rcu_pm_notify(struct notifier_block *self, static int __init rcu_spawn_gp_kthread(void) { unsigned long flags; - int kthread_prio_in = kthread_prio; struct rcu_node *rnp; struct sched_param sp; struct task_struct *t; - /* Force priority into range. */ - if (IS_ENABLED(CONFIG_RCU_BOOST) && kthread_prio < 2 - && IS_BUILTIN(CONFIG_RCU_TORTURE_TEST)) - kthread_prio = 2; - else if (IS_ENABLED(CONFIG_RCU_BOOST) && kthread_prio < 1) - kthread_prio = 1; - else if (kthread_prio < 0) - kthread_prio = 0; - else if (kthread_prio > 99) - kthread_prio = 99; - - if (kthread_prio != kthread_prio_in) - pr_alert("rcu_spawn_gp_kthread(): Limited prio to %d from %d\n", - kthread_prio, kthread_prio_in); - rcu_scheduler_fully_active = 1; t = kthread_create(rcu_gp_kthread, NULL, "%s", rcu_state.name); if (WARN_ONCE(IS_ERR(t), "%s: Could not start grace-period kthread, OOM is now expected behavior\n", __func__)) @@ -4584,6 +4568,28 @@ static void __init rcu_init_one(void) } } +/* + * Force priority from the kernel command-line into range. + */ +static void __init sanitize_kthread_prio(void) +{ + int kthread_prio_in = kthread_prio; + + if (IS_ENABLED(CONFIG_RCU_BOOST) && kthread_prio < 2 + && IS_BUILTIN(CONFIG_RCU_TORTURE_TEST)) + kthread_prio = 2; + else if (IS_ENABLED(CONFIG_RCU_BOOST) && kthread_prio < 1) + kthread_prio = 1; + else if (kthread_prio < 0) + kthread_prio = 0; + else if (kthread_prio > 99) + kthread_prio = 99; + + if (kthread_prio != kthread_prio_in) + pr_alert("%s: Limited prio to %d from %d\n", + __func__, kthread_prio, kthread_prio_in); +} + /* * Compute the rcu_node tree geometry from kernel parameters. This cannot * replace the definitions in tree.h because those are needed to size @@ -4744,6 +4750,7 @@ void __init rcu_init(void) kfree_rcu_batch_init(); rcu_bootup_announce(); + sanitize_kthread_prio(); rcu_init_geometry(); rcu_init_one(); if (dump_tree) From 54577e23fa0791599db1a3d86fc8e7a205d3da75 Mon Sep 17 00:00:00 2001 From: Alison Chaiken Date: Tue, 11 Jan 2022 15:32:51 -0800 Subject: [PATCH 032/294] rcu: Make priority of grace-period thread consistent The priority of RCU grace period threads is set to kthread_prio when they are launched from rcu_spawn_gp_kthread(). The same is not true of rcu_spawn_one_nocb_kthread(). Accordingly, add priority elevation to rcu_spawn_one_nocb_kthread(). Signed-off-by: Alison Chaiken Signed-off-by: Paul E. McKenney --- kernel/rcu/tree_nocb.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/rcu/tree_nocb.h b/kernel/rcu/tree_nocb.h index 1e40519d1a05..ea889cbfc3b9 100644 --- a/kernel/rcu/tree_nocb.h +++ b/kernel/rcu/tree_nocb.h @@ -1239,6 +1239,7 @@ static void rcu_spawn_cpu_nocb_kthread(int cpu) struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu); struct rcu_data *rdp_gp; struct task_struct *t; + struct sched_param sp; if (!rcu_scheduler_fully_active || !rcu_nocb_is_setup) return; @@ -1248,6 +1249,7 @@ static void rcu_spawn_cpu_nocb_kthread(int cpu) return; /* If we didn't spawn the GP kthread first, reorganize! */ + sp.sched_priority = kthread_prio; rdp_gp = rdp->nocb_gp_rdp; mutex_lock(&rdp_gp->nocb_gp_kthread_mutex); if (!rdp_gp->nocb_gp_kthread) { @@ -1258,6 +1260,8 @@ static void rcu_spawn_cpu_nocb_kthread(int cpu) return; } WRITE_ONCE(rdp_gp->nocb_gp_kthread, t); + if (kthread_prio) + sched_setscheduler_nocheck(t, SCHED_FIFO, &sp); } mutex_unlock(&rdp_gp->nocb_gp_kthread_mutex); From c8b16a65267e35ecc5621dbc81cbe7e5b0992fce Mon Sep 17 00:00:00 2001 From: Alison Chaiken Date: Tue, 11 Jan 2022 15:32:52 -0800 Subject: [PATCH 033/294] rcu: Elevate priority of offloaded callback threads When CONFIG_PREEMPT_RT=y, the rcutree.kthread_prio command-line parameter signals initialization code to boost the priority of rcuc callbacks to the designated value. With the additional CONFIG_RCU_NOCB_CPU=y configuration and an additional rcu_nocbs command-line parameter, the callbacks on the listed cores are offloaded to new rcuop kthreads that are not pinned to the cores whose post-grace-period work is performed. While the rcuop kthreads perform the same function as the rcuc kthreads they offload, the kthread_prio parameter only boosts the priority of the rcuc kthreads. Fix this inconsistency by elevating rcuop kthreads to the same priority as the rcuc kthreads. Signed-off-by: Alison Chaiken Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 2 +- kernel/rcu/tree_nocb.h | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 5bf0312f6676..9e4c5b281f00 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -153,7 +153,7 @@ static void sync_sched_exp_online_cleanup(int cpu); static void check_cb_ovld_locked(struct rcu_data *rdp, struct rcu_node *rnp); static bool rcu_rdp_is_offloaded(struct rcu_data *rdp); -/* rcuc/rcub kthread realtime priority */ +/* rcuc/rcub/rcuop kthread realtime priority */ static int kthread_prio = IS_ENABLED(CONFIG_RCU_BOOST) ? 1 : 0; module_param(kthread_prio, int, 0444); diff --git a/kernel/rcu/tree_nocb.h b/kernel/rcu/tree_nocb.h index ea889cbfc3b9..547c41437c76 100644 --- a/kernel/rcu/tree_nocb.h +++ b/kernel/rcu/tree_nocb.h @@ -1270,6 +1270,9 @@ static void rcu_spawn_cpu_nocb_kthread(int cpu) "rcuo%c/%d", rcu_state.abbr, cpu); if (WARN_ONCE(IS_ERR(t), "%s: Could not start rcuo CB kthread, OOM is now expected behavior\n", __func__)) return; + + if (kthread_prio) + sched_setscheduler_nocheck(t, SCHED_FIFO, &sp); WRITE_ONCE(rdp->nocb_cb_kthread, t); WRITE_ONCE(rdp->nocb_gp_kthread, rdp_gp->nocb_gp_kthread); } From a469948b20ed9898f61e9906fd9b135ad853bb94 Mon Sep 17 00:00:00 2001 From: Alison Chaiken Date: Tue, 11 Jan 2022 15:32:53 -0800 Subject: [PATCH 034/294] rcu: Update documentation regarding kthread_prio cmdline parameter Inform readers that the priority of RCU no-callback threads will also be boosted. Signed-off-by: Alison Chaiken Signed-off-by: Paul E. McKenney --- Documentation/admin-guide/kernel-parameters.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index f5a27f067db9..8e2e65122b99 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -4504,6 +4504,8 @@ (the least-favored priority). Otherwise, when RCU_BOOST is not set, valid values are 0-99 and the default is zero (non-realtime operation). + When RCU_NOCB_CPU is set, also adjust the + priority of NOCB callback kthreads. rcutree.rcu_nocb_gp_stride= [KNL] Set the number of NOCB callback kthreads in From 10c535787436d62ea28156a4b91365fd89b5a432 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 21 Jan 2022 12:40:08 -0800 Subject: [PATCH 035/294] rcu: Don't deboost before reporting expedited quiescent state Currently rcu_preempt_deferred_qs_irqrestore() releases rnp->boost_mtx before reporting the expedited quiescent state. Under heavy real-time load, this can result in this function being preempted before the quiescent state is reported, which can in turn prevent the expedited grace period from completing. Tim Murray reports that the resulting expedited grace periods can take hundreds of milliseconds and even more than one second, when they should normally complete in less than a millisecond. This was fine given that there were no particular response-time constraints for synchronize_rcu_expedited(), as it was designed for throughput rather than latency. However, some users now need sub-100-millisecond response-time constratints. This patch therefore follows Neeraj's suggestion (seconded by Tim and by Uladzislau Rezki) of simply reversing the two operations. Reported-by: Tim Murray Reported-by: Joel Fernandes Reported-by: Neeraj Upadhyay Reviewed-by: Neeraj Upadhyay Reviewed-by: Uladzislau Rezki (Sony) Tested-by: Tim Murray Cc: Todd Kjos Cc: Sandeep Patil Cc: # 5.4.x Signed-off-by: Paul E. McKenney --- kernel/rcu/tree_plugin.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 109429e70a64..02ac057ba3f8 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -556,16 +556,16 @@ rcu_preempt_deferred_qs_irqrestore(struct task_struct *t, unsigned long flags) raw_spin_unlock_irqrestore_rcu_node(rnp, flags); } - /* Unboost if we were boosted. */ - if (IS_ENABLED(CONFIG_RCU_BOOST) && drop_boost_mutex) - rt_mutex_futex_unlock(&rnp->boost_mtx.rtmutex); - /* * If this was the last task on the expedited lists, * then we need to report up the rcu_node hierarchy. */ if (!empty_exp && empty_exp_now) rcu_report_exp_rnp(rnp, true); + + /* Unboost if we were boosted. */ + if (IS_ENABLED(CONFIG_RCU_BOOST) && drop_boost_mutex) + rt_mutex_futex_unlock(&rnp->boost_mtx.rtmutex); } else { local_irq_restore(flags); } From c9515875850fefcc79492c5189fe8431e75ddec5 Mon Sep 17 00:00:00 2001 From: Zqiang Date: Tue, 25 Jan 2022 10:47:44 +0800 Subject: [PATCH 036/294] rcu: Add per-CPU rcuc task dumps to RCU CPU stall warnings When the rcutree.use_softirq kernel boot parameter is set to zero, all RCU_SOFTIRQ processing is carried out by the per-CPU rcuc kthreads. If these kthreads are being starved, quiescent states will not be reported, which in turn means that the grace period will not end, which can in turn trigger RCU CPU stall warnings. This commit therefore dumps stack traces of stalled CPUs' rcuc kthreads, which can help identify what is preventing those kthreads from running. Suggested-by: Ammar Faizi Reviewed-by: Ammar Faizi Signed-off-by: Zqiang Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 3 +++ kernel/rcu/tree.h | 1 + kernel/rcu/tree_plugin.h | 3 +++ kernel/rcu/tree_stall.h | 35 +++++++++++++++++++++++++++++++++++ 4 files changed, 42 insertions(+) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 9e4c5b281f00..bd9b2af247ab 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -2850,10 +2850,12 @@ static void rcu_cpu_kthread(unsigned int cpu) { unsigned int *statusp = this_cpu_ptr(&rcu_data.rcu_cpu_kthread_status); char work, *workp = this_cpu_ptr(&rcu_data.rcu_cpu_has_work); + unsigned long *j = this_cpu_ptr(&rcu_data.rcuc_activity); int spincnt; trace_rcu_utilization(TPS("Start CPU kthread@rcu_run")); for (spincnt = 0; spincnt < 10; spincnt++) { + WRITE_ONCE(*j, jiffies); local_bh_disable(); *statusp = RCU_KTHREAD_RUNNING; local_irq_disable(); @@ -2874,6 +2876,7 @@ static void rcu_cpu_kthread(unsigned int cpu) schedule_timeout_idle(2); trace_rcu_utilization(TPS("End CPU kthread@rcu_yield")); *statusp = RCU_KTHREAD_WAITING; + WRITE_ONCE(*j, jiffies); } static struct smp_hotplug_thread rcu_cpu_thread_spec = { diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index e9990945483f..b84cc5742c31 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -239,6 +239,7 @@ struct rcu_data { /* rcuc per-CPU kthread or NULL. */ unsigned int rcu_cpu_kthread_status; char rcu_cpu_has_work; + unsigned long rcuc_activity; /* 7) Diagnostic data, including RCU CPU stall warnings. */ unsigned int softirq_snap; /* Snapshot of softirq activity. */ diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 02ac057ba3f8..8167cab1bffc 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -996,12 +996,15 @@ dump_blkd_tasks(struct rcu_node *rnp, int ncheck) */ static void rcu_cpu_kthread_setup(unsigned int cpu) { + struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu); #ifdef CONFIG_RCU_BOOST struct sched_param sp; sp.sched_priority = kthread_prio; sched_setscheduler_nocheck(current, SCHED_FIFO, &sp); #endif /* #ifdef CONFIG_RCU_BOOST */ + + WRITE_ONCE(rdp->rcuc_activity, jiffies); } #ifdef CONFIG_RCU_BOOST diff --git a/kernel/rcu/tree_stall.h b/kernel/rcu/tree_stall.h index 21bebf7c9030..0c5d8516516a 100644 --- a/kernel/rcu/tree_stall.h +++ b/kernel/rcu/tree_stall.h @@ -379,6 +379,15 @@ static bool rcu_is_gp_kthread_starving(unsigned long *jp) return j > 2 * HZ; } +static bool rcu_is_rcuc_kthread_starving(struct rcu_data *rdp, unsigned long *jp) +{ + unsigned long j = jiffies - READ_ONCE(rdp->rcuc_activity); + + if (jp) + *jp = j; + return j > 2 * HZ; +} + /* * Print out diagnostic information for the specified stalled CPU. * @@ -430,6 +439,29 @@ static void print_cpu_stall_info(int cpu) falsepositive ? " (false positive?)" : ""); } +static void rcuc_kthread_dump(struct rcu_data *rdp) +{ + int cpu; + unsigned long j; + struct task_struct *rcuc; + + rcuc = rdp->rcu_cpu_kthread_task; + if (!rcuc) + return; + + cpu = task_cpu(rcuc); + if (cpu_is_offline(cpu) || idle_cpu(cpu)) + return; + + if (!rcu_is_rcuc_kthread_starving(rdp, &j)) + return; + + pr_err("%s kthread starved for %ld jiffies\n", rcuc->comm, j); + sched_show_task(rcuc); + if (!trigger_single_cpu_backtrace(cpu)) + dump_cpu_task(cpu); +} + /* Complain about starvation of grace-period kthread. */ static void rcu_check_gp_kthread_starvation(void) { @@ -601,6 +633,9 @@ static void print_cpu_stall(unsigned long gps) rcu_check_gp_kthread_expired_fqs_timer(); rcu_check_gp_kthread_starvation(); + if (!use_softirq) + rcuc_kthread_dump(rdp); + rcu_dump_cpu_stacks(); raw_spin_lock_irqsave_rcu_node(rnp, flags); From 6f81bd6a4e305d15d9c2a6a350e2876a7a814d7e Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 6 Dec 2021 15:12:14 -0800 Subject: [PATCH 037/294] rcutorture: Print message before invoking ->cb_barrier() The various ->cb_barrier() functions, for example, rcu_barrier(), sometimes cause rcutorture hangs. But currently, the last console message is the unenlightening "Stopping rcu_torture_stats". This commit therefore prints a message of the form "rcu_torture_cleanup: Invoking rcu_barrier+0x0/0x1e0()" to help point people in the right direction. Signed-off-by: Paul E. McKenney --- kernel/rcu/rcutorture.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 422f7e4cc08d..00400aef5818 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -2905,8 +2905,10 @@ rcu_torture_cleanup(void) int i; if (torture_cleanup_begin()) { - if (cur_ops->cb_barrier != NULL) + if (cur_ops->cb_barrier != NULL) { + pr_info("%s: Invoking %pS().\n", __func__, cur_ops->cb_barrier); cur_ops->cb_barrier(); + } return; } if (!cur_ops) { @@ -2961,8 +2963,10 @@ rcu_torture_cleanup(void) * Wait for all RCU callbacks to fire, then do torture-type-specific * cleanup operations. */ - if (cur_ops->cb_barrier != NULL) + if (cur_ops->cb_barrier != NULL) { + pr_info("%s: Invoking %pS().\n", __func__, cur_ops->cb_barrier); cur_ops->cb_barrier(); + } if (cur_ops->cleanup != NULL) cur_ops->cleanup(); From 2b4a7f20f160e6440848c62a70ee5dc5237a2c8b Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 16 Dec 2021 12:23:31 -0800 Subject: [PATCH 038/294] torture: Distinguish kthread stopping and being asked to stop Right now, if a given kthread (call it "kthread") realizes that it needs to stop, "Stopping kthread" is written to the console. When the cleanup code decides that it is time to stop that kthread, "Stopping kthread tasks" is written to the console. These two events might happen in either order, especially in the case of time-based torture-test shutdown. But it is hard to distinguish these, especially for those unfamiliar with the torture tests. This commit therefore changes the first case from "Stopping kthread" to "kthread is stopping" to make things more clear. Signed-off-by: Paul E. McKenney --- kernel/torture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/torture.c b/kernel/torture.c index ef27a6c82451..f55d803f995d 100644 --- a/kernel/torture.c +++ b/kernel/torture.c @@ -911,7 +911,7 @@ void torture_kthread_stopping(char *title) { char buf[128]; - snprintf(buf, sizeof(buf), "Stopping %s", title); + snprintf(buf, sizeof(buf), "%s is stopping", title); VERBOSE_TOROUT_STRING(buf); while (!kthread_should_stop()) { torture_shutdown_absorb(title); From 05b724655bf1908abf531dd0bce455e55703a3a8 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 16 Dec 2021 15:36:02 -0800 Subject: [PATCH 039/294] rcutorture: Increase visibility of forward-progress hangs This commit adds a few pr_alert() calls to rcutorture's forward-progress testing in order to better diagnose shutdown-time hangs. Signed-off-by: Paul E. McKenney --- kernel/rcu/rcutorture.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 00400aef5818..fefc3fa1a9c2 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -2281,6 +2281,7 @@ static void rcu_torture_fwd_prog_nr(struct rcu_fwd *rfp, unsigned long stopat; static DEFINE_TORTURE_RANDOM(trs); + pr_alert("%s: Starting forward-progress test %d\n", __func__, rfp->rcu_fwd_id); if (!cur_ops->sync) return; // Cannot do need_resched() forward progress testing without ->sync. if (cur_ops->call && cur_ops->cb_barrier) { @@ -2325,6 +2326,7 @@ static void rcu_torture_fwd_prog_nr(struct rcu_fwd *rfp, if (selfpropcb) { WRITE_ONCE(fcs.stop, 1); cur_ops->sync(); /* Wait for running CB to complete. */ + pr_alert("%s: Waiting for CBs: %pS() %d\n", __func__, cur_ops->cb_barrier, rfp->rcu_fwd_id); cur_ops->cb_barrier(); /* Wait for queued callbacks. */ } @@ -2353,6 +2355,7 @@ static void rcu_torture_fwd_prog_cr(struct rcu_fwd *rfp) unsigned long stopat; unsigned long stoppedat; + pr_alert("%s: Starting forward-progress test %d\n", __func__, rfp->rcu_fwd_id); if (READ_ONCE(rcu_fwd_emergency_stop)) return; /* Get out of the way quickly, no GP wait! */ if (!cur_ops->call) @@ -2414,6 +2417,7 @@ static void rcu_torture_fwd_prog_cr(struct rcu_fwd *rfp) n_launders_cb_snap = READ_ONCE(rfp->n_launders_cb); cver = READ_ONCE(rcu_torture_current_version) - cver; gps = rcutorture_seq_diff(cur_ops->get_gp_seq(), gps); + pr_alert("%s: Waiting for CBs: %pS() %d\n", __func__, cur_ops->cb_barrier, rfp->rcu_fwd_id); cur_ops->cb_barrier(); /* Wait for callbacks to be invoked. */ (void)rcu_torture_fwd_prog_cbfree(rfp); From e22ef8df415d924428e35c9c112526306e684adc Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 17 Dec 2021 12:33:53 -0800 Subject: [PATCH 040/294] rcutorture: Make rcu_fwd_cb_nodelay be a counter Back when only one rcutorture kthread could do forward-progress testing, it was just fine for rcu_fwd_cb_nodelay to be a non-atomic bool. It was set at the start of forward-progress testing and cleared at the end. But now that there are multiple threads, the value can be cleared while one of the threads is still doing forward-progress testing. This commit therefore makes rcu_fwd_cb_nodelay be an atomic counter, replacing the WRITE_ONCE() operations with atomic_inc() and atomic_dec(). Signed-off-by: Paul E. McKenney --- kernel/rcu/rcutorture.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index fefc3fa1a9c2..afe95c694895 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -284,7 +284,7 @@ static atomic_t barrier_cbs_invoked; /* Barrier callbacks invoked. */ static wait_queue_head_t *barrier_cbs_wq; /* Coordinate barrier testing. */ static DECLARE_WAIT_QUEUE_HEAD(barrier_wq); -static bool rcu_fwd_cb_nodelay; /* Short rcu_torture_delay() delays. */ +static atomic_t rcu_fwd_cb_nodelay; /* Short rcu_torture_delay() delays. */ /* * Allocate an element from the rcu_tortures pool. @@ -387,7 +387,7 @@ rcu_read_delay(struct torture_random_state *rrsp, struct rt_read_seg *rtrsp) * period, and we want a long delay occasionally to trigger * force_quiescent_state. */ - if (!READ_ONCE(rcu_fwd_cb_nodelay) && + if (!atomic_read(&rcu_fwd_cb_nodelay) && !(torture_random(rrsp) % (nrealreaders * 2000 * longdelay_ms))) { started = cur_ops->get_gp_seq(); ts = rcu_trace_clock_local(); @@ -1276,7 +1276,7 @@ rcu_torture_writer(void *arg) boot_ended = rcu_inkernel_boot_has_ended(); stutter_waited = stutter_wait("rcu_torture_writer"); if (stutter_waited && - !READ_ONCE(rcu_fwd_cb_nodelay) && + !atomic_read(&rcu_fwd_cb_nodelay) && !cur_ops->slow_gps && !torture_must_stop() && boot_ended) @@ -2290,7 +2290,7 @@ static void rcu_torture_fwd_prog_nr(struct rcu_fwd *rfp, } /* Tight loop containing cond_resched(). */ - WRITE_ONCE(rcu_fwd_cb_nodelay, true); + atomic_inc(&rcu_fwd_cb_nodelay); cur_ops->sync(); /* Later readers see above write. */ if (selfpropcb) { WRITE_ONCE(fcs.stop, 0); @@ -2335,7 +2335,7 @@ static void rcu_torture_fwd_prog_nr(struct rcu_fwd *rfp, destroy_rcu_head_on_stack(&fcs.rh); } schedule_timeout_uninterruptible(HZ / 10); /* Let kthreads recover. */ - WRITE_ONCE(rcu_fwd_cb_nodelay, false); + atomic_dec(&rcu_fwd_cb_nodelay); } /* Carry out call_rcu() forward-progress testing. */ @@ -2362,7 +2362,7 @@ static void rcu_torture_fwd_prog_cr(struct rcu_fwd *rfp) return; /* Can't do call_rcu() fwd prog without ->call. */ /* Loop continuously posting RCU callbacks. */ - WRITE_ONCE(rcu_fwd_cb_nodelay, true); + atomic_inc(&rcu_fwd_cb_nodelay); cur_ops->sync(); /* Later readers see above write. */ WRITE_ONCE(rfp->rcu_fwd_startat, jiffies); stopat = rfp->rcu_fwd_startat + MAX_FWD_CB_JIFFIES; @@ -2435,7 +2435,7 @@ static void rcu_torture_fwd_prog_cr(struct rcu_fwd *rfp) } schedule_timeout_uninterruptible(HZ); /* Let CBs drain. */ tick_dep_clear_task(current, TICK_DEP_BIT_RCU); - WRITE_ONCE(rcu_fwd_cb_nodelay, false); + atomic_dec(&rcu_fwd_cb_nodelay); } From 02b51a1cf47977d8772c7dcc363ef6a1e6e59f21 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 17 Dec 2021 15:05:05 -0800 Subject: [PATCH 041/294] rcutorture: Add end-of-test check to rcu_torture_fwd_prog() loop The second and subsequent forward-progress kthreads loop waiting for the first forward-progress kthread to start the next test interval. Unfortunately, if the test ends while one of those kthreads is waiting, the test will hang. This hang occurs because that wait loop fails to check for the end of the test. This commit therefore adds an end-of-test check to that wait loop. Signed-off-by: Paul E. McKenney --- kernel/rcu/rcutorture.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index afe95c694895..e99658efd97f 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -2515,7 +2515,7 @@ static int rcu_torture_fwd_prog(void *args) firsttime = false; WRITE_ONCE(rcu_fwd_seq, rcu_fwd_seq + 1); } else { - while (READ_ONCE(rcu_fwd_seq) == oldseq) + while (READ_ONCE(rcu_fwd_seq) == oldseq && !torture_must_stop()) schedule_timeout_interruptible(1); oldseq = READ_ONCE(rcu_fwd_seq); } From 89440d2dad0cc2a781290470cb90402ebba481fc Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 28 Dec 2021 15:59:38 -0800 Subject: [PATCH 042/294] rcutorture: Fix rcu_fwd_mutex deadlock The rcu_torture_fwd_cb_hist() function acquires rcu_fwd_mutex, but is invoked from rcutorture_oom_notify() function, which hold this same mutex across this call. This commit fixes the resulting deadlock. Reported-by: kernel test robot Tested-by: Oliver Sang Signed-off-by: Paul E. McKenney --- kernel/rcu/rcutorture.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index e99658efd97f..1c8f40b90f70 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -2180,7 +2180,6 @@ static void rcu_torture_fwd_cb_hist(struct rcu_fwd *rfp) for (i = ARRAY_SIZE(rfp->n_launders_hist) - 1; i > 0; i--) if (rfp->n_launders_hist[i].n_launders > 0) break; - mutex_lock(&rcu_fwd_mutex); // Serialize histograms. pr_alert("%s: Callback-invocation histogram %d (duration %lu jiffies):", __func__, rfp->rcu_fwd_id, jiffies - rfp->rcu_fwd_startat); gps_old = rfp->rcu_launder_gp_seq_start; @@ -2193,7 +2192,6 @@ static void rcu_torture_fwd_cb_hist(struct rcu_fwd *rfp) gps_old = gps; } pr_cont("\n"); - mutex_unlock(&rcu_fwd_mutex); } /* Callback function for continuous-flood RCU callbacks. */ @@ -2431,7 +2429,9 @@ static void rcu_torture_fwd_prog_cr(struct rcu_fwd *rfp) n_launders, n_launders_sa, n_max_gps, n_max_cbs, cver, gps); atomic_long_add(n_max_cbs, &rcu_fwd_max_cbs); + mutex_lock(&rcu_fwd_mutex); // Serialize histograms. rcu_torture_fwd_cb_hist(rfp); + mutex_unlock(&rcu_fwd_mutex); } schedule_timeout_uninterruptible(HZ); /* Let CBs drain. */ tick_dep_clear_task(current, TICK_DEP_BIT_RCU); From 6b8646a9d37c6324cf994dbefb75f3eb20b109ab Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 3 Jan 2022 06:07:09 -0800 Subject: [PATCH 043/294] torture: Wake up kthreads after storing task_struct pointer Currently, _torture_create_kthread() uses kthread_run() to create torture-test kthreads, which means that the resulting task_struct pointer is stored after the newly created kthread has been marked runnable. This in turn can cause spurious failure of checks for code being run by a particular kthread. This commit therefore changes _torture_create_kthread() to use kthread_create(), then to do an explicit wake_up_process() after the task_struct pointer has been stored. Reported-by: Frederic Weisbecker Reviewed-by: Neeraj Upadhyay Reviewed-by: Uladzislau Rezki (Sony) Reviewed-by: Frederic Weisbecker Signed-off-by: Paul E. McKenney --- kernel/torture.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/torture.c b/kernel/torture.c index f55d803f995d..789aeb0e1159 100644 --- a/kernel/torture.c +++ b/kernel/torture.c @@ -931,12 +931,14 @@ int _torture_create_kthread(int (*fn)(void *arg), void *arg, char *s, char *m, int ret = 0; VERBOSE_TOROUT_STRING(m); - *tp = kthread_run(fn, arg, "%s", s); + *tp = kthread_create(fn, arg, "%s", s); if (IS_ERR(*tp)) { ret = PTR_ERR(*tp); TOROUT_ERRSTRING(f); *tp = NULL; + return ret; } + wake_up_process(*tp); // Process is sleeping, so ordering provided. torture_shuffle_task_register(*tp); return ret; } From 9c0f1c7fd7c6e1e0f5b84e20c577fbab62563d03 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 27 Jan 2022 20:29:10 -0800 Subject: [PATCH 044/294] rcutorture: Enable limited callback-flooding tests of SRCU This commit allows up to 50,000 callbacks worth of callback-flooding tests of SRCU. The goal of this change is to exercise Tree SRCU's ability to transition from SRCU_SIZE_SMALL to SRCU_SIZE_BIG triggered by callback-queue-time lock contention. Signed-off-by: Paul E. McKenney --- kernel/rcu/rcutorture.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/kernel/rcu/rcutorture.c b/kernel/rcu/rcutorture.c index 1c8f40b90f70..b41db719085e 100644 --- a/kernel/rcu/rcutorture.c +++ b/kernel/rcu/rcutorture.c @@ -674,6 +674,7 @@ static struct rcu_torture_ops srcu_ops = { .call = srcu_torture_call, .cb_barrier = srcu_torture_barrier, .stats = srcu_torture_stats, + .cbflood_max = 50000, .irq_capable = 1, .no_pi_lock = IS_ENABLED(CONFIG_TINY_SRCU), .name = "srcu" @@ -708,6 +709,7 @@ static struct rcu_torture_ops srcud_ops = { .call = srcu_torture_call, .cb_barrier = srcu_torture_barrier, .stats = srcu_torture_stats, + .cbflood_max = 50000, .irq_capable = 1, .no_pi_lock = IS_ENABLED(CONFIG_TINY_SRCU), .name = "srcud" From bee6f2169935658fc405128a8b763ea49f50985c Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 1 Dec 2021 19:19:13 -0800 Subject: [PATCH 045/294] torture: Drop trailing ^M from console output Console logs can sometimes have trailing control-M characters, which the forward-progress evaluation code in kvm-recheck-rcu.sh passes through to the user output. Which does not cause a technical problem, but which can look ugly. This commit therefore strips the control-M characters. Signed-off-by: Paul E. McKenney --- tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh index 1c4c2c727dad..43e1387234d1 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-recheck-rcu.sh @@ -25,7 +25,7 @@ stopstate="`grep 'End-test grace-period state: g' $i/console.log 2> /dev/null | tail -1 | sed -e 's/^\[[ 0-9.]*] //' | awk '{ print \"[\" $1 \" \" $5 \" \" $6 \" \" $7 \"]\"; }' | tr -d '\012\015'`" -fwdprog="`grep 'rcu_torture_fwd_prog n_max_cbs: ' $i/console.log 2> /dev/null | sed -e 's/^\[[^]]*] //' | sort -k3nr | head -1 | awk '{ print $2 " " $3 }'`" +fwdprog="`grep 'rcu_torture_fwd_prog n_max_cbs: ' $i/console.log 2> /dev/null | sed -e 's/^\[[^]]*] //' | sort -k3nr | head -1 | awk '{ print $2 " " $3 }' | tr -d '\015'`" if test -z "$ngps" then echo "$configfile ------- " $stopstate From 010e5773b2050db260395a240e2f7adee3108603 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 2 Dec 2021 11:24:05 -0800 Subject: [PATCH 046/294] torture: Allow four-digit repetition numbers for --configs parameter In a clear-cut case of "not thinking big enough", kvm.sh limits the multipliers for torture-test scenarios to three digits. Although this is large enough for any single system that I have ever run rcutorture on, it does become a problem when you want to use kvm-remote.sh to run as many instances of TREE09 as fit on a set of 20 systems with 80 CPUs each. Yes, one could simply say "--configs '800*TREE09 800*TREE09'", but this commit removes the need for that sort of hacky workaround by permitting four-digit repetition numbers, thus allowing "--configs '1600*TREE09'". Five-digit repetition numbers remain off the menu. Should they ever really be needed, they can easily be added! Signed-off-by: Paul E. McKenney --- tools/testing/selftests/rcutorture/bin/kvm.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/rcutorture/bin/kvm.sh b/tools/testing/selftests/rcutorture/bin/kvm.sh index 6de0c183db5b..348ad177a5ac 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm.sh @@ -280,7 +280,7 @@ configs_derep= for CF in $configs do case $CF in - [0-9]\**|[0-9][0-9]\**|[0-9][0-9][0-9]\**) + [0-9]\**|[0-9][0-9]\**|[0-9][0-9][0-9]\**|[0-9][0-9][0-9][0-9]\**) config_reps=`echo $CF | sed -e 's/\*.*$//'` CF1=`echo $CF | sed -e 's/^[^*]*\*//'` ;; From 21fbc62576b67d6db66cae88710f82bd51a556e1 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sat, 4 Dec 2021 13:53:24 -0800 Subject: [PATCH 047/294] torture: Output per-failed-run summary lines from torture.sh Currently, torture.sh lists the failed runs, but it is up to the user to work out what failed. This is especially annoying for KCSAN runs, where RCU's tighter definitions result in failures being reported for other parts of the kernel. This commit therefore outputs "Summary:" lines for each failed run, allowing the user to more quickly identify which failed runs need focused attention. Signed-off-by: Paul E. McKenney --- tools/testing/selftests/rcutorture/bin/torture.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/rcutorture/bin/torture.sh b/tools/testing/selftests/rcutorture/bin/torture.sh index eae88aacca2a..894f589dd562 100755 --- a/tools/testing/selftests/rcutorture/bin/torture.sh +++ b/tools/testing/selftests/rcutorture/bin/torture.sh @@ -414,7 +414,7 @@ nfailures=0 echo FAILURES: | tee -a $T/log if test -s "$T/failures" then - cat "$T/failures" | tee -a $T/log + awk < "$T/failures" -v sq="'" '{ print "echo " sq $0 sq; print "grep Summary: " $2 "/log | sed -e " sq "s/^[^S]*/ /" sq; }' | sh | tee -a $T/log nfailures="`wc -l "$T/failures" | awk '{ print $1 }'`" ret=2 fi From a711aaccf6b389b7fa7f3e35b4f99c437a36b9b2 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sat, 4 Dec 2021 21:00:55 -0800 Subject: [PATCH 048/294] torture: Make kvm.sh summaries note runs having only KCSAN reports Runs having only KCSAN reports will normally print a summary line containing only a "Bugs:" entry. However, these bugs might or might not be KCSAN reports. This commit therefore flags runs in which all the "Bugs:" entries are KCSAN reports. Signed-off-by: Paul E. McKenney --- .../selftests/rcutorture/bin/console-badness.sh | 2 +- .../testing/selftests/rcutorture/bin/parse-console.sh | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/rcutorture/bin/console-badness.sh b/tools/testing/selftests/rcutorture/bin/console-badness.sh index e6a132df6172..69f8a5958cef 100755 --- a/tools/testing/selftests/rcutorture/bin/console-badness.sh +++ b/tools/testing/selftests/rcutorture/bin/console-badness.sh @@ -10,7 +10,7 @@ # # Authors: Paul E. McKenney -egrep 'Badness|WARNING:|Warn|BUG|===========|Call Trace:|Oops:|detected stalls on CPUs/tasks:|self-detected stall on CPU|Stall ended before state dump start|\?\?\? Writer stall state|rcu_.*kthread starved for|!!!' | +egrep 'Badness|WARNING:|Warn|BUG|===========|BUG: KCSAN:|Call Trace:|Oops:|detected stalls on CPUs/tasks:|self-detected stall on CPU|Stall ended before state dump start|\?\?\? Writer stall state|rcu_.*kthread starved for|!!!' | grep -v 'ODEBUG: ' | grep -v 'This means that this is a DEBUG kernel and it is' | grep -v 'Warning: unable to open an initial console' | diff --git a/tools/testing/selftests/rcutorture/bin/parse-console.sh b/tools/testing/selftests/rcutorture/bin/parse-console.sh index 9f624bd53c27..822eb037a057 100755 --- a/tools/testing/selftests/rcutorture/bin/parse-console.sh +++ b/tools/testing/selftests/rcutorture/bin/parse-console.sh @@ -138,6 +138,16 @@ then then summary="$summary Bugs: $n_bugs" fi + n_kcsan=`egrep -c 'BUG: KCSAN: ' $file` + if test "$n_kcsan" -ne 0 + then + if test "$n_bugs" = "$n_kcsan" + then + summary="$summary (all bugs kcsan)" + else + summary="$summary KCSAN: $n_kcsan" + fi + fi n_calltrace=`grep -c 'Call Trace:' $file` if test "$n_calltrace" -ne 0 then From 9a32ed1cf6cdebff7ccd82b91f8021267ed0d9cb Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 6 Dec 2021 09:13:37 -0800 Subject: [PATCH 049/294] torture: Indicate which torture.sh runs' bugs are all KCSAN reports This commit further improves torture.sh run summaries by indicating which runs' "Bugs:" counts are all KCSAN reports, and further printing an additional end-of-run summary line when all errors reported in all runs were KCSAN reports. Signed-off-by: Paul E. McKenney --- tools/testing/selftests/rcutorture/bin/torture.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tools/testing/selftests/rcutorture/bin/torture.sh b/tools/testing/selftests/rcutorture/bin/torture.sh index 894f589dd562..bddce72ea5ce 100755 --- a/tools/testing/selftests/rcutorture/bin/torture.sh +++ b/tools/testing/selftests/rcutorture/bin/torture.sh @@ -414,8 +414,14 @@ nfailures=0 echo FAILURES: | tee -a $T/log if test -s "$T/failures" then - awk < "$T/failures" -v sq="'" '{ print "echo " sq $0 sq; print "grep Summary: " $2 "/log | sed -e " sq "s/^[^S]*/ /" sq; }' | sh | tee -a $T/log + awk < "$T/failures" -v sq="'" '{ print "echo " sq $0 sq; print "grep Summary: " $2 "/log | sed -e " sq "s/^[^S]*/ /" sq; }' | sh | tee -a $T/log | tee "$T/failuresum" nfailures="`wc -l "$T/failures" | awk '{ print $1 }'`" + grep "^ Summary: " "$T/failuresum" | + grep -v '^ Summary: Bugs: [0-9]* (all bugs kcsan)$' > "$T/nonkcsan" + if test -s "$T/nonkcsan" + then + nonkcsanbug="yes" + fi ret=2 fi if test "$do_kcsan" = "yes" @@ -424,6 +430,10 @@ then fi echo Started at $startdate, ended at `date`, duration `get_starttime_duration $starttime`. | tee -a $T/log echo Summary: Successes: $nsuccesses Failures: $nfailures. | tee -a $T/log +if test -z "$nonkcsanbug" && test -s "$T/failuresum" +then + echo " All bugs were KCSAN failures." +fi tdir="`cat $T/successes $T/failures | head -1 | awk '{ print $NF }' | sed -e 's,/[^/]\+/*$,,'`" if test -n "$tdir" && test $compress_kasan_vmlinux -gt 0 then From 99c80a96a512f32a234687343ff1c8e5e033976b Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 17 Dec 2021 16:14:31 -0800 Subject: [PATCH 050/294] torture: Compress KCSAN as well as KASAN vmlinux files Compressing KASAN vmlinux files reduces torture.sh res file size from about 100G to about 50G, which is good, but the KCSAN vmlinux files are also large. Compressing them reduces their size from about 700M to about 100M (but of course your mileage may vary). This commit therefore compresses both KASAN and KCSAN vmlinux files. Signed-off-by: Paul E. McKenney --- .../selftests/rcutorture/bin/torture.sh | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/tools/testing/selftests/rcutorture/bin/torture.sh b/tools/testing/selftests/rcutorture/bin/torture.sh index bddce72ea5ce..a5f1c5fbefe4 100755 --- a/tools/testing/selftests/rcutorture/bin/torture.sh +++ b/tools/testing/selftests/rcutorture/bin/torture.sh @@ -37,7 +37,7 @@ configs_scftorture= kcsan_kmake_args= # Default compression, duration, and apportionment. -compress_kasan_vmlinux="`identify_qemu_vcpus`" +compress_concurrency="`identify_qemu_vcpus`" duration_base=10 duration_rcutorture_frac=7 duration_locktorture_frac=1 @@ -67,7 +67,7 @@ function doyesno () { usage () { echo "Usage: $scriptname optional arguments:" - echo " --compress-kasan-vmlinux concurrency" + echo " --compress-concurrency concurrency" echo " --configs-rcutorture \"config-file list w/ repeat factor (3*TINY01)\"" echo " --configs-locktorture \"config-file list w/ repeat factor (10*LOCK01)\"" echo " --configs-scftorture \"config-file list w/ repeat factor (2*CFLIST)\"" @@ -91,9 +91,9 @@ usage () { while test $# -gt 0 do case "$1" in - --compress-kasan-vmlinux) - checkarg --compress-kasan-vmlinux "(concurrency level)" $# "$2" '^[0-9][0-9]*$' '^error' - compress_kasan_vmlinux=$2 + --compress-concurrency) + checkarg --compress-concurrency "(concurrency level)" $# "$2" '^[0-9][0-9]*$' '^error' + compress_concurrency=$2 shift ;; --config-rcutorture|--configs-rcutorture) @@ -435,11 +435,11 @@ then echo " All bugs were KCSAN failures." fi tdir="`cat $T/successes $T/failures | head -1 | awk '{ print $NF }' | sed -e 's,/[^/]\+/*$,,'`" -if test -n "$tdir" && test $compress_kasan_vmlinux -gt 0 +if test -n "$tdir" && test $compress_concurrency -gt 0 then # KASAN vmlinux files can approach 1GB in size, so compress them. - echo Looking for KASAN files to compress: `date` > "$tdir/log-xz" 2>&1 - find "$tdir" -type d -name '*-kasan' -print > $T/xz-todo + echo Looking for K[AC]SAN files to compress: `date` > "$tdir/log-xz" 2>&1 + find "$tdir" -type d -name '*-k[ac]san' -print > $T/xz-todo ncompresses=0 batchno=1 if test -s $T/xz-todo @@ -457,7 +457,7 @@ then do xz "$j" >> "$tdir/log-xz" 2>&1 & ncompresses=$((ncompresses+1)) - if test $ncompresses -ge $compress_kasan_vmlinux + if test $ncompresses -ge $compress_concurrency then echo Waiting for batch $batchno of $ncompresses compressions `date` | tee -a "$tdir/log-xz" | tee -a $T/log wait From 2bc9062e7f394b3c6cbe408a13523c0a4bd13d2a Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 20 Dec 2021 20:24:25 -0800 Subject: [PATCH 051/294] torture: Make kvm-remote.sh try multiple times to download tarball This commit ups the retries for downloading the build-product tarball to a given remote system from once to five times, the better to handle transient network failures. Signed-off-by: Paul E. McKenney --- .../selftests/rcutorture/bin/kvm-remote.sh | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/tools/testing/selftests/rcutorture/bin/kvm-remote.sh b/tools/testing/selftests/rcutorture/bin/kvm-remote.sh index e09b1bc78708..29b068a55b46 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-remote.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-remote.sh @@ -155,18 +155,23 @@ do echo Downloading tarball to $i `date` | tee -a "$oldrun/remote-log" cat $T/binres.tgz | ssh $i "cd /tmp; tar -xzf -" ret=$? - if test "$ret" -ne 0 - then - echo Unable to download $T/binres.tgz to system $i, waiting and then retrying. | tee -a "$oldrun/remote-log" + tries=0 + while test "$ret" -ne 0 + do + echo Unable to download $T/binres.tgz to system $i, waiting and then retrying. $tries prior retries. | tee -a "$oldrun/remote-log" sleep 60 cat $T/binres.tgz | ssh $i "cd /tmp; tar -xzf -" ret=$? if test "$ret" -ne 0 then - echo Unable to download $T/binres.tgz to system $i, giving up. | tee -a "$oldrun/remote-log" - exit 10 + if test "$tries" > 5 + then + echo Unable to download $T/binres.tgz to system $i, giving up. | tee -a "$oldrun/remote-log" + exit 10 + fi fi - fi + tries=$((tries+1)) + done done # Function to check for presence of a file on the specified system. From b376005eb3a8aedcde437d3d495418d63ce5451c Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 27 Dec 2021 21:21:35 -0800 Subject: [PATCH 052/294] torture: Print only one summary line per run The torture.sh scripts currently duplicates the summary lines, getting one during the run phase and one during the summary phase of each run. This commit therefore removes the run phase from consideration so as to get only one summary line per run. Signed-off-by: Paul E. McKenney --- tools/testing/selftests/rcutorture/bin/torture.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/testing/selftests/rcutorture/bin/torture.sh b/tools/testing/selftests/rcutorture/bin/torture.sh index a5f1c5fbefe4..d1cb60085d8f 100755 --- a/tools/testing/selftests/rcutorture/bin/torture.sh +++ b/tools/testing/selftests/rcutorture/bin/torture.sh @@ -414,7 +414,7 @@ nfailures=0 echo FAILURES: | tee -a $T/log if test -s "$T/failures" then - awk < "$T/failures" -v sq="'" '{ print "echo " sq $0 sq; print "grep Summary: " $2 "/log | sed -e " sq "s/^[^S]*/ /" sq; }' | sh | tee -a $T/log | tee "$T/failuresum" + awk < "$T/failures" -v sq="'" '{ print "echo " sq $0 sq; print "sed -e " sq "1,/^ --- .* Test summary:$/d" sq " " $2 "/log | grep Summary: | sed -e " sq "s/^[^S]*/ /" sq; }' | sh | tee -a $T/log | tee "$T/failuresum" nfailures="`wc -l "$T/failures" | awk '{ print $1 }'`" grep "^ Summary: " "$T/failuresum" | grep -v '^ Summary: Bugs: [0-9]* (all bugs kcsan)$' > "$T/nonkcsan" From e31ccc1ddd2ad3e14f02a53cfa22e6b2b98c6dab Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 18 Jan 2022 15:40:49 -0800 Subject: [PATCH 053/294] torture: Make kvm-find-errors.sh notice missing vmlinux file Currently, an obtuse compiler diagnostic can fool kvm-find-errors.sh into believing that the build was successful. This commit therefore adds a check for a missing vmlinux file. Note that in the case of repeated torture-test scenarios ("--configs '2*TREE01'"), the vmlinux file will only be present in the first directory, that is, in TREE01 but not TREE01.2. Link: https://lore.kernel.org/lkml/36bd91e4-8eda-5677-7fde-40295932a640@molgen.mpg.de/ Reported-by: Paul Menzel Signed-off-by: Paul E. McKenney --- tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh b/tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh index 2e9e9e2eedb6..5f682fc892dd 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-find-errors.sh @@ -30,10 +30,16 @@ editor=${EDITOR-vi} files= for i in ${rundir}/*/Make.out do + scenariodir="`dirname $i`" + scenariobasedir="`echo ${scenariodir} | sed -e 's/\.[0-9]*$//'`" if egrep -q "error:|warning:|^ld: .*undefined reference to" < $i then egrep "error:|warning:|^ld: .*undefined reference to" < $i > $i.diags files="$files $i.diags $i" + elif ! test -f ${scenariobasedir}/vmlinux + then + echo No ${scenariobasedir}/vmlinux file > $i.diags + files="$files $i.diags $i" fi done if test -n "$files" From a7d89cfb8e1269cb6d22453adba56b8d0218589f Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 25 Jan 2022 21:08:55 -0800 Subject: [PATCH 054/294] torture: Change KVM environment variable to RCUTORTURE The torture-test scripting's long-standing use of KVM as the environment variable tracking the pathname of the rcutorture directory now conflicts with allmodconfig builds due to the virt/kvm/Makefile.kvm file's use of this as a makefile variable. This commit therefore changes the torture-test scripting from KVM to RCUTORTURE, avoiding the name conflict. Reported-by: Zhouyi Zhou Tested-by: Zhouyi Zhou Signed-off-by: Paul E. McKenney --- .../testing/selftests/rcutorture/bin/kvm-again.sh | 4 ++-- .../selftests/rcutorture/bin/kvm-check-branches.sh | 4 ++-- .../selftests/rcutorture/bin/kvm-end-run-stats.sh | 4 ++-- .../testing/selftests/rcutorture/bin/kvm-remote.sh | 8 ++++---- tools/testing/selftests/rcutorture/bin/kvm.sh | 14 +++++++------- tools/testing/selftests/rcutorture/bin/torture.sh | 4 ++-- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/tools/testing/selftests/rcutorture/bin/kvm-again.sh b/tools/testing/selftests/rcutorture/bin/kvm-again.sh index 5a0023d183da..0941f1ddab65 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-again.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-again.sh @@ -47,8 +47,8 @@ else exit 1 fi -KVM="`pwd`/tools/testing/selftests/rcutorture"; export KVM -PATH=${KVM}/bin:$PATH; export PATH +RCUTORTURE="`pwd`/tools/testing/selftests/rcutorture"; export RCUTORTURE +PATH=${RCUTORTURE}/bin:$PATH; export PATH . functions.sh dryrun= diff --git a/tools/testing/selftests/rcutorture/bin/kvm-check-branches.sh b/tools/testing/selftests/rcutorture/bin/kvm-check-branches.sh index 370406bbfeed..f17000a2ccf1 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-check-branches.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-check-branches.sh @@ -49,8 +49,8 @@ fi mkdir $resdir/$ds echo Results directory: $resdir/$ds -KVM="`pwd`/tools/testing/selftests/rcutorture"; export KVM -PATH=${KVM}/bin:$PATH; export PATH +RCUTORTURE="`pwd`/tools/testing/selftests/rcutorture"; export RCUTORTURE +PATH=${RCUTORTURE}/bin:$PATH; export PATH . functions.sh echo Using all `identify_qemu_vcpus` CPUs. diff --git a/tools/testing/selftests/rcutorture/bin/kvm-end-run-stats.sh b/tools/testing/selftests/rcutorture/bin/kvm-end-run-stats.sh index e4a00779b8c6..ee886b40a5d2 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-end-run-stats.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-end-run-stats.sh @@ -22,8 +22,8 @@ T=${TMPDIR-/tmp}/kvm-end-run-stats.sh.$$ trap 'rm -rf $T' 0 mkdir $T -KVM="`pwd`/tools/testing/selftests/rcutorture"; export KVM -PATH=${KVM}/bin:$PATH; export PATH +RCUTORTURE="`pwd`/tools/testing/selftests/rcutorture"; export RCUTORTURE +PATH=${RCUTORTURE}/bin:$PATH; export PATH . functions.sh default_starttime="`get_starttime`" starttime="${2-default_starttime}" diff --git a/tools/testing/selftests/rcutorture/bin/kvm-remote.sh b/tools/testing/selftests/rcutorture/bin/kvm-remote.sh index 29b068a55b46..8c4c1e4792d0 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm-remote.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm-remote.sh @@ -19,8 +19,8 @@ then exit 1 fi -KVM="`pwd`/tools/testing/selftests/rcutorture"; export KVM -PATH=${KVM}/bin:$PATH; export PATH +RCUTORTURE="`pwd`/tools/testing/selftests/rcutorture"; export RCUTORTURE +PATH=${RCUTORTURE}/bin:$PATH; export PATH . functions.sh starttime="`get_starttime`" @@ -108,8 +108,8 @@ else cat $T/kvm-again.sh.out | tee -a "$oldrun/remote-log" exit 2 fi - cp -a "$rundir" "$KVM/res/" - oldrun="$KVM/res/$ds" + cp -a "$rundir" "$RCUTORTURE/res/" + oldrun="$RCUTORTURE/res/$ds" fi echo | tee -a "$oldrun/remote-log" echo " ----" kvm-again.sh output: "(`date`)" | tee -a "$oldrun/remote-log" diff --git a/tools/testing/selftests/rcutorture/bin/kvm.sh b/tools/testing/selftests/rcutorture/bin/kvm.sh index 348ad177a5ac..55b2c1533282 100755 --- a/tools/testing/selftests/rcutorture/bin/kvm.sh +++ b/tools/testing/selftests/rcutorture/bin/kvm.sh @@ -25,15 +25,15 @@ LANG=en_US.UTF-8; export LANG dur=$((30*60)) dryrun="" -KVM="`pwd`/tools/testing/selftests/rcutorture"; export KVM -PATH=${KVM}/bin:$PATH; export PATH +RCUTORTURE="`pwd`/tools/testing/selftests/rcutorture"; export RCUTORTURE +PATH=${RCUTORTURE}/bin:$PATH; export PATH . functions.sh TORTURE_ALLOTED_CPUS="`identify_qemu_vcpus`" TORTURE_DEFCONFIG=defconfig TORTURE_BOOT_IMAGE="" TORTURE_BUILDONLY= -TORTURE_INITRD="$KVM/initrd"; export TORTURE_INITRD +TORTURE_INITRD="$RCUTORTURE/initrd"; export TORTURE_INITRD TORTURE_KCONFIG_ARG="" TORTURE_KCONFIG_GDB_ARG="" TORTURE_BOOT_GDB_ARG="" @@ -262,7 +262,7 @@ else exit 1 fi -CONFIGFRAG=${KVM}/configs/${TORTURE_SUITE}; export CONFIGFRAG +CONFIGFRAG=${RCUTORTURE}/configs/${TORTURE_SUITE}; export CONFIGFRAG defaultconfigs="`tr '\012' ' ' < $CONFIGFRAG/CFLIST`" if test -z "$configs" @@ -272,7 +272,7 @@ fi if test -z "$resdir" then - resdir=$KVM/res + resdir=$RCUTORTURE/res fi # Create a file of test-name/#cpus pairs, sorted by decreasing #cpus. @@ -386,7 +386,7 @@ END { # Generate a script to execute the tests in appropriate batches. cat << ___EOF___ > $T/script CONFIGFRAG="$CONFIGFRAG"; export CONFIGFRAG -KVM="$KVM"; export KVM +RCUTORTURE="$RCUTORTURE"; export RCUTORTURE PATH="$PATH"; export PATH TORTURE_ALLOTED_CPUS="$TORTURE_ALLOTED_CPUS"; export TORTURE_ALLOTED_CPUS TORTURE_BOOT_IMAGE="$TORTURE_BOOT_IMAGE"; export TORTURE_BOOT_IMAGE @@ -569,7 +569,7 @@ ___EOF___ awk < $T/cfgcpu.pack \ -v TORTURE_BUILDONLY="$TORTURE_BUILDONLY" \ -v CONFIGDIR="$CONFIGFRAG/" \ - -v KVM="$KVM" \ + -v RCUTORTURE="$RCUTORTURE" \ -v ncpus=$cpus \ -v jitter="$jitter" \ -v rd=$resdir/$ds/ \ diff --git a/tools/testing/selftests/rcutorture/bin/torture.sh b/tools/testing/selftests/rcutorture/bin/torture.sh index d1cb60085d8f..e00e60efb231 100755 --- a/tools/testing/selftests/rcutorture/bin/torture.sh +++ b/tools/testing/selftests/rcutorture/bin/torture.sh @@ -13,8 +13,8 @@ scriptname=$0 args="$*" -KVM="`pwd`/tools/testing/selftests/rcutorture"; export KVM -PATH=${KVM}/bin:$PATH; export PATH +RCUTORTURE="`pwd`/tools/testing/selftests/rcutorture"; export RCUTORTURE +PATH=${RCUTORTURE}/bin:$PATH; export PATH . functions.sh TORTURE_ALLOTED_CPUS="`identify_qemu_vcpus`" From e2b665f612ca2ddc61c3d54817a3a780aee6b251 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 1 Feb 2022 14:00:08 -0500 Subject: [PATCH 055/294] tools/memory-model: Explain syntactic and semantic dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Paul Heidekrüger pointed out that the Linux Kernel Memory Model documentation doesn't mention the distinction between syntactic and semantic dependencies. This is an important difference, because the compiler can easily break dependencies that are only syntactic, not semantic. This patch adds a few paragraphs to the LKMM documentation explaining these issues and illustrating how they can matter. Suggested-by: Paul Heidekrüger Reviewed-by: Akira Yokosawa Signed-off-by: Alan Stern Signed-off-by: Paul E. McKenney --- .../Documentation/explanation.txt | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/tools/memory-model/Documentation/explanation.txt b/tools/memory-model/Documentation/explanation.txt index 394ee57d58f2..ee819a402b69 100644 --- a/tools/memory-model/Documentation/explanation.txt +++ b/tools/memory-model/Documentation/explanation.txt @@ -485,6 +485,57 @@ have R ->po X. It wouldn't make sense for a computation to depend somehow on a value that doesn't get loaded from shared memory until later in the code! +Here's a trick question: When is a dependency not a dependency? Answer: +When it is purely syntactic rather than semantic. We say a dependency +between two accesses is purely syntactic if the second access doesn't +actually depend on the result of the first. Here is a trivial example: + + r1 = READ_ONCE(x); + WRITE_ONCE(y, r1 * 0); + +There appears to be a data dependency from the load of x to the store +of y, since the value to be stored is computed from the value that was +loaded. But in fact, the value stored does not really depend on +anything since it will always be 0. Thus the data dependency is only +syntactic (it appears to exist in the code) but not semantic (the +second access will always be the same, regardless of the value of the +first access). Given code like this, a compiler could simply discard +the value returned by the load from x, which would certainly destroy +any dependency. (The compiler is not permitted to eliminate entirely +the load generated for a READ_ONCE() -- that's one of the nice +properties of READ_ONCE() -- but it is allowed to ignore the load's +value.) + +It's natural to object that no one in their right mind would write +code like the above. However, macro expansions can easily give rise +to this sort of thing, in ways that often are not apparent to the +programmer. + +Another mechanism that can lead to purely syntactic dependencies is +related to the notion of "undefined behavior". Certain program +behaviors are called "undefined" in the C language specification, +which means that when they occur there are no guarantees at all about +the outcome. Consider the following example: + + int a[1]; + int i; + + r1 = READ_ONCE(i); + r2 = READ_ONCE(a[r1]); + +Access beyond the end or before the beginning of an array is one kind +of undefined behavior. Therefore the compiler doesn't have to worry +about what will happen if r1 is nonzero, and it can assume that r1 +will always be zero regardless of the value actually loaded from i. +(If the assumption turns out to be wrong the resulting behavior will +be undefined anyway, so the compiler doesn't care!) Thus the value +from the load can be discarded, breaking the address dependency. + +The LKMM is unaware that purely syntactic dependencies are different +from semantic dependencies and therefore mistakenly predicts that the +accesses in the two examples above will be ordered. This is another +example of how the compiler can undermine the memory model. Be warned. + THE READS-FROM RELATION: rf, rfi, and rfe ----------------------------------------- From 4a960e8941bd59fe20f8f774de371f40f222a0c7 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Thu, 27 Jan 2022 11:34:48 -0800 Subject: [PATCH 056/294] x86/Documentation: Describe the Intel Hardware Feedback Interface Start a documentation file to describe the purpose and operation of Intel's Hardware Feedback Interface. Describe how this interface is used in Linux to relay performance and energy efficiency updates to userspace. Reviewed-by: Len Brown Suggested-by: Srinivas Pandruvada Signed-off-by: Ricardo Neri Signed-off-by: Rafael J. Wysocki --- Documentation/x86/index.rst | 1 + Documentation/x86/intel-hfi.rst | 72 +++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 Documentation/x86/intel-hfi.rst diff --git a/Documentation/x86/index.rst b/Documentation/x86/index.rst index f498f1d36cd3..982c8af853b9 100644 --- a/Documentation/x86/index.rst +++ b/Documentation/x86/index.rst @@ -21,6 +21,7 @@ x86-specific Documentation tlb mtrr pat + intel-hfi intel-iommu intel_txt amd-memory-encryption diff --git a/Documentation/x86/intel-hfi.rst b/Documentation/x86/intel-hfi.rst new file mode 100644 index 000000000000..49dea58ea4fb --- /dev/null +++ b/Documentation/x86/intel-hfi.rst @@ -0,0 +1,72 @@ +.. SPDX-License-Identifier: GPL-2.0 + +============================================================ +Hardware-Feedback Interface for scheduling on Intel Hardware +============================================================ + +Overview +-------- + +Intel has described the Hardware Feedback Interface (HFI) in the Intel 64 and +IA-32 Architectures Software Developer's Manual (Intel SDM) Volume 3 Section +14.6 [1]_. + +The HFI gives the operating system a performance and energy efficiency +capability data for each CPU in the system. Linux can use the information from +the HFI to influence task placement decisions. + +The Hardware Feedback Interface +------------------------------- + +The Hardware Feedback Interface provides to the operating system information +about the performance and energy efficiency of each CPU in the system. Each +capability is given as a unit-less quantity in the range [0-255]. Higher values +indicate higher capability. Energy efficiency and performance are reported in +separate capabilities. Even though on some systems these two metrics may be +related, they are specified as independent capabilities in the Intel SDM. + +These capabilities may change at runtime as a result of changes in the +operating conditions of the system or the action of external factors. The rate +at which these capabilities are updated is specific to each processor model. On +some models, capabilities are set at boot time and never change. On others, +capabilities may change every tens of milliseconds. For instance, a remote +mechanism may be used to lower Thermal Design Power. Such change can be +reflected in the HFI. Likewise, if the system needs to be throttled due to +excessive heat, the HFI may reflect reduced performance on specific CPUs. + +The kernel or a userspace policy daemon can use these capabilities to modify +task placement decisions. For instance, if either the performance or energy +capabilities of a given logical processor becomes zero, it is an indication that +the hardware recommends to the operating system to not schedule any tasks on +that processor for performance or energy efficiency reasons, respectively. + +Implementation details for Linux +-------------------------------- + +The infrastructure to handle thermal event interrupts has two parts. In the +Local Vector Table of a CPU's local APIC, there exists a register for the +Thermal Monitor Register. This register controls how interrupts are delivered +to a CPU when the thermal monitor generates and interrupt. Further details +can be found in the Intel SDM Vol. 3 Section 10.5 [1]_. + +The thermal monitor may generate interrupts per CPU or per package. The HFI +generates package-level interrupts. This monitor is configured and initialized +via a set of machine-specific registers. Specifically, the HFI interrupt and +status are controlled via designated bits in the IA32_PACKAGE_THERM_INTERRUPT +and IA32_PACKAGE_THERM_STATUS registers, respectively. There exists one HFI +table per package. Further details can be found in the Intel SDM Vol. 3 +Section 14.9 [1]_. + +The hardware issues an HFI interrupt after updating the HFI table and is ready +for the operating system to consume it. CPUs receive such interrupt via the +thermal entry in the Local APIC's Local Vector Table. + +When servicing such interrupt, the HFI driver parses the updated table and +relays the update to userspace using the thermal notification framework. Given +that there may be many HFI updates every second, the updates relayed to +userspace are throttled at a rate of CONFIG_HZ jiffies. + +References +---------- + +.. [1] https://www.intel.com/sdm From 7b8f40b3de75c971a4e5f9308b06deb59118dbac Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Thu, 27 Jan 2022 11:34:49 -0800 Subject: [PATCH 057/294] x86/cpu: Add definitions for the Intel Hardware Feedback Interface Add the CPUID feature bit and the model-specific registers needed to identify and configure the Intel Hardware Feedback Interface. Acked-by: Borislav Petkov Signed-off-by: Ricardo Neri Signed-off-by: Rafael J. Wysocki --- arch/x86/include/asm/cpufeatures.h | 1 + arch/x86/include/asm/msr-index.h | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/arch/x86/include/asm/cpufeatures.h b/arch/x86/include/asm/cpufeatures.h index 6db4e2932b3d..b39f510128f3 100644 --- a/arch/x86/include/asm/cpufeatures.h +++ b/arch/x86/include/asm/cpufeatures.h @@ -330,6 +330,7 @@ #define X86_FEATURE_HWP_ACT_WINDOW (14*32+ 9) /* HWP Activity Window */ #define X86_FEATURE_HWP_EPP (14*32+10) /* HWP Energy Perf. Preference */ #define X86_FEATURE_HWP_PKG_REQ (14*32+11) /* HWP Package Level Request */ +#define X86_FEATURE_HFI (14*32+19) /* Hardware Feedback Interface */ /* AMD SVM Feature Identification, CPUID level 0x8000000a (EDX), word 15 */ #define X86_FEATURE_NPT (15*32+ 0) /* Nested Page Table support */ diff --git a/arch/x86/include/asm/msr-index.h b/arch/x86/include/asm/msr-index.h index 3faf0f97edb1..89e0a1d5ee36 100644 --- a/arch/x86/include/asm/msr-index.h +++ b/arch/x86/include/asm/msr-index.h @@ -704,12 +704,14 @@ #define PACKAGE_THERM_STATUS_PROCHOT (1 << 0) #define PACKAGE_THERM_STATUS_POWER_LIMIT (1 << 10) +#define PACKAGE_THERM_STATUS_HFI_UPDATED (1 << 26) #define MSR_IA32_PACKAGE_THERM_INTERRUPT 0x000001b2 #define PACKAGE_THERM_INT_HIGH_ENABLE (1 << 0) #define PACKAGE_THERM_INT_LOW_ENABLE (1 << 1) #define PACKAGE_THERM_INT_PLN_ENABLE (1 << 24) +#define PACKAGE_THERM_INT_HFI_ENABLE (1 << 25) /* Thermal Thresholds Support */ #define THERM_INT_THRESHOLD0_ENABLE (1 << 15) @@ -958,4 +960,8 @@ #define MSR_VM_IGNNE 0xc0010115 #define MSR_VM_HSAVE_PA 0xc0010117 +/* Hardware Feedback Interface */ +#define MSR_IA32_HW_FEEDBACK_PTR 0x17d0 +#define MSR_IA32_HW_FEEDBACK_CONFIG 0x17d1 + #endif /* _ASM_X86_MSR_INDEX_H */ From 1cb19cabeb0e187b6c244d0da73d27f7432c40dc Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Thu, 27 Jan 2022 11:34:50 -0800 Subject: [PATCH 058/294] thermal: intel: hfi: Minimally initialize the Hardware Feedback Interface The Intel Hardware Feedback Interface provides guidance to the operating system about the performance and energy efficiency capabilities of each CPU in the system. Capabilities are numbers between 0 and 255 where a higher number represents a higher capability. For each CPU, energy efficiency and performance are reported as separate capabilities. Hardware computes these capabilities based on the operating conditions of the system such as power and thermal limits. These capabilities are shared with the operating system in a table resident in memory. Each package in the system has its own HFI instance. Every logical CPU in the package is represented in the table. More than one logical CPUs may be represented in a single table entry. When the hardware updates the table, it generates a package-level thermal interrupt. The size and format of the HFI table depend on the supported features and can only be determined at runtime. To minimally initialize the HFI, parse its features and allocate one instance per package of a data structure with the necessary parameters to read and navigate a local copy (i.e., owned by the driver) of individual HFI tables. A subsequent changeset will provide per-CPU initialization and interrupt handling. Reviewed-by: Len Brown Co-developed by: Aubrey Li Signed-off-by: Aubrey Li Signed-off-by: Ricardo Neri Signed-off-by: Rafael J. Wysocki --- drivers/thermal/intel/Kconfig | 12 ++ drivers/thermal/intel/Makefile | 1 + drivers/thermal/intel/intel_hfi.c | 181 ++++++++++++++++++++++++++++ drivers/thermal/intel/intel_hfi.h | 11 ++ drivers/thermal/intel/therm_throt.c | 3 + 5 files changed, 208 insertions(+) create mode 100644 drivers/thermal/intel/intel_hfi.c create mode 100644 drivers/thermal/intel/intel_hfi.h diff --git a/drivers/thermal/intel/Kconfig b/drivers/thermal/intel/Kconfig index c83ea5d04a1d..e9d2925227d4 100644 --- a/drivers/thermal/intel/Kconfig +++ b/drivers/thermal/intel/Kconfig @@ -99,3 +99,15 @@ config INTEL_MENLOW Intel Menlow platform. If unsure, say N. + +config INTEL_HFI_THERMAL + bool "Intel Hardware Feedback Interface" + depends on CPU_SUP_INTEL + depends on X86_THERMAL_VECTOR + help + Select this option to enable the Hardware Feedback Interface. If + selected, hardware provides guidance to the operating system on + the performance and energy efficiency capabilities of each CPU. + These capabilities may change as a result of changes in the operating + conditions of the system such power and thermal limits. If selected, + the kernel relays updates in CPUs' capabilities to userspace. diff --git a/drivers/thermal/intel/Makefile b/drivers/thermal/intel/Makefile index 960b56268b4a..9a8d8054f316 100644 --- a/drivers/thermal/intel/Makefile +++ b/drivers/thermal/intel/Makefile @@ -13,3 +13,4 @@ obj-$(CONFIG_INTEL_PCH_THERMAL) += intel_pch_thermal.o obj-$(CONFIG_INTEL_TCC_COOLING) += intel_tcc_cooling.o obj-$(CONFIG_X86_THERMAL_VECTOR) += therm_throt.o obj-$(CONFIG_INTEL_MENLOW) += intel_menlow.o +obj-$(CONFIG_INTEL_HFI_THERMAL) += intel_hfi.o diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c new file mode 100644 index 000000000000..969f4a150973 --- /dev/null +++ b/drivers/thermal/intel/intel_hfi.c @@ -0,0 +1,181 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Hardware Feedback Interface Driver + * + * Copyright (c) 2021, Intel Corporation. + * + * Authors: Aubrey Li + * Ricardo Neri + * + * + * The Hardware Feedback Interface provides a performance and energy efficiency + * capability information for each CPU in the system. Depending on the processor + * model, hardware may periodically update these capabilities as a result of + * changes in the operating conditions (e.g., power limits or thermal + * constraints). On other processor models, there is a single HFI update + * at boot. + * + * This file provides functionality to process HFI updates and relay these + * updates to userspace. + */ + +#define pr_fmt(fmt) "intel-hfi: " fmt + +#include +#include +#include +#include +#include +#include +#include + +#include "intel_hfi.h" + +/* CPUID detection and enumeration definitions for HFI */ + +#define CPUID_HFI_LEAF 6 + +union hfi_capabilities { + struct { + u8 performance:1; + u8 energy_efficiency:1; + u8 __reserved:6; + } split; + u8 bits; +}; + +union cpuid6_edx { + struct { + union hfi_capabilities capabilities; + u32 table_pages:4; + u32 __reserved:4; + s32 index:16; + } split; + u32 full; +}; + +/** + * struct hfi_cpu_data - HFI capabilities per CPU + * @perf_cap: Performance capability + * @ee_cap: Energy efficiency capability + * + * Capabilities of a logical processor in the HFI table. These capabilities are + * unitless. + */ +struct hfi_cpu_data { + u8 perf_cap; + u8 ee_cap; +} __packed; + +/** + * struct hfi_hdr - Header of the HFI table + * @perf_updated: Hardware updated performance capabilities + * @ee_updated: Hardware updated energy efficiency capabilities + * + * Properties of the data in an HFI table. + */ +struct hfi_hdr { + u8 perf_updated; + u8 ee_updated; +} __packed; + +/** + * struct hfi_instance - Representation of an HFI instance (i.e., a table) + * @local_table: Base of the local copy of the HFI table + * @timestamp: Timestamp of the last update of the local table. + * Located at the base of the local table. + * @hdr: Base address of the header of the local table + * @data: Base address of the data of the local table + * + * A set of parameters to parse and navigate a specific HFI table. + */ +struct hfi_instance { + union { + void *local_table; + u64 *timestamp; + }; + void *hdr; + void *data; +}; + +/** + * struct hfi_features - Supported HFI features + * @nr_table_pages: Size of the HFI table in 4KB pages + * @cpu_stride: Stride size to locate the capability data of a logical + * processor within the table (i.e., row stride) + * @hdr_size: Size of the table header + * + * Parameters and supported features that are common to all HFI instances + */ +struct hfi_features { + unsigned int nr_table_pages; + unsigned int cpu_stride; + unsigned int hdr_size; +}; + +static int max_hfi_instances; +static struct hfi_instance *hfi_instances; + +static struct hfi_features hfi_features; + +static __init int hfi_parse_features(void) +{ + unsigned int nr_capabilities; + union cpuid6_edx edx; + + if (!boot_cpu_has(X86_FEATURE_HFI)) + return -ENODEV; + + /* + * If we are here we know that CPUID_HFI_LEAF exists. Parse the + * supported capabilities and the size of the HFI table. + */ + edx.full = cpuid_edx(CPUID_HFI_LEAF); + + if (!edx.split.capabilities.split.performance) { + pr_debug("Performance reporting not supported! Not using HFI\n"); + return -ENODEV; + } + + /* + * The number of supported capabilities determines the number of + * columns in the HFI table. Exclude the reserved bits. + */ + edx.split.capabilities.split.__reserved = 0; + nr_capabilities = hweight8(edx.split.capabilities.bits); + + /* The number of 4KB pages required by the table */ + hfi_features.nr_table_pages = edx.split.table_pages + 1; + + /* + * The header contains change indications for each supported feature. + * The size of the table header is rounded up to be a multiple of 8 + * bytes. + */ + hfi_features.hdr_size = DIV_ROUND_UP(nr_capabilities, 8) * 8; + + /* + * Data of each logical processor is also rounded up to be a multiple + * of 8 bytes. + */ + hfi_features.cpu_stride = DIV_ROUND_UP(nr_capabilities, 8) * 8; + + return 0; +} + +void __init intel_hfi_init(void) +{ + if (hfi_parse_features()) + return; + + /* There is one HFI instance per die/package. */ + max_hfi_instances = topology_max_packages() * + topology_max_die_per_package(); + + /* + * This allocation may fail. CPU hotplug callbacks must check + * for a null pointer. + */ + hfi_instances = kcalloc(max_hfi_instances, sizeof(*hfi_instances), + GFP_KERNEL); +} diff --git a/drivers/thermal/intel/intel_hfi.h b/drivers/thermal/intel/intel_hfi.h new file mode 100644 index 000000000000..05f748b48a4e --- /dev/null +++ b/drivers/thermal/intel/intel_hfi.h @@ -0,0 +1,11 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _INTEL_HFI_H +#define _INTEL_HFI_H + +#if defined(CONFIG_INTEL_HFI_THERMAL) +void __init intel_hfi_init(void); +#else +static inline void intel_hfi_init(void) { } +#endif /* CONFIG_INTEL_HFI_THERMAL */ + +#endif /* _INTEL_HFI_H */ diff --git a/drivers/thermal/intel/therm_throt.c b/drivers/thermal/intel/therm_throt.c index dab7e8fb1059..ac408714d52b 100644 --- a/drivers/thermal/intel/therm_throt.c +++ b/drivers/thermal/intel/therm_throt.c @@ -32,6 +32,7 @@ #include #include +#include "intel_hfi.h" #include "thermal_interrupt.h" /* How long to wait between reporting thermal events */ @@ -509,6 +510,8 @@ static __init int thermal_throttle_init_device(void) if (!atomic_read(&therm_throt_en)) return 0; + intel_hfi_init(); + ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "x86/therm:online", thermal_throttle_online, thermal_throttle_offline); From 2d74e6319abe278981e79166b6c2d0c3ed39b1ae Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Thu, 27 Jan 2022 11:34:51 -0800 Subject: [PATCH 059/294] thermal: intel: hfi: Handle CPU hotplug events All CPUs in a package are represented in an HFI table. There exists an HFI table per package. Thus, CPUs in a package need to coordinate to initialize and access the table. Do such coordination during CPU hotplug. Use the first CPU to come online in a package to initialize the HFI instance and the data structure representing it. Other CPUs in the same package need only to register or unregister themselves in that data structure. The HFI depends on both the package-level thermal management and the local APIC thermal local vector. Thus, to ensure that a CPU coming online has an associated HFI instance when the hardware issues an HFI event, enable the HFI only after having enabled the local APIC thermal vector. The thermal throttle driver takes care of the needed package-level initialization. Reviewed-by: Len Brown Signed-off-by: Ricardo Neri Signed-off-by: Rafael J. Wysocki --- drivers/thermal/intel/intel_hfi.c | 205 ++++++++++++++++++++++++++++ drivers/thermal/intel/intel_hfi.h | 4 + drivers/thermal/intel/therm_throt.c | 9 ++ 3 files changed, 218 insertions(+) diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c index 969f4a150973..d36858474f74 100644 --- a/drivers/thermal/intel/intel_hfi.c +++ b/drivers/thermal/intel/intel_hfi.c @@ -23,14 +23,24 @@ #include #include +#include +#include +#include #include +#include +#include #include #include #include #include +#include + #include "intel_hfi.h" +/* Hardware Feedback Interface MSR configuration bits */ +#define HW_FEEDBACK_PTR_VALID_BIT BIT(0) + /* CPUID detection and enumeration definitions for HFI */ #define CPUID_HFI_LEAF 6 @@ -86,6 +96,8 @@ struct hfi_hdr { * Located at the base of the local table. * @hdr: Base address of the header of the local table * @data: Base address of the data of the local table + * @cpus: CPUs represented in this HFI table instance + * @hw_table: Pointer to the HFI table of this instance * * A set of parameters to parse and navigate a specific HFI table. */ @@ -96,6 +108,8 @@ struct hfi_instance { }; void *hdr; void *data; + cpumask_var_t cpus; + void *hw_table; }; /** @@ -113,10 +127,179 @@ struct hfi_features { unsigned int hdr_size; }; +/** + * struct hfi_cpu_info - Per-CPU attributes to consume HFI data + * @index: Row of this CPU in its HFI table + * @hfi_instance: Attributes of the HFI table to which this CPU belongs + * + * Parameters to link a logical processor to an HFI table and a row within it. + */ +struct hfi_cpu_info { + s16 index; + struct hfi_instance *hfi_instance; +}; + +static DEFINE_PER_CPU(struct hfi_cpu_info, hfi_cpu_info) = { .index = -1 }; + static int max_hfi_instances; static struct hfi_instance *hfi_instances; static struct hfi_features hfi_features; +static DEFINE_MUTEX(hfi_instance_lock); + +static void init_hfi_cpu_index(struct hfi_cpu_info *info) +{ + union cpuid6_edx edx; + + /* Do not re-read @cpu's index if it has already been initialized. */ + if (info->index > -1) + return; + + edx.full = cpuid_edx(CPUID_HFI_LEAF); + info->index = edx.split.index; +} + +/* + * The format of the HFI table depends on the number of capabilities that the + * hardware supports. Keep a data structure to navigate the table. + */ +static void init_hfi_instance(struct hfi_instance *hfi_instance) +{ + /* The HFI header is below the time-stamp. */ + hfi_instance->hdr = hfi_instance->local_table + + sizeof(*hfi_instance->timestamp); + + /* The HFI data starts below the header. */ + hfi_instance->data = hfi_instance->hdr + hfi_features.hdr_size; +} + +/** + * intel_hfi_online() - Enable HFI on @cpu + * @cpu: CPU in which the HFI will be enabled + * + * Enable the HFI to be used in @cpu. The HFI is enabled at the die/package + * level. The first CPU in the die/package to come online does the full HFI + * initialization. Subsequent CPUs will just link themselves to the HFI + * instance of their die/package. + * + * This function is called before enabling the thermal vector in the local APIC + * in order to ensure that @cpu has an associated HFI instance when it receives + * an HFI event. + */ +void intel_hfi_online(unsigned int cpu) +{ + struct hfi_instance *hfi_instance; + struct hfi_cpu_info *info; + phys_addr_t hw_table_pa; + u64 msr_val; + u16 die_id; + + /* Nothing to do if hfi_instances are missing. */ + if (!hfi_instances) + return; + + /* + * Link @cpu to the HFI instance of its package/die. It does not + * matter whether the instance has been initialized. + */ + info = &per_cpu(hfi_cpu_info, cpu); + die_id = topology_logical_die_id(cpu); + hfi_instance = info->hfi_instance; + if (!hfi_instance) { + if (die_id < 0 || die_id >= max_hfi_instances) + return; + + hfi_instance = &hfi_instances[die_id]; + info->hfi_instance = hfi_instance; + } + + init_hfi_cpu_index(info); + + /* + * Now check if the HFI instance of the package/die of @cpu has been + * initialized (by checking its header). In such case, all we have to + * do is to add @cpu to this instance's cpumask. + */ + mutex_lock(&hfi_instance_lock); + if (hfi_instance->hdr) { + cpumask_set_cpu(cpu, hfi_instance->cpus); + goto unlock; + } + + /* + * Hardware is programmed with the physical address of the first page + * frame of the table. Hence, the allocated memory must be page-aligned. + */ + hfi_instance->hw_table = alloc_pages_exact(hfi_features.nr_table_pages, + GFP_KERNEL | __GFP_ZERO); + if (!hfi_instance->hw_table) + goto unlock; + + hw_table_pa = virt_to_phys(hfi_instance->hw_table); + + /* + * Allocate memory to keep a local copy of the table that + * hardware generates. + */ + hfi_instance->local_table = kzalloc(hfi_features.nr_table_pages << PAGE_SHIFT, + GFP_KERNEL); + if (!hfi_instance->local_table) + goto free_hw_table; + + /* + * Program the address of the feedback table of this die/package. On + * some processors, hardware remembers the old address of the HFI table + * even after having been reprogrammed and re-enabled. Thus, do not free + * the pages allocated for the table or reprogram the hardware with a + * new base address. Namely, program the hardware only once. + */ + msr_val = hw_table_pa | HW_FEEDBACK_PTR_VALID_BIT; + wrmsrl(MSR_IA32_HW_FEEDBACK_PTR, msr_val); + + init_hfi_instance(hfi_instance); + + cpumask_set_cpu(cpu, hfi_instance->cpus); + +unlock: + mutex_unlock(&hfi_instance_lock); + return; + +free_hw_table: + free_pages_exact(hfi_instance->hw_table, hfi_features.nr_table_pages); + goto unlock; +} + +/** + * intel_hfi_offline() - Disable HFI on @cpu + * @cpu: CPU in which the HFI will be disabled + * + * Remove @cpu from those covered by its HFI instance. + * + * On some processors, hardware remembers previous programming settings even + * after being reprogrammed. Thus, keep HFI enabled even if all CPUs in the + * die/package of @cpu are offline. See note in intel_hfi_online(). + */ +void intel_hfi_offline(unsigned int cpu) +{ + struct hfi_cpu_info *info = &per_cpu(hfi_cpu_info, cpu); + struct hfi_instance *hfi_instance; + + /* + * Check if @cpu as an associated, initialized (i.e., with a non-NULL + * header). Also, HFI instances are only initialized if X86_FEATURE_HFI + * is present. + */ + hfi_instance = info->hfi_instance; + if (!hfi_instance) + return; + + if (!hfi_instance->hdr) + return; + + mutex_lock(&hfi_instance_lock); + cpumask_clear_cpu(cpu, hfi_instance->cpus); + mutex_unlock(&hfi_instance_lock); +} static __init int hfi_parse_features(void) { @@ -165,6 +348,9 @@ static __init int hfi_parse_features(void) void __init intel_hfi_init(void) { + struct hfi_instance *hfi_instance; + int i, j; + if (hfi_parse_features()) return; @@ -178,4 +364,23 @@ void __init intel_hfi_init(void) */ hfi_instances = kcalloc(max_hfi_instances, sizeof(*hfi_instances), GFP_KERNEL); + if (!hfi_instances) + return; + + for (i = 0; i < max_hfi_instances; i++) { + hfi_instance = &hfi_instances[i]; + if (!zalloc_cpumask_var(&hfi_instance->cpus, GFP_KERNEL)) + goto err_nomem; + } + + return; + +err_nomem: + for (j = 0; j < i; ++j) { + hfi_instance = &hfi_instances[j]; + free_cpumask_var(hfi_instance->cpus); + } + + kfree(hfi_instances); + hfi_instances = NULL; } diff --git a/drivers/thermal/intel/intel_hfi.h b/drivers/thermal/intel/intel_hfi.h index 05f748b48a4e..56c6b2d75202 100644 --- a/drivers/thermal/intel/intel_hfi.h +++ b/drivers/thermal/intel/intel_hfi.h @@ -4,8 +4,12 @@ #if defined(CONFIG_INTEL_HFI_THERMAL) void __init intel_hfi_init(void); +void intel_hfi_online(unsigned int cpu); +void intel_hfi_offline(unsigned int cpu); #else static inline void intel_hfi_init(void) { } +static inline void intel_hfi_online(unsigned int cpu) { } +static inline void intel_hfi_offline(unsigned int cpu) { } #endif /* CONFIG_INTEL_HFI_THERMAL */ #endif /* _INTEL_HFI_H */ diff --git a/drivers/thermal/intel/therm_throt.c b/drivers/thermal/intel/therm_throt.c index ac408714d52b..8571e57a013a 100644 --- a/drivers/thermal/intel/therm_throt.c +++ b/drivers/thermal/intel/therm_throt.c @@ -476,6 +476,13 @@ static int thermal_throttle_online(unsigned int cpu) INIT_DELAYED_WORK(&state->package_throttle.therm_work, throttle_active_work); INIT_DELAYED_WORK(&state->core_throttle.therm_work, throttle_active_work); + /* + * The first CPU coming online will enable the HFI. Usually this causes + * hardware to issue an HFI thermal interrupt. Such interrupt will reach + * the CPU once we enable the thermal vector in the local APIC. + */ + intel_hfi_online(cpu); + /* Unmask the thermal vector after the above workqueues are initialized. */ l = apic_read(APIC_LVTTHMR); apic_write(APIC_LVTTHMR, l & ~APIC_LVT_MASKED); @@ -493,6 +500,8 @@ static int thermal_throttle_offline(unsigned int cpu) l = apic_read(APIC_LVTTHMR); apic_write(APIC_LVTTHMR, l | APIC_LVT_MASKED); + intel_hfi_offline(cpu); + cancel_delayed_work_sync(&state->package_throttle.therm_work); cancel_delayed_work_sync(&state->core_throttle.therm_work); From ab09b0744a9944cbdc0ac9a5cb00bef72adf79d5 Mon Sep 17 00:00:00 2001 From: Ricardo Neri Date: Thu, 27 Jan 2022 11:34:52 -0800 Subject: [PATCH 060/294] thermal: intel: hfi: Enable notification interrupt When hardware wants to inform the operating system about updates in the HFI table, it issues a package-level thermal event interrupt. For this, hardware has new interrupt and status bits in the IA32_PACKAGE_THERM_ INTERRUPT and IA32_PACKAGE_THERM_STATUS registers. The existing thermal throttle driver already handles thermal event interrupts: it initializes the thermal vector of the local APIC as well as per-CPU and package-level interrupt reporting. It also provides routines to service such interrupts. Extend its functionality to also handle HFI interrupts. The frequency of the thermal HFI interrupt is specific to each processor model. On some processors, a single interrupt happens as soon as the HFI is enabled and hardware will never update HFI capabilities afterwards. On other processors, thermal and power constraints may cause thermal HFI interrupts every tens of milliseconds. To not overwhelm consumers of the HFI data, use delayed work to throttle the rate at which HFI updates are processed. Use a dedicated workqueue to not overload system_wq if hardware issues many HFI updates. Reviewed-by: Len Brown Signed-off-by: Ricardo Neri Signed-off-by: Rafael J. Wysocki --- drivers/thermal/intel/intel_hfi.c | 110 ++++++++++++++++++++++++++++ drivers/thermal/intel/intel_hfi.h | 2 + drivers/thermal/intel/therm_throt.c | 10 +++ 3 files changed, 122 insertions(+) diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c index d36858474f74..7a2b750eecc4 100644 --- a/drivers/thermal/intel/intel_hfi.c +++ b/drivers/thermal/intel/intel_hfi.c @@ -26,20 +26,28 @@ #include #include #include +#include #include #include #include #include #include #include +#include +#include #include +#include #include #include "intel_hfi.h" +#define THERM_STATUS_CLEAR_PKG_MASK (BIT(1) | BIT(3) | BIT(5) | BIT(7) | \ + BIT(9) | BIT(11) | BIT(26)) + /* Hardware Feedback Interface MSR configuration bits */ #define HW_FEEDBACK_PTR_VALID_BIT BIT(0) +#define HW_FEEDBACK_CONFIG_HFI_ENABLE_BIT BIT(0) /* CPUID detection and enumeration definitions for HFI */ @@ -98,6 +106,9 @@ struct hfi_hdr { * @data: Base address of the data of the local table * @cpus: CPUs represented in this HFI table instance * @hw_table: Pointer to the HFI table of this instance + * @update_work: Delayed work to process HFI updates + * @table_lock: Lock to protect acceses to the table of this instance + * @event_lock: Lock to process HFI interrupts * * A set of parameters to parse and navigate a specific HFI table. */ @@ -110,6 +121,9 @@ struct hfi_instance { void *data; cpumask_var_t cpus; void *hw_table; + struct delayed_work update_work; + raw_spinlock_t table_lock; + raw_spinlock_t event_lock; }; /** @@ -147,6 +161,86 @@ static struct hfi_instance *hfi_instances; static struct hfi_features hfi_features; static DEFINE_MUTEX(hfi_instance_lock); +static struct workqueue_struct *hfi_updates_wq; +#define HFI_UPDATE_INTERVAL HZ + +static void hfi_update_work_fn(struct work_struct *work) +{ + struct hfi_instance *hfi_instance; + + hfi_instance = container_of(to_delayed_work(work), struct hfi_instance, + update_work); + if (!hfi_instance) + return; + + /* TODO: Consume update here. */ +} + +void intel_hfi_process_event(__u64 pkg_therm_status_msr_val) +{ + struct hfi_instance *hfi_instance; + int cpu = smp_processor_id(); + struct hfi_cpu_info *info; + u64 new_timestamp; + + if (!pkg_therm_status_msr_val) + return; + + info = &per_cpu(hfi_cpu_info, cpu); + if (!info) + return; + + /* + * A CPU is linked to its HFI instance before the thermal vector in the + * local APIC is unmasked. Hence, info->hfi_instance cannot be NULL + * when receiving an HFI event. + */ + hfi_instance = info->hfi_instance; + if (unlikely(!hfi_instance)) { + pr_debug("Received event on CPU %d but instance was null", cpu); + return; + } + + /* + * On most systems, all CPUs in the package receive a package-level + * thermal interrupt when there is an HFI update. It is sufficient to + * let a single CPU to acknowledge the update and queue work to + * process it. The remaining CPUs can resume their work. + */ + if (!raw_spin_trylock(&hfi_instance->event_lock)) + return; + + /* Skip duplicated updates. */ + new_timestamp = *(u64 *)hfi_instance->hw_table; + if (*hfi_instance->timestamp == new_timestamp) { + raw_spin_unlock(&hfi_instance->event_lock); + return; + } + + raw_spin_lock(&hfi_instance->table_lock); + + /* + * Copy the updated table into our local copy. This includes the new + * timestamp. + */ + memcpy(hfi_instance->local_table, hfi_instance->hw_table, + hfi_features.nr_table_pages << PAGE_SHIFT); + + raw_spin_unlock(&hfi_instance->table_lock); + raw_spin_unlock(&hfi_instance->event_lock); + + /* + * Let hardware know that we are done reading the HFI table and it is + * free to update it again. + */ + pkg_therm_status_msr_val &= THERM_STATUS_CLEAR_PKG_MASK & + ~PACKAGE_THERM_STATUS_HFI_UPDATED; + wrmsrl(MSR_IA32_PACKAGE_THERM_STATUS, pkg_therm_status_msr_val); + + queue_delayed_work(hfi_updates_wq, &hfi_instance->update_work, + HFI_UPDATE_INTERVAL); +} + static void init_hfi_cpu_index(struct hfi_cpu_info *info) { union cpuid6_edx edx; @@ -258,8 +352,20 @@ void intel_hfi_online(unsigned int cpu) init_hfi_instance(hfi_instance); + INIT_DELAYED_WORK(&hfi_instance->update_work, hfi_update_work_fn); + raw_spin_lock_init(&hfi_instance->table_lock); + raw_spin_lock_init(&hfi_instance->event_lock); + cpumask_set_cpu(cpu, hfi_instance->cpus); + /* + * Enable the hardware feedback interface and never disable it. See + * comment on programming the address of the table. + */ + rdmsrl(MSR_IA32_HW_FEEDBACK_CONFIG, msr_val); + msr_val |= HW_FEEDBACK_CONFIG_HFI_ENABLE_BIT; + wrmsrl(MSR_IA32_HW_FEEDBACK_CONFIG, msr_val); + unlock: mutex_unlock(&hfi_instance_lock); return; @@ -373,6 +479,10 @@ void __init intel_hfi_init(void) goto err_nomem; } + hfi_updates_wq = create_singlethread_workqueue("hfi-updates"); + if (!hfi_updates_wq) + goto err_nomem; + return; err_nomem: diff --git a/drivers/thermal/intel/intel_hfi.h b/drivers/thermal/intel/intel_hfi.h index 56c6b2d75202..325aa78b745c 100644 --- a/drivers/thermal/intel/intel_hfi.h +++ b/drivers/thermal/intel/intel_hfi.h @@ -6,10 +6,12 @@ void __init intel_hfi_init(void); void intel_hfi_online(unsigned int cpu); void intel_hfi_offline(unsigned int cpu); +void intel_hfi_process_event(__u64 pkg_therm_status_msr_val); #else static inline void intel_hfi_init(void) { } static inline void intel_hfi_online(unsigned int cpu) { } static inline void intel_hfi_offline(unsigned int cpu) { } +static inline void intel_hfi_process_event(__u64 pkg_therm_status_msr_val) { } #endif /* CONFIG_INTEL_HFI_THERMAL */ #endif /* _INTEL_HFI_H */ diff --git a/drivers/thermal/intel/therm_throt.c b/drivers/thermal/intel/therm_throt.c index 8571e57a013a..8352083b87c7 100644 --- a/drivers/thermal/intel/therm_throt.c +++ b/drivers/thermal/intel/therm_throt.c @@ -620,6 +620,10 @@ void intel_thermal_interrupt(void) PACKAGE_THERM_STATUS_POWER_LIMIT, POWER_LIMIT_EVENT, PACKAGE_LEVEL); + + if (this_cpu_has(X86_FEATURE_HFI)) + intel_hfi_process_event(msr_val & + PACKAGE_THERM_STATUS_HFI_UPDATED); } } @@ -729,6 +733,12 @@ void intel_init_thermal(struct cpuinfo_x86 *c) wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l | (PACKAGE_THERM_INT_LOW_ENABLE | PACKAGE_THERM_INT_HIGH_ENABLE), h); + + if (cpu_has(c, X86_FEATURE_HFI)) { + rdmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, l, h); + wrmsr(MSR_IA32_PACKAGE_THERM_INTERRUPT, + l | PACKAGE_THERM_INT_HFI_ENABLE, h); + } } rdmsr(MSR_IA32_MISC_ENABLE, l, h); From e4b1eb24ce5a696ef7229f9926ff34d7502f0582 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 27 Jan 2022 11:34:53 -0800 Subject: [PATCH 061/294] thermal: netlink: Add a new event to notify CPU capabilities change Add a new netlink event to notify change in CPU capabilities in terms of performance and efficiency. Firmware may change CPU capabilities as a result of thermal events in the system or to account for changes in the TDP (thermal design power) level. This notification type will allow user space to avoid running workloads on certain CPUs or proactively adjust power limits to avoid future events. The netlink message consists of a nested attribute (THERMAL_GENL_ATTR_CPU_CAPABILITY) with three attributes: * THERMAL_GENL_ATTR_CPU_CAPABILITY_ID (type u32): -- logical CPU number * THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE (type u32): -- Scaled performance from 0-1023 * THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY (type u32): -- Scaled efficiency from 0-1023 Reviewed-by: Len Brown Signed-off-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki --- drivers/thermal/thermal_netlink.c | 53 +++++++++++++++++++++++++++++++ drivers/thermal/thermal_netlink.h | 14 ++++++++ include/uapi/linux/thermal.h | 6 +++- 3 files changed, 72 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/thermal_netlink.c b/drivers/thermal/thermal_netlink.c index a16dd4d5d710..7c97a091680e 100644 --- a/drivers/thermal/thermal_netlink.c +++ b/drivers/thermal/thermal_netlink.c @@ -43,6 +43,11 @@ static const struct nla_policy thermal_genl_policy[THERMAL_GENL_ATTR_MAX + 1] = [THERMAL_GENL_ATTR_CDEV_MAX_STATE] = { .type = NLA_U32 }, [THERMAL_GENL_ATTR_CDEV_NAME] = { .type = NLA_STRING, .len = THERMAL_NAME_LENGTH }, + /* CPU capabilities */ + [THERMAL_GENL_ATTR_CPU_CAPABILITY] = { .type = NLA_NESTED }, + [THERMAL_GENL_ATTR_CPU_CAPABILITY_ID] = { .type = NLA_U32 }, + [THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE] = { .type = NLA_U32 }, + [THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY] = { .type = NLA_U32 }, }; struct param { @@ -58,6 +63,8 @@ struct param { int temp; int cdev_state; int cdev_max_state; + struct thermal_genl_cpu_caps *cpu_capabilities; + int cpu_capabilities_count; }; typedef int (*cb_t)(struct param *); @@ -190,6 +197,42 @@ static int thermal_genl_event_gov_change(struct param *p) return 0; } +static int thermal_genl_event_cpu_capability_change(struct param *p) +{ + struct thermal_genl_cpu_caps *cpu_cap = p->cpu_capabilities; + struct sk_buff *msg = p->msg; + struct nlattr *start_cap; + int i; + + start_cap = nla_nest_start(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY); + if (!start_cap) + return -EMSGSIZE; + + for (i = 0; i < p->cpu_capabilities_count; ++i) { + if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_ID, + cpu_cap->cpu)) + goto out_cancel_nest; + + if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE, + cpu_cap->performance)) + goto out_cancel_nest; + + if (nla_put_u32(msg, THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY, + cpu_cap->efficiency)) + goto out_cancel_nest; + + ++cpu_cap; + } + + nla_nest_end(msg, start_cap); + + return 0; +out_cancel_nest: + nla_nest_cancel(msg, start_cap); + + return -EMSGSIZE; +} + int thermal_genl_event_tz_delete(struct param *p) __attribute__((alias("thermal_genl_event_tz"))); @@ -219,6 +262,7 @@ static cb_t event_cb[] = { [THERMAL_GENL_EVENT_CDEV_DELETE] = thermal_genl_event_cdev_delete, [THERMAL_GENL_EVENT_CDEV_STATE_UPDATE] = thermal_genl_event_cdev_state_update, [THERMAL_GENL_EVENT_TZ_GOV_CHANGE] = thermal_genl_event_gov_change, + [THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE] = thermal_genl_event_cpu_capability_change, }; /* @@ -356,6 +400,15 @@ int thermal_notify_tz_gov_change(int tz_id, const char *name) return thermal_genl_send_event(THERMAL_GENL_EVENT_TZ_GOV_CHANGE, &p); } +int thermal_genl_cpu_capability_event(int count, + struct thermal_genl_cpu_caps *caps) +{ + struct param p = { .cpu_capabilities_count = count, .cpu_capabilities = caps }; + + return thermal_genl_send_event(THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE, &p); +} +EXPORT_SYMBOL_GPL(thermal_genl_cpu_capability_event); + /*************************** Command encoding ********************************/ static int __thermal_genl_cmd_tz_get_id(struct thermal_zone_device *tz, diff --git a/drivers/thermal/thermal_netlink.h b/drivers/thermal/thermal_netlink.h index e554f76291f4..04d1adbbc012 100644 --- a/drivers/thermal/thermal_netlink.h +++ b/drivers/thermal/thermal_netlink.h @@ -4,6 +4,12 @@ * Author: Daniel Lezcano */ +struct thermal_genl_cpu_caps { + int cpu; + int performance; + int efficiency; +}; + /* Netlink notification function */ #ifdef CONFIG_THERMAL_NETLINK int __init thermal_netlink_init(void); @@ -23,6 +29,8 @@ int thermal_notify_cdev_add(int cdev_id, const char *name, int max_state); int thermal_notify_cdev_delete(int cdev_id); int thermal_notify_tz_gov_change(int tz_id, const char *name); int thermal_genl_sampling_temp(int id, int temp); +int thermal_genl_cpu_capability_event(int count, + struct thermal_genl_cpu_caps *caps); #else static inline int thermal_netlink_init(void) { @@ -101,4 +109,10 @@ static inline int thermal_genl_sampling_temp(int id, int temp) { return 0; } + +static inline int thermal_genl_cpu_capability_event(int count, struct cpu_capability *caps) +{ + return 0; +} + #endif /* CONFIG_THERMAL_NETLINK */ diff --git a/include/uapi/linux/thermal.h b/include/uapi/linux/thermal.h index 9aa2fedfa309..fc78bf3aead7 100644 --- a/include/uapi/linux/thermal.h +++ b/include/uapi/linux/thermal.h @@ -44,7 +44,10 @@ enum thermal_genl_attr { THERMAL_GENL_ATTR_CDEV_MAX_STATE, THERMAL_GENL_ATTR_CDEV_NAME, THERMAL_GENL_ATTR_GOV_NAME, - + THERMAL_GENL_ATTR_CPU_CAPABILITY, + THERMAL_GENL_ATTR_CPU_CAPABILITY_ID, + THERMAL_GENL_ATTR_CPU_CAPABILITY_PERFORMANCE, + THERMAL_GENL_ATTR_CPU_CAPABILITY_EFFICIENCY, __THERMAL_GENL_ATTR_MAX, }; #define THERMAL_GENL_ATTR_MAX (__THERMAL_GENL_ATTR_MAX - 1) @@ -71,6 +74,7 @@ enum thermal_genl_event { THERMAL_GENL_EVENT_CDEV_DELETE, /* Cdev unbound */ THERMAL_GENL_EVENT_CDEV_STATE_UPDATE, /* Cdev state updated */ THERMAL_GENL_EVENT_TZ_GOV_CHANGE, /* Governor policy changed */ + THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE, /* CPU capability changed */ __THERMAL_GENL_EVENT_MAX, }; #define THERMAL_GENL_EVENT_MAX (__THERMAL_GENL_EVENT_MAX - 1) From bd30cdfd9bd73b68e4977ce7c5540aa7b14c25cd Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Thu, 27 Jan 2022 11:34:54 -0800 Subject: [PATCH 062/294] thermal: intel: hfi: Notify user space for HFI events When the hardware issues an HFI event, relay a notification to user space. This allows user space to respond by reading performance and efficiency of each CPU and take appropriate action. For example, when the performance and efficiency of a CPU is 0, user space can either offline the CPU or inject idle. Also, if user space notices a downward trend in performance, it may proactively adjust power limits to avoid future situations in which performance drops to 0. To avoid excessive notifications, the rate is limited by one HZ per event. To limit the netlink message size, send parameters for up to 16 CPUs in a single message. If there are more than 16 CPUs, issue as many messages as needed to notify the status of all CPUs. In the HFI specification, both performance and efficiency capabilities are defined in the [0, 255] range. The existing implementations of HFI hardware do not scale the maximum values to 255. Since userspace cares about capability values that are either 0 or show a downward/upward trend, this fact does not matter much. Relative changes in capabilities are enough. To comply with the thermal netlink ABI, scale both performance and efficiency capabilities to the [0, 1023] interval. Reviewed-by: Len Brown Signed-off-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki --- drivers/thermal/intel/Kconfig | 1 + drivers/thermal/intel/intel_hfi.c | 75 ++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/drivers/thermal/intel/Kconfig b/drivers/thermal/intel/Kconfig index e9d2925227d4..6cf3fe36a4ae 100644 --- a/drivers/thermal/intel/Kconfig +++ b/drivers/thermal/intel/Kconfig @@ -104,6 +104,7 @@ config INTEL_HFI_THERMAL bool "Intel Hardware Feedback Interface" depends on CPU_SUP_INTEL depends on X86_THERMAL_VECTOR + select THERMAL_NETLINK help Select this option to enable the Hardware Feedback Interface. If selected, hardware provides guidance to the operating system on diff --git a/drivers/thermal/intel/intel_hfi.c b/drivers/thermal/intel/intel_hfi.c index 7a2b750eecc4..730fd121df6e 100644 --- a/drivers/thermal/intel/intel_hfi.c +++ b/drivers/thermal/intel/intel_hfi.c @@ -40,6 +40,7 @@ #include +#include "../thermal_core.h" #include "intel_hfi.h" #define THERM_STATUS_CLEAR_PKG_MASK (BIT(1) | BIT(3) | BIT(5) | BIT(7) | \ @@ -163,6 +164,78 @@ static DEFINE_MUTEX(hfi_instance_lock); static struct workqueue_struct *hfi_updates_wq; #define HFI_UPDATE_INTERVAL HZ +#define HFI_MAX_THERM_NOTIFY_COUNT 16 + +static void get_hfi_caps(struct hfi_instance *hfi_instance, + struct thermal_genl_cpu_caps *cpu_caps) +{ + int cpu, i = 0; + + raw_spin_lock_irq(&hfi_instance->table_lock); + for_each_cpu(cpu, hfi_instance->cpus) { + struct hfi_cpu_data *caps; + s16 index; + + index = per_cpu(hfi_cpu_info, cpu).index; + caps = hfi_instance->data + index * hfi_features.cpu_stride; + cpu_caps[i].cpu = cpu; + + /* + * Scale performance and energy efficiency to + * the [0, 1023] interval that thermal netlink uses. + */ + cpu_caps[i].performance = caps->perf_cap << 2; + cpu_caps[i].efficiency = caps->ee_cap << 2; + + ++i; + } + raw_spin_unlock_irq(&hfi_instance->table_lock); +} + +/* + * Call update_capabilities() when there are changes in the HFI table. + */ +static void update_capabilities(struct hfi_instance *hfi_instance) +{ + struct thermal_genl_cpu_caps *cpu_caps; + int i = 0, cpu_count; + + /* CPUs may come online/offline while processing an HFI update. */ + mutex_lock(&hfi_instance_lock); + + cpu_count = cpumask_weight(hfi_instance->cpus); + + /* No CPUs to report in this hfi_instance. */ + if (!cpu_count) + goto out; + + cpu_caps = kcalloc(cpu_count, sizeof(*cpu_caps), GFP_KERNEL); + if (!cpu_caps) + goto out; + + get_hfi_caps(hfi_instance, cpu_caps); + + if (cpu_count < HFI_MAX_THERM_NOTIFY_COUNT) + goto last_cmd; + + /* Process complete chunks of HFI_MAX_THERM_NOTIFY_COUNT capabilities. */ + for (i = 0; + (i + HFI_MAX_THERM_NOTIFY_COUNT) <= cpu_count; + i += HFI_MAX_THERM_NOTIFY_COUNT) + thermal_genl_cpu_capability_event(HFI_MAX_THERM_NOTIFY_COUNT, + &cpu_caps[i]); + + cpu_count = cpu_count - i; + +last_cmd: + /* Process the remaining capabilities if any. */ + if (cpu_count) + thermal_genl_cpu_capability_event(cpu_count, &cpu_caps[i]); + + kfree(cpu_caps); +out: + mutex_unlock(&hfi_instance_lock); +} static void hfi_update_work_fn(struct work_struct *work) { @@ -173,7 +246,7 @@ static void hfi_update_work_fn(struct work_struct *work) if (!hfi_instance) return; - /* TODO: Consume update here. */ + update_capabilities(hfi_instance); } void intel_hfi_process_event(__u64 pkg_therm_status_msr_val) From b9794a822281944ef3de5b1812a94cbdb8134320 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 28 Jan 2022 17:35:33 +0100 Subject: [PATCH 063/294] powercap/drivers/dtpm: Convert the init table section to a simple array The init table section is freed after the system booted. However the next changes will make per module the DTPM description, so the table won't be accessible when the module is loaded. In order to fix that, we should move the table to the data section where there are very few entries and that makes strange to add it there. The main goal of the table was to keep self-encapsulated code and we can keep it almost as it by using an array instead. Suggested-by: Ulf Hansson Reviewed-by: Ulf Hansson Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20220128163537.212248-2-daniel.lezcano@linaro.org --- drivers/powercap/dtpm.c | 2 ++ drivers/powercap/dtpm_cpu.c | 5 ++++- drivers/powercap/dtpm_subsys.h | 18 ++++++++++++++++++ include/asm-generic/vmlinux.lds.h | 11 ----------- include/linux/dtpm.h | 24 +++--------------------- 5 files changed, 27 insertions(+), 33 deletions(-) create mode 100644 drivers/powercap/dtpm_subsys.h diff --git a/drivers/powercap/dtpm.c b/drivers/powercap/dtpm.c index 8cb45f2d3d78..0e5c93443c70 100644 --- a/drivers/powercap/dtpm.c +++ b/drivers/powercap/dtpm.c @@ -24,6 +24,8 @@ #include #include +#include "dtpm_subsys.h" + #define DTPM_POWER_LIMIT_FLAG 0 static const char *constraint_name[] = { diff --git a/drivers/powercap/dtpm_cpu.c b/drivers/powercap/dtpm_cpu.c index b740866b228d..5763e0ce2af5 100644 --- a/drivers/powercap/dtpm_cpu.c +++ b/drivers/powercap/dtpm_cpu.c @@ -269,4 +269,7 @@ static int __init dtpm_cpu_init(void) return 0; } -DTPM_DECLARE(dtpm_cpu, dtpm_cpu_init); +struct dtpm_subsys_ops dtpm_cpu_ops = { + .name = KBUILD_MODNAME, + .init = dtpm_cpu_init, +}; diff --git a/drivers/powercap/dtpm_subsys.h b/drivers/powercap/dtpm_subsys.h new file mode 100644 index 000000000000..2a3a2055f60e --- /dev/null +++ b/drivers/powercap/dtpm_subsys.h @@ -0,0 +1,18 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (C) 2022 Linaro Ltd + * + * Author: Daniel Lezcano + */ +#ifndef ___DTPM_SUBSYS_H__ +#define ___DTPM_SUBSYS_H__ + +extern struct dtpm_subsys_ops dtpm_cpu_ops; + +struct dtpm_subsys_ops *dtpm_subsys[] = { +#ifdef CONFIG_DTPM_CPU + &dtpm_cpu_ops, +#endif +}; + +#endif diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index 42f3866bca69..2a10db2f0bc5 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -321,16 +321,6 @@ #define THERMAL_TABLE(name) #endif -#ifdef CONFIG_DTPM -#define DTPM_TABLE() \ - . = ALIGN(8); \ - __dtpm_table = .; \ - KEEP(*(__dtpm_table)) \ - __dtpm_table_end = .; -#else -#define DTPM_TABLE() -#endif - #define KERNEL_DTB() \ STRUCT_ALIGN(); \ __dtb_start = .; \ @@ -723,7 +713,6 @@ ACPI_PROBE_TABLE(irqchip) \ ACPI_PROBE_TABLE(timer) \ THERMAL_TABLE(governor) \ - DTPM_TABLE() \ EARLYCON_TABLE() \ LSM_TABLE() \ EARLY_LSM_TABLE() \ diff --git a/include/linux/dtpm.h b/include/linux/dtpm.h index d37e5d06a357..506048158a50 100644 --- a/include/linux/dtpm.h +++ b/include/linux/dtpm.h @@ -32,29 +32,11 @@ struct dtpm_ops { void (*release)(struct dtpm *); }; -typedef int (*dtpm_init_t)(void); - -struct dtpm_descr { - dtpm_init_t init; +struct dtpm_subsys_ops { + const char *name; + int (*init)(void); }; -/* Init section thermal table */ -extern struct dtpm_descr __dtpm_table[]; -extern struct dtpm_descr __dtpm_table_end[]; - -#define DTPM_TABLE_ENTRY(name, __init) \ - static struct dtpm_descr __dtpm_table_entry_##name \ - __used __section("__dtpm_table") = { \ - .init = __init, \ - } - -#define DTPM_DECLARE(name, init) DTPM_TABLE_ENTRY(name, init) - -#define for_each_dtpm_table(__dtpm) \ - for (__dtpm = __dtpm_table; \ - __dtpm < __dtpm_table_end; \ - __dtpm++) - static inline struct dtpm *to_dtpm(struct powercap_zone *zone) { return container_of(zone, struct dtpm, zone); From 3759ec678e8944dc2ea70cab77a300408f78ae27 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 28 Jan 2022 17:35:34 +0100 Subject: [PATCH 064/294] powercap/drivers/dtpm: Add hierarchy creation The DTPM framework is available but without a way to configure it. This change provides a way to create a hierarchy of DTPM node where the power consumption reflects the sum of the children's power consumption. It is up to the platform to specify an array of dtpm nodes where each element has a pointer to its parent, except the top most one. The type of the node gives the indication of which initialization callback to call. At this time, we can create a virtual node, where its purpose is to be a parent in the hierarchy, and a DT node where the name describes its path. In order to ensure a nice self-encapsulation, the DTPM subsys array contains a couple of initialization functions, one to setup the DTPM backend and one to initialize it up. With this approach, the DTPM framework has a very few material to export. Signed-off-by: Daniel Lezcano Reviewed-by: Ulf Hansson Link: https://lore.kernel.org/r/20220128163537.212248-3-daniel.lezcano@linaro.org --- drivers/powercap/Kconfig | 1 + drivers/powercap/dtpm.c | 196 +++++++++++++++++++++++++++++++++++++-- include/linux/dtpm.h | 15 +++ 3 files changed, 206 insertions(+), 6 deletions(-) diff --git a/drivers/powercap/Kconfig b/drivers/powercap/Kconfig index 8242e8c5ed77..b1ca339957e3 100644 --- a/drivers/powercap/Kconfig +++ b/drivers/powercap/Kconfig @@ -46,6 +46,7 @@ config IDLE_INJECT config DTPM bool "Power capping for Dynamic Thermal Power Management (EXPERIMENTAL)" + depends on OF help This enables support for the power capping for the dynamic thermal power management userspace engine. diff --git a/drivers/powercap/dtpm.c b/drivers/powercap/dtpm.c index 0e5c93443c70..414826a1509b 100644 --- a/drivers/powercap/dtpm.c +++ b/drivers/powercap/dtpm.c @@ -23,6 +23,7 @@ #include #include #include +#include #include "dtpm_subsys.h" @@ -463,14 +464,197 @@ int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent) return 0; } -static int __init init_dtpm(void) +static struct dtpm *dtpm_setup_virtual(const struct dtpm_node *hierarchy, + struct dtpm *parent) { - pct = powercap_register_control_type(NULL, "dtpm", NULL); - if (IS_ERR(pct)) { - pr_err("Failed to register control type\n"); - return PTR_ERR(pct); + struct dtpm *dtpm; + int ret; + + dtpm = kzalloc(sizeof(*dtpm), GFP_KERNEL); + if (!dtpm) + return ERR_PTR(-ENOMEM); + dtpm_init(dtpm, NULL); + + ret = dtpm_register(hierarchy->name, dtpm, parent); + if (ret) { + pr_err("Failed to register dtpm node '%s': %d\n", + hierarchy->name, ret); + kfree(dtpm); + return ERR_PTR(ret); + } + + return dtpm; +} + +static struct dtpm *dtpm_setup_dt(const struct dtpm_node *hierarchy, + struct dtpm *parent) +{ + struct device_node *np; + int i, ret; + + np = of_find_node_by_path(hierarchy->name); + if (!np) { + pr_err("Failed to find '%s'\n", hierarchy->name); + return ERR_PTR(-ENXIO); + } + + for (i = 0; i < ARRAY_SIZE(dtpm_subsys); i++) { + + if (!dtpm_subsys[i]->setup) + continue; + + ret = dtpm_subsys[i]->setup(parent, np); + if (ret) { + pr_err("Failed to setup '%s': %d\n", dtpm_subsys[i]->name, ret); + of_node_put(np); + return ERR_PTR(ret); + } + } + + of_node_put(np); + + /* + * By returning a NULL pointer, we let know the caller there + * is no child for us as we are a leaf of the tree + */ + return NULL; +} + +typedef struct dtpm * (*dtpm_node_callback_t)(const struct dtpm_node *, struct dtpm *); + +dtpm_node_callback_t dtpm_node_callback[] = { + [DTPM_NODE_VIRTUAL] = dtpm_setup_virtual, + [DTPM_NODE_DT] = dtpm_setup_dt, +}; + +static int dtpm_for_each_child(const struct dtpm_node *hierarchy, + const struct dtpm_node *it, struct dtpm *parent) +{ + struct dtpm *dtpm; + int i, ret; + + for (i = 0; hierarchy[i].name; i++) { + + if (hierarchy[i].parent != it) + continue; + + dtpm = dtpm_node_callback[hierarchy[i].type](&hierarchy[i], parent); + + /* + * A NULL pointer means there is no children, hence we + * continue without going deeper in the recursivity. + */ + if (!dtpm) + continue; + + /* + * There are multiple reasons why the callback could + * fail. The generic glue is abstracting the backend + * and therefore it is not possible to report back or + * take a decision based on the error. In any case, + * if this call fails, it is not critical in the + * hierarchy creation, we can assume the underlying + * service is not found, so we continue without this + * branch in the tree but with a warning to log the + * information the node was not created. + */ + if (IS_ERR(dtpm)) { + pr_warn("Failed to create '%s' in the hierarchy\n", + hierarchy[i].name); + continue; + } + + ret = dtpm_for_each_child(hierarchy, &hierarchy[i], dtpm); + if (ret) + return ret; } return 0; } -late_initcall(init_dtpm); + +/** + * dtpm_create_hierarchy - Create the dtpm hierarchy + * @hierarchy: An array of struct dtpm_node describing the hierarchy + * + * The function is called by the platform specific code with the + * description of the different node in the hierarchy. It creates the + * tree in the sysfs filesystem under the powercap dtpm entry. + * + * The expected tree has the format: + * + * struct dtpm_node hierarchy[] = { + * [0] { .name = "topmost", type = DTPM_NODE_VIRTUAL }, + * [1] { .name = "package", .type = DTPM_NODE_VIRTUAL, .parent = &hierarchy[0] }, + * [2] { .name = "/cpus/cpu0", .type = DTPM_NODE_DT, .parent = &hierarchy[1] }, + * [3] { .name = "/cpus/cpu1", .type = DTPM_NODE_DT, .parent = &hierarchy[1] }, + * [4] { .name = "/cpus/cpu2", .type = DTPM_NODE_DT, .parent = &hierarchy[1] }, + * [5] { .name = "/cpus/cpu3", .type = DTPM_NODE_DT, .parent = &hierarchy[1] }, + * [6] { } + * }; + * + * The last element is always an empty one and marks the end of the + * array. + * + * Return: zero on success, a negative value in case of error. Errors + * are reported back from the underlying functions. + */ +int dtpm_create_hierarchy(struct of_device_id *dtpm_match_table) +{ + const struct of_device_id *match; + const struct dtpm_node *hierarchy; + struct device_node *np; + int i, ret; + + if (pct) + return -EBUSY; + + pct = powercap_register_control_type(NULL, "dtpm", NULL); + if (IS_ERR(pct)) { + pr_err("Failed to register control type\n"); + ret = PTR_ERR(pct); + goto out_pct; + } + + ret = -ENODEV; + np = of_find_node_by_path("/"); + if (!np) + goto out_err; + + match = of_match_node(dtpm_match_table, np); + + of_node_put(np); + + if (!match) + goto out_err; + + hierarchy = match->data; + if (!hierarchy) { + ret = -EFAULT; + goto out_err; + } + + ret = dtpm_for_each_child(hierarchy, NULL, NULL); + if (ret) + goto out_err; + + for (i = 0; i < ARRAY_SIZE(dtpm_subsys); i++) { + + if (!dtpm_subsys[i]->init) + continue; + + ret = dtpm_subsys[i]->init(); + if (ret) + pr_info("Failed to initialze '%s': %d", + dtpm_subsys[i]->name, ret); + } + + return 0; + +out_err: + powercap_unregister_control_type(pct); +out_pct: + pct = NULL; + + return ret; +} +EXPORT_SYMBOL_GPL(dtpm_create_hierarchy); diff --git a/include/linux/dtpm.h b/include/linux/dtpm.h index 506048158a50..f7a25c70dd4c 100644 --- a/include/linux/dtpm.h +++ b/include/linux/dtpm.h @@ -32,9 +32,23 @@ struct dtpm_ops { void (*release)(struct dtpm *); }; +struct device_node; + struct dtpm_subsys_ops { const char *name; int (*init)(void); + int (*setup)(struct dtpm *, struct device_node *); +}; + +enum DTPM_NODE_TYPE { + DTPM_NODE_VIRTUAL = 0, + DTPM_NODE_DT, +}; + +struct dtpm_node { + enum DTPM_NODE_TYPE type; + const char *name; + struct dtpm_node *parent; }; static inline struct dtpm *to_dtpm(struct powercap_zone *zone) @@ -52,4 +66,5 @@ void dtpm_unregister(struct dtpm *dtpm); int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent); +int dtpm_create_hierarchy(struct of_device_id *dtpm_match_table); #endif From 73dbcb6e37bf0c43bac8c15fe5bcab2bec2367fb Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 28 Jan 2022 17:35:35 +0100 Subject: [PATCH 065/294] powercap/drivers/dtpm: Add CPU DT initialization support Based on the previous DT changes in the core code, use the 'setup' callback to initialize the CPU DTPM backend. Code is reorganized to stick to the DTPM table description. No functional changes. Signed-off-by: Daniel Lezcano Reviewed-by: Ulf Hansson Link: https://lore.kernel.org/r/20220128163537.212248-4-daniel.lezcano@linaro.org --- drivers/powercap/dtpm_cpu.c | 36 ++++++++++++++++++++++++++++++------ 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/drivers/powercap/dtpm_cpu.c b/drivers/powercap/dtpm_cpu.c index 5763e0ce2af5..eed5ad688d46 100644 --- a/drivers/powercap/dtpm_cpu.c +++ b/drivers/powercap/dtpm_cpu.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -176,6 +177,17 @@ static int cpuhp_dtpm_cpu_offline(unsigned int cpu) } static int cpuhp_dtpm_cpu_online(unsigned int cpu) +{ + struct dtpm_cpu *dtpm_cpu; + + dtpm_cpu = per_cpu(dtpm_per_cpu, cpu); + if (dtpm_cpu) + return dtpm_update_power(&dtpm_cpu->dtpm); + + return 0; +} + +static int __dtpm_cpu_setup(int cpu, struct dtpm *parent) { struct dtpm_cpu *dtpm_cpu; struct cpufreq_policy *policy; @@ -183,6 +195,10 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu) char name[CPUFREQ_NAME_LEN]; int ret = -ENOMEM; + dtpm_cpu = per_cpu(dtpm_per_cpu, cpu); + if (dtpm_cpu) + return 0; + policy = cpufreq_cpu_get(cpu); if (!policy) return 0; @@ -191,10 +207,6 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu) if (!pd) return -EINVAL; - dtpm_cpu = per_cpu(dtpm_per_cpu, cpu); - if (dtpm_cpu) - return dtpm_update_power(&dtpm_cpu->dtpm); - dtpm_cpu = kzalloc(sizeof(*dtpm_cpu), GFP_KERNEL); if (!dtpm_cpu) return -ENOMEM; @@ -207,7 +219,7 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu) snprintf(name, sizeof(name), "cpu%d-cpufreq", dtpm_cpu->cpu); - ret = dtpm_register(name, &dtpm_cpu->dtpm, NULL); + ret = dtpm_register(name, &dtpm_cpu->dtpm, parent); if (ret) goto out_kfree_dtpm_cpu; @@ -231,7 +243,18 @@ static int cpuhp_dtpm_cpu_online(unsigned int cpu) return ret; } -static int __init dtpm_cpu_init(void) +static int dtpm_cpu_setup(struct dtpm *dtpm, struct device_node *np) +{ + int cpu; + + cpu = of_cpu_node_to_id(np); + if (cpu < 0) + return 0; + + return __dtpm_cpu_setup(cpu, dtpm); +} + +static int dtpm_cpu_init(void) { int ret; @@ -272,4 +295,5 @@ static int __init dtpm_cpu_init(void) struct dtpm_subsys_ops dtpm_cpu_ops = { .name = KBUILD_MODNAME, .init = dtpm_cpu_init, + .setup = dtpm_cpu_setup, }; From e446556173170e675a7a321e76ce5fa3587de724 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 28 Jan 2022 17:35:36 +0100 Subject: [PATCH 066/294] powercap/drivers/dtpm: Add dtpm devfreq with energy model support Currently the dtpm supports the CPUs via cpufreq and the energy model. This change provides the same for the device which supports devfreq. Each device supporting devfreq and having an energy model can be added to the hierarchy. The concept is the same as the cpufreq DTPM support: the QoS is used to aggregate the requests and the energy model gives the value of the instantaneous power consumption ponderated by the load of the device. Cc: Chanwoo Choi Cc: Lukasz Luba Cc: Kyungmin Park Cc: MyungJoo Ham Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20220128163537.212248-5-daniel.lezcano@linaro.org --- drivers/powercap/Kconfig | 7 ++ drivers/powercap/Makefile | 1 + drivers/powercap/dtpm_devfreq.c | 203 ++++++++++++++++++++++++++++++++ drivers/powercap/dtpm_subsys.h | 4 + 4 files changed, 215 insertions(+) create mode 100644 drivers/powercap/dtpm_devfreq.c diff --git a/drivers/powercap/Kconfig b/drivers/powercap/Kconfig index b1ca339957e3..515e3ceb3393 100644 --- a/drivers/powercap/Kconfig +++ b/drivers/powercap/Kconfig @@ -57,4 +57,11 @@ config DTPM_CPU help This enables support for CPU power limitation based on energy model. + +config DTPM_DEVFREQ + bool "Add device power capping based on the energy model" + depends on DTPM && ENERGY_MODEL + help + This enables support for device power limitation based on + energy model. endif diff --git a/drivers/powercap/Makefile b/drivers/powercap/Makefile index fabcf388a8d3..494617cdad88 100644 --- a/drivers/powercap/Makefile +++ b/drivers/powercap/Makefile @@ -1,6 +1,7 @@ # SPDX-License-Identifier: GPL-2.0-only obj-$(CONFIG_DTPM) += dtpm.o obj-$(CONFIG_DTPM_CPU) += dtpm_cpu.o +obj-$(CONFIG_DTPM_DEVFREQ) += dtpm_devfreq.o obj-$(CONFIG_POWERCAP) += powercap_sys.o obj-$(CONFIG_INTEL_RAPL_CORE) += intel_rapl_common.o obj-$(CONFIG_INTEL_RAPL) += intel_rapl_msr.o diff --git a/drivers/powercap/dtpm_devfreq.c b/drivers/powercap/dtpm_devfreq.c new file mode 100644 index 000000000000..91276761a31d --- /dev/null +++ b/drivers/powercap/dtpm_devfreq.c @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2021 Linaro Limited + * + * Author: Daniel Lezcano + * + * The devfreq device combined with the energy model and the load can + * give an estimation of the power consumption as well as limiting the + * power. + * + */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +struct dtpm_devfreq { + struct dtpm dtpm; + struct dev_pm_qos_request qos_req; + struct devfreq *devfreq; +}; + +static struct dtpm_devfreq *to_dtpm_devfreq(struct dtpm *dtpm) +{ + return container_of(dtpm, struct dtpm_devfreq, dtpm); +} + +static int update_pd_power_uw(struct dtpm *dtpm) +{ + struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm); + struct devfreq *devfreq = dtpm_devfreq->devfreq; + struct device *dev = devfreq->dev.parent; + struct em_perf_domain *pd = em_pd_get(dev); + + dtpm->power_min = pd->table[0].power; + dtpm->power_min *= MICROWATT_PER_MILLIWATT; + + dtpm->power_max = pd->table[pd->nr_perf_states - 1].power; + dtpm->power_max *= MICROWATT_PER_MILLIWATT; + + return 0; +} + +static u64 set_pd_power_limit(struct dtpm *dtpm, u64 power_limit) +{ + struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm); + struct devfreq *devfreq = dtpm_devfreq->devfreq; + struct device *dev = devfreq->dev.parent; + struct em_perf_domain *pd = em_pd_get(dev); + unsigned long freq; + u64 power; + int i; + + for (i = 0; i < pd->nr_perf_states; i++) { + + power = pd->table[i].power * MICROWATT_PER_MILLIWATT; + if (power > power_limit) + break; + } + + freq = pd->table[i - 1].frequency; + + dev_pm_qos_update_request(&dtpm_devfreq->qos_req, freq); + + power_limit = pd->table[i - 1].power * MICROWATT_PER_MILLIWATT; + + return power_limit; +} + +static void _normalize_load(struct devfreq_dev_status *status) +{ + if (status->total_time > 0xfffff) { + status->total_time >>= 10; + status->busy_time >>= 10; + } + + status->busy_time <<= 10; + status->busy_time /= status->total_time ? : 1; + + status->busy_time = status->busy_time ? : 1; + status->total_time = 1024; +} + +static u64 get_pd_power_uw(struct dtpm *dtpm) +{ + struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm); + struct devfreq *devfreq = dtpm_devfreq->devfreq; + struct device *dev = devfreq->dev.parent; + struct em_perf_domain *pd = em_pd_get(dev); + struct devfreq_dev_status status; + unsigned long freq; + u64 power; + int i; + + mutex_lock(&devfreq->lock); + status = devfreq->last_status; + mutex_unlock(&devfreq->lock); + + freq = DIV_ROUND_UP(status.current_frequency, HZ_PER_KHZ); + _normalize_load(&status); + + for (i = 0; i < pd->nr_perf_states; i++) { + + if (pd->table[i].frequency < freq) + continue; + + power = pd->table[i].power * MICROWATT_PER_MILLIWATT; + power *= status.busy_time; + power >>= 10; + + return power; + } + + return 0; +} + +static void pd_release(struct dtpm *dtpm) +{ + struct dtpm_devfreq *dtpm_devfreq = to_dtpm_devfreq(dtpm); + + if (dev_pm_qos_request_active(&dtpm_devfreq->qos_req)) + dev_pm_qos_remove_request(&dtpm_devfreq->qos_req); + + kfree(dtpm_devfreq); +} + +static struct dtpm_ops dtpm_ops = { + .set_power_uw = set_pd_power_limit, + .get_power_uw = get_pd_power_uw, + .update_power_uw = update_pd_power_uw, + .release = pd_release, +}; + +static int __dtpm_devfreq_setup(struct devfreq *devfreq, struct dtpm *parent) +{ + struct device *dev = devfreq->dev.parent; + struct dtpm_devfreq *dtpm_devfreq; + struct em_perf_domain *pd; + int ret = -ENOMEM; + + pd = em_pd_get(dev); + if (!pd) { + ret = dev_pm_opp_of_register_em(dev, NULL); + if (ret) { + pr_err("No energy model available for '%s'\n", dev_name(dev)); + return -EINVAL; + } + } + + dtpm_devfreq = kzalloc(sizeof(*dtpm_devfreq), GFP_KERNEL); + if (!dtpm_devfreq) + return -ENOMEM; + + dtpm_init(&dtpm_devfreq->dtpm, &dtpm_ops); + + dtpm_devfreq->devfreq = devfreq; + + ret = dtpm_register(dev_name(dev), &dtpm_devfreq->dtpm, parent); + if (ret) { + pr_err("Failed to register '%s': %d\n", dev_name(dev), ret); + kfree(dtpm_devfreq); + return ret; + } + + ret = dev_pm_qos_add_request(dev, &dtpm_devfreq->qos_req, + DEV_PM_QOS_MAX_FREQUENCY, + PM_QOS_MAX_FREQUENCY_DEFAULT_VALUE); + if (ret) { + pr_err("Failed to add QoS request: %d\n", ret); + goto out_dtpm_unregister; + } + + dtpm_update_power(&dtpm_devfreq->dtpm); + + return 0; + +out_dtpm_unregister: + dtpm_unregister(&dtpm_devfreq->dtpm); + + return ret; +} + +static int dtpm_devfreq_setup(struct dtpm *dtpm, struct device_node *np) +{ + struct devfreq *devfreq; + + devfreq = devfreq_get_devfreq_by_node(np); + if (IS_ERR(devfreq)) + return 0; + + return __dtpm_devfreq_setup(devfreq, dtpm); +} + +struct dtpm_subsys_ops dtpm_devfreq_ops = { + .name = KBUILD_MODNAME, + .setup = dtpm_devfreq_setup, +}; diff --git a/drivers/powercap/dtpm_subsys.h b/drivers/powercap/dtpm_subsys.h index 2a3a2055f60e..db1712938a96 100644 --- a/drivers/powercap/dtpm_subsys.h +++ b/drivers/powercap/dtpm_subsys.h @@ -8,11 +8,15 @@ #define ___DTPM_SUBSYS_H__ extern struct dtpm_subsys_ops dtpm_cpu_ops; +extern struct dtpm_subsys_ops dtpm_devfreq_ops; struct dtpm_subsys_ops *dtpm_subsys[] = { #ifdef CONFIG_DTPM_CPU &dtpm_cpu_ops, #endif +#ifdef CONFIG_DTPM_DEVFREQ + &dtpm_devfreq_ops, +#endif }; #endif From b9d6c47a2be8d273ecc063afda6e9fd66a35116d Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Fri, 28 Jan 2022 17:35:37 +0100 Subject: [PATCH 067/294] rockchip/soc/drivers: Add DTPM description for rk3399 The DTPM framework does support now the hierarchy description. The platform specific code can call the hierarchy creation function with an array of struct dtpm_node pointing to their parent. This patch provides a description of the big / Little CPUs and the GPU and tie them together under a virtual 'package' name. Only rk3399 is described now. The description could be extended in the future with the memory controller with devfreq. The description is always a module and it describes the soft dependencies. The userspace has to load the softdeps module in the right order. Signed-off-by: Daniel Lezcano Reviewed-by; Heiko Stuebner Link: https://lore.kernel.org/r/20220128163537.212248-6-daniel.lezcano@linaro.org --- drivers/soc/rockchip/Kconfig | 8 +++++ drivers/soc/rockchip/Makefile | 1 + drivers/soc/rockchip/dtpm.c | 59 +++++++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+) create mode 100644 drivers/soc/rockchip/dtpm.c diff --git a/drivers/soc/rockchip/Kconfig b/drivers/soc/rockchip/Kconfig index 25eb2c1e31bb..156ac0e0c8fe 100644 --- a/drivers/soc/rockchip/Kconfig +++ b/drivers/soc/rockchip/Kconfig @@ -34,4 +34,12 @@ config ROCKCHIP_PM_DOMAINS If unsure, say N. +config ROCKCHIP_DTPM + tristate "Rockchip DTPM hierarchy" + depends on DTPM && m + help + Describe the hierarchy for the Dynamic Thermal Power + Management tree on this platform. That will create all the + power capping capable devices. + endif diff --git a/drivers/soc/rockchip/Makefile b/drivers/soc/rockchip/Makefile index 875032f7344e..05f31a4e743c 100644 --- a/drivers/soc/rockchip/Makefile +++ b/drivers/soc/rockchip/Makefile @@ -5,3 +5,4 @@ obj-$(CONFIG_ROCKCHIP_GRF) += grf.o obj-$(CONFIG_ROCKCHIP_IODOMAIN) += io-domain.o obj-$(CONFIG_ROCKCHIP_PM_DOMAINS) += pm_domains.o +obj-$(CONFIG_ROCKCHIP_DTPM) += dtpm.o diff --git a/drivers/soc/rockchip/dtpm.c b/drivers/soc/rockchip/dtpm.c new file mode 100644 index 000000000000..ebebb748488b --- /dev/null +++ b/drivers/soc/rockchip/dtpm.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2021 Linaro Limited + * + * Author: Daniel Lezcano + * + * DTPM hierarchy description + */ +#include +#include +#include +#include + +static struct dtpm_node __initdata rk3399_hierarchy[] = { + [0]{ .name = "rk3399", + .type = DTPM_NODE_VIRTUAL }, + [1]{ .name = "package", + .type = DTPM_NODE_VIRTUAL, + .parent = &rk3399_hierarchy[0] }, + [2]{ .name = "/cpus/cpu@0", + .type = DTPM_NODE_DT, + .parent = &rk3399_hierarchy[1] }, + [3]{ .name = "/cpus/cpu@1", + .type = DTPM_NODE_DT, + .parent = &rk3399_hierarchy[1] }, + [4]{ .name = "/cpus/cpu@2", + .type = DTPM_NODE_DT, + .parent = &rk3399_hierarchy[1] }, + [5]{ .name = "/cpus/cpu@3", + .type = DTPM_NODE_DT, + .parent = &rk3399_hierarchy[1] }, + [6]{ .name = "/cpus/cpu@100", + .type = DTPM_NODE_DT, + .parent = &rk3399_hierarchy[1] }, + [7]{ .name = "/cpus/cpu@101", + .type = DTPM_NODE_DT, + .parent = &rk3399_hierarchy[1] }, + [8]{ .name = "/gpu@ff9a0000", + .type = DTPM_NODE_DT, + .parent = &rk3399_hierarchy[1] }, + [9]{ /* sentinel */ } +}; + +static struct of_device_id __initdata rockchip_dtpm_match_table[] = { + { .compatible = "rockchip,rk3399", .data = rk3399_hierarchy }, + {}, +}; + +static int __init rockchip_dtpm_init(void) +{ + return dtpm_create_hierarchy(rockchip_dtpm_match_table); +} +module_init(rockchip_dtpm_init); + +MODULE_SOFTDEP("pre: panfrost cpufreq-dt"); +MODULE_DESCRIPTION("Rockchip DTPM driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:dtpm"); +MODULE_AUTHOR("Daniel Lezcano Date: Wed, 26 Jan 2022 09:48:53 +0800 Subject: [PATCH 068/294] thermal: int340x: Check for NULL after calling kmemdup() As the potential failure of the allocation, kmemdup() may return NULL. Then, 'bin_attr_data_vault.private' will be NULL, but 'bin_attr_data_vault.size' is not 0, which is not consistent. Therefore, it is better to check the return value of kmemdup() to avoid the confusion. Fixes: 0ba13c763aac ("thermal/int340x_thermal: Export GDDV") Signed-off-by: Jiasheng Jiang [ rjw: Subject and changelog edits ] Signed-off-by: Rafael J. Wysocki --- drivers/thermal/intel/int340x_thermal/int3400_thermal.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c index 72acb1f61849..82de8bcd368f 100644 --- a/drivers/thermal/intel/int340x_thermal/int3400_thermal.c +++ b/drivers/thermal/intel/int340x_thermal/int3400_thermal.c @@ -464,6 +464,11 @@ static void int3400_setup_gddv(struct int3400_thermal_priv *priv) priv->data_vault = kmemdup(obj->package.elements[0].buffer.pointer, obj->package.elements[0].buffer.length, GFP_KERNEL); + if (!priv->data_vault) { + kfree(buffer.pointer); + return; + } + bin_attr_data_vault.private = priv->data_vault; bin_attr_data_vault.size = obj->package.elements[0].buffer.length; kfree(buffer.pointer); From ae26508651272695a3ab353f75ab9a8daf3da324 Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Sun, 23 Jan 2022 20:45:06 +0800 Subject: [PATCH 069/294] cpufreq: Move to_gov_attr_set() to cpufreq.h So it can be reused by other codes. Signed-off-by: Kevin Hao Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq_governor_attr_set.c | 5 ----- include/linux/cpufreq.h | 5 +++++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/cpufreq/cpufreq_governor_attr_set.c b/drivers/cpufreq/cpufreq_governor_attr_set.c index a6f365b9cc1a..771770ea0ed0 100644 --- a/drivers/cpufreq/cpufreq_governor_attr_set.c +++ b/drivers/cpufreq/cpufreq_governor_attr_set.c @@ -8,11 +8,6 @@ #include "cpufreq_governor.h" -static inline struct gov_attr_set *to_gov_attr_set(struct kobject *kobj) -{ - return container_of(kobj, struct gov_attr_set, kobj); -} - static inline struct governor_attr *to_gov_attr(struct attribute *attr) { return container_of(attr, struct governor_attr, attr); diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 1ab29e61b078..f0dfc0b260ec 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -658,6 +658,11 @@ struct gov_attr_set { /* sysfs ops for cpufreq governors */ extern const struct sysfs_ops governor_sysfs_ops; +static inline struct gov_attr_set *to_gov_attr_set(struct kobject *kobj) +{ + return container_of(kobj, struct gov_attr_set, kobj); +} + void gov_attr_set_init(struct gov_attr_set *attr_set, struct list_head *list_node); void gov_attr_set_get(struct gov_attr_set *attr_set, struct list_head *list_node); unsigned int gov_attr_set_put(struct gov_attr_set *attr_set, struct list_head *list_node); From 53725c4cbd4567423ff6143c5d10300e53ecf52a Mon Sep 17 00:00:00 2001 From: Kevin Hao Date: Sun, 23 Jan 2022 20:45:07 +0800 Subject: [PATCH 070/294] cpufreq: schedutil: Use to_gov_attr_set() to get the gov_attr_set The to_gov_attr_set() has been moved to the cpufreq.h, so use it to get the gov_attr_set. Signed-off-by: Kevin Hao Signed-off-by: Rafael J. Wysocki --- kernel/sched/cpufreq_schedutil.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index 26778884d9ab..cffcd08f4ec8 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -539,7 +539,7 @@ ATTRIBUTE_GROUPS(sugov); static void sugov_tunables_free(struct kobject *kobj) { - struct gov_attr_set *attr_set = container_of(kobj, struct gov_attr_set, kobj); + struct gov_attr_set *attr_set = to_gov_attr_set(kobj); kfree(to_sugov_tunables(attr_set)); } From a11cda8e2f184e35e42bbb96a27272b3c5e801d2 Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Sun, 23 Jan 2022 10:38:34 -0800 Subject: [PATCH 071/294] thermal: intel_powerclamp: don't use bitmap_weight() in end_power_clamp() Don't call bitmap_weight() if the following code can get by without it. Signed-off-by: Yury Norov Signed-off-by: Rafael J. Wysocki --- drivers/thermal/intel/intel_powerclamp.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/drivers/thermal/intel/intel_powerclamp.c b/drivers/thermal/intel/intel_powerclamp.c index 14256421d98c..c841ab37e7c6 100644 --- a/drivers/thermal/intel/intel_powerclamp.c +++ b/drivers/thermal/intel/intel_powerclamp.c @@ -556,12 +556,9 @@ static void end_power_clamp(void) * stop faster. */ clamping = false; - if (bitmap_weight(cpu_clamping_mask, num_possible_cpus())) { - for_each_set_bit(i, cpu_clamping_mask, num_possible_cpus()) { - pr_debug("clamping worker for cpu %d alive, destroy\n", - i); - stop_power_clamp_worker(i); - } + for_each_set_bit(i, cpu_clamping_mask, num_possible_cpus()) { + pr_debug("clamping worker for cpu %d alive, destroy\n", i); + stop_power_clamp_worker(i); } } From 7ddf5e37631ac7a96920f0f8aa3c8c4c289aaa25 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 26 Jan 2022 20:43:59 +0100 Subject: [PATCH 072/294] cpufreq: longhaul: Replace acpi_bus_get_device() Replace acpi_bus_get_device() that is going to be dropped with acpi_fetch_acpi_dev(). No intentional functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/longhaul.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/longhaul.c b/drivers/cpufreq/longhaul.c index c538a153ee82..3e000e1a75c6 100644 --- a/drivers/cpufreq/longhaul.c +++ b/drivers/cpufreq/longhaul.c @@ -668,9 +668,9 @@ static acpi_status longhaul_walk_callback(acpi_handle obj_handle, u32 nesting_level, void *context, void **return_value) { - struct acpi_device *d; + struct acpi_device *d = acpi_fetch_acpi_dev(obj_handle); - if (acpi_bus_get_device(obj_handle, &d)) + if (!d) return 0; *return_value = acpi_driver_data(d); From 098c874e20be2a4cee3021aa9b3485ed5e1f4d5b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 26 Jan 2022 20:45:17 +0100 Subject: [PATCH 073/294] thermal: Replace acpi_bus_get_device() Replace acpi_bus_get_device() that is going to be dropped with acpi_fetch_acpi_dev(). No intentional functional impact. Signed-off-by: Rafael J. Wysocki --- .../intel/int340x_thermal/acpi_thermal_rel.c | 23 ++++++------------- 1 file changed, 7 insertions(+), 16 deletions(-) diff --git a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c index e90690a234c4..01b80331eab6 100644 --- a/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c +++ b/drivers/thermal/intel/int340x_thermal/acpi_thermal_rel.c @@ -72,7 +72,6 @@ int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp, int i; int nr_bad_entries = 0; struct trt *trts; - struct acpi_device *adev; union acpi_object *p; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer element = { 0, NULL }; @@ -112,12 +111,10 @@ int acpi_parse_trt(acpi_handle handle, int *trt_count, struct trt **trtp, if (!create_dev) continue; - result = acpi_bus_get_device(trt->source, &adev); - if (result) + if (!acpi_fetch_acpi_dev(trt->source)) pr_warn("Failed to get source ACPI device\n"); - result = acpi_bus_get_device(trt->target, &adev); - if (result) + if (!acpi_fetch_acpi_dev(trt->target)) pr_warn("Failed to get target ACPI device\n"); } @@ -149,7 +146,6 @@ int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp, int i; int nr_bad_entries = 0; struct art *arts; - struct acpi_device *adev; union acpi_object *p; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_buffer element = { 0, NULL }; @@ -191,16 +187,11 @@ int acpi_parse_art(acpi_handle handle, int *art_count, struct art **artp, if (!create_dev) continue; - if (art->source) { - result = acpi_bus_get_device(art->source, &adev); - if (result) - pr_warn("Failed to get source ACPI device\n"); - } - if (art->target) { - result = acpi_bus_get_device(art->target, &adev); - if (result) - pr_warn("Failed to get target ACPI device\n"); - } + if (!acpi_fetch_acpi_dev(art->source)) + pr_warn("Failed to get source ACPI device\n"); + + if (!acpi_fetch_acpi_dev(art->target)) + pr_warn("Failed to get target ACPI device\n"); } *artp = arts; From ed945296bb90ce79a7ff331885dd37eafa38b551 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 26 Jan 2022 20:51:27 +0100 Subject: [PATCH 074/294] PNP: Replace acpi_bus_get_device() Replace acpi_bus_get_device() that is going to be dropped with acpi_fetch_acpi_dev(). No intentional functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/pnp/pnpacpi/core.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index afaf30a3622c..38928ff7472b 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -287,9 +287,9 @@ static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle, u32 lvl, void *context, void **rv) { - struct acpi_device *device; + struct acpi_device *device = acpi_fetch_acpi_dev(handle); - if (acpi_bus_get_device(handle, &device)) + if (!device) return AE_CTRL_DEPTH; if (acpi_is_pnp_device(device)) pnpacpi_add_device(device); From 783dedf41b79ac7a3a68b51cf6f88cbfd6dc3292 Mon Sep 17 00:00:00 2001 From: Robert Kiraly Date: Thu, 27 Jan 2022 02:15:23 -0800 Subject: [PATCH 075/294] ACPI: tables: Add CEDT signature to the list of known tables Add ACPI_SIG_CEDT to table_sigs[] in "drivers/acpi/tables.c". Signed-off-by: Robert Kiraly [ rjw: Rebase, new subject and changelog ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/tables.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/acpi/tables.c b/drivers/acpi/tables.c index 0741a4933f62..2d7ed7126faa 100644 --- a/drivers/acpi/tables.c +++ b/drivers/acpi/tables.c @@ -545,7 +545,7 @@ static const char table_sigs[][ACPI_NAMESEG_SIZE] __initconst = { ACPI_SIG_WDDT, ACPI_SIG_WDRT, ACPI_SIG_DSDT, ACPI_SIG_FADT, ACPI_SIG_PSDT, ACPI_SIG_RSDT, ACPI_SIG_XSDT, ACPI_SIG_SSDT, ACPI_SIG_IORT, ACPI_SIG_NFIT, ACPI_SIG_HMAT, ACPI_SIG_PPTT, - ACPI_SIG_NHLT, ACPI_SIG_AEST }; + ACPI_SIG_NHLT, ACPI_SIG_AEST, ACPI_SIG_CEDT }; #define ACPI_HEADER_SIZE sizeof(struct acpi_table_header) From 345be4275cad454ae7e25884369a9c6c25e56279 Mon Sep 17 00:00:00 2001 From: Nathan Chancellor Date: Mon, 7 Feb 2022 09:38:29 -0700 Subject: [PATCH 076/294] thermal: netlink: Fix parameter type of thermal_genl_cpu_capability_event() stub When building with CONFIG_THERMAL_NETLINK=n, there is a spew of warnings along the lines of: In file included from drivers/thermal/thermal_core.c:27: In file included from drivers/thermal/thermal_core.h:15: drivers/thermal/thermal_netlink.h:113:71: warning: declaration of 'struct cpu_capability' will not be visible outside of this function [-Wvisibility] static inline int thermal_genl_cpu_capability_event(int count, struct cpu_capability *caps) ^ 1 warning generated. 'struct cpu_capability' is not forward declared anywhere in the header. As it turns out, this should really be 'struct thermal_genl_cpu_caps', which silences the warning and makes the parameter types of the stub match the full function. Fixes: e4b1eb24ce5a ("thermal: netlink: Add a new event to notify CPU capabilities change") Reported-by: Stephen Rothwell Signed-off-by: Nathan Chancellor Reviewed-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki --- drivers/thermal/thermal_netlink.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/thermal/thermal_netlink.h b/drivers/thermal/thermal_netlink.h index 04d1adbbc012..1052f523188d 100644 --- a/drivers/thermal/thermal_netlink.h +++ b/drivers/thermal/thermal_netlink.h @@ -110,7 +110,7 @@ static inline int thermal_genl_sampling_temp(int id, int temp) return 0; } -static inline int thermal_genl_cpu_capability_event(int count, struct cpu_capability *caps) +static inline int thermal_genl_cpu_capability_event(int count, struct thermal_genl_cpu_caps *caps) { return 0; } From 82980b1622d97017053c6792382469d7dc26a486 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Tue, 16 Feb 2021 15:04:34 +0000 Subject: [PATCH 077/294] rcu: Kill rnp->ofl_seq and use only rcu_state.ofl_lock for exclusion If we allow architectures to bring APs online in parallel, then we end up requiring rcu_cpu_starting() to be reentrant. But currently, the manipulation of rnp->ofl_seq is not thread-safe. However, rnp->ofl_seq is also fairly much pointless anyway since both rcu_cpu_starting() and rcu_report_dead() hold rcu_state.ofl_lock for fairly much the whole time that rnp->ofl_seq is set to an odd number to indicate that an operation is in progress. So drop rnp->ofl_seq completely, and use only rcu_state.ofl_lock. This has a couple of minor complexities: lockdep will complain when we take rcu_state.ofl_lock, and currently accepts the 'excuse' of having an odd value in rnp->ofl_seq. So switch it to an arch_spinlock_t to avoid that false positive complaint. Since we're killing rnp->ofl_seq of course that 'excuse' has to be changed too, so make it check for arch_spin_is_locked(rcu_state.ofl_lock). There's no arch_spin_lock_irqsave() so we have to manually save and restore local interrupts around the locking. At Paul's request based on Neeraj's analysis, make rcu_gp_init not just wait but *exclude* any CPU online/offline activity, which was fairly much true already by virtue of it holding rcu_state.ofl_lock. Signed-off-by: David Woodhouse Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 71 ++++++++++++++++++++++++----------------------- kernel/rcu/tree.h | 4 +-- 2 files changed, 37 insertions(+), 38 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index a4c25a6283b0..73a4c9d07b86 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -91,7 +91,7 @@ static struct rcu_state rcu_state = { .abbr = RCU_ABBR, .exp_mutex = __MUTEX_INITIALIZER(rcu_state.exp_mutex), .exp_wake_mutex = __MUTEX_INITIALIZER(rcu_state.exp_wake_mutex), - .ofl_lock = __RAW_SPIN_LOCK_UNLOCKED(rcu_state.ofl_lock), + .ofl_lock = __ARCH_SPIN_LOCK_UNLOCKED, }; /* Dump rcu_node combining tree at boot to verify correct setup. */ @@ -1175,7 +1175,15 @@ bool rcu_lockdep_current_cpu_online(void) preempt_disable_notrace(); rdp = this_cpu_ptr(&rcu_data); rnp = rdp->mynode; - if (rdp->grpmask & rcu_rnp_online_cpus(rnp) || READ_ONCE(rnp->ofl_seq) & 0x1) + /* + * Strictly, we care here about the case where the current CPU is + * in rcu_cpu_starting() and thus has an excuse for rdp->grpmask + * not being up to date. So arch_spin_is_locked() might have a + * false positive if it's held by some *other* CPU, but that's + * OK because that just means a false *negative* on the warning. + */ + if (rdp->grpmask & rcu_rnp_online_cpus(rnp) || + arch_spin_is_locked(&rcu_state.ofl_lock)) ret = true; preempt_enable_notrace(); return ret; @@ -1739,7 +1747,6 @@ static void rcu_strict_gp_boundary(void *unused) */ static noinline_for_stack bool rcu_gp_init(void) { - unsigned long firstseq; unsigned long flags; unsigned long oldmask; unsigned long mask; @@ -1782,22 +1789,17 @@ static noinline_for_stack bool rcu_gp_init(void) * of RCU's Requirements documentation. */ WRITE_ONCE(rcu_state.gp_state, RCU_GP_ONOFF); + /* Exclude CPU hotplug operations. */ rcu_for_each_leaf_node(rnp) { - // Wait for CPU-hotplug operations that might have - // started before this grace period did. - smp_mb(); // Pair with barriers used when updating ->ofl_seq to odd values. - firstseq = READ_ONCE(rnp->ofl_seq); - if (firstseq & 0x1) - while (firstseq == READ_ONCE(rnp->ofl_seq)) - schedule_timeout_idle(1); // Can't wake unless RCU is watching. - smp_mb(); // Pair with barriers used when updating ->ofl_seq to even values. - raw_spin_lock(&rcu_state.ofl_lock); - raw_spin_lock_irq_rcu_node(rnp); + local_irq_save(flags); + arch_spin_lock(&rcu_state.ofl_lock); + raw_spin_lock_rcu_node(rnp); if (rnp->qsmaskinit == rnp->qsmaskinitnext && !rnp->wait_blkd_tasks) { /* Nothing to do on this leaf rcu_node structure. */ - raw_spin_unlock_irq_rcu_node(rnp); - raw_spin_unlock(&rcu_state.ofl_lock); + raw_spin_unlock_rcu_node(rnp); + arch_spin_unlock(&rcu_state.ofl_lock); + local_irq_restore(flags); continue; } @@ -1832,8 +1834,9 @@ static noinline_for_stack bool rcu_gp_init(void) rcu_cleanup_dead_rnp(rnp); } - raw_spin_unlock_irq_rcu_node(rnp); - raw_spin_unlock(&rcu_state.ofl_lock); + raw_spin_unlock_rcu_node(rnp); + arch_spin_unlock(&rcu_state.ofl_lock); + local_irq_restore(flags); } rcu_gp_slow(gp_preinit_delay); /* Races with CPU hotplug. */ @@ -4287,11 +4290,10 @@ void rcu_cpu_starting(unsigned int cpu) rnp = rdp->mynode; mask = rdp->grpmask; - WRITE_ONCE(rnp->ofl_seq, rnp->ofl_seq + 1); - WARN_ON_ONCE(!(rnp->ofl_seq & 0x1)); + local_irq_save(flags); + arch_spin_lock(&rcu_state.ofl_lock); rcu_dynticks_eqs_online(); - smp_mb(); // Pair with rcu_gp_cleanup()'s ->ofl_seq barrier(). - raw_spin_lock_irqsave_rcu_node(rnp, flags); + raw_spin_lock_rcu_node(rnp); WRITE_ONCE(rnp->qsmaskinitnext, rnp->qsmaskinitnext | mask); newcpu = !(rnp->expmaskinitnext & mask); rnp->expmaskinitnext |= mask; @@ -4304,15 +4306,18 @@ void rcu_cpu_starting(unsigned int cpu) /* An incoming CPU should never be blocking a grace period. */ if (WARN_ON_ONCE(rnp->qsmask & mask)) { /* RCU waiting on incoming CPU? */ + /* rcu_report_qs_rnp() *really* wants some flags to restore */ + unsigned long flags2; + + local_irq_save(flags2); rcu_disable_urgency_upon_qs(rdp); /* Report QS -after- changing ->qsmaskinitnext! */ - rcu_report_qs_rnp(mask, rnp, rnp->gp_seq, flags); + rcu_report_qs_rnp(mask, rnp, rnp->gp_seq, flags2); } else { - raw_spin_unlock_irqrestore_rcu_node(rnp, flags); + raw_spin_unlock_rcu_node(rnp); } - smp_mb(); // Pair with rcu_gp_cleanup()'s ->ofl_seq barrier(). - WRITE_ONCE(rnp->ofl_seq, rnp->ofl_seq + 1); - WARN_ON_ONCE(rnp->ofl_seq & 0x1); + arch_spin_unlock(&rcu_state.ofl_lock); + local_irq_restore(flags); smp_mb(); /* Ensure RCU read-side usage follows above initialization. */ } @@ -4326,7 +4331,7 @@ void rcu_cpu_starting(unsigned int cpu) */ void rcu_report_dead(unsigned int cpu) { - unsigned long flags; + unsigned long flags, seq_flags; unsigned long mask; struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu); struct rcu_node *rnp = rdp->mynode; /* Outgoing CPU's rdp & rnp. */ @@ -4340,10 +4345,8 @@ void rcu_report_dead(unsigned int cpu) /* Remove outgoing CPU from mask in the leaf rcu_node structure. */ mask = rdp->grpmask; - WRITE_ONCE(rnp->ofl_seq, rnp->ofl_seq + 1); - WARN_ON_ONCE(!(rnp->ofl_seq & 0x1)); - smp_mb(); // Pair with rcu_gp_cleanup()'s ->ofl_seq barrier(). - raw_spin_lock(&rcu_state.ofl_lock); + local_irq_save(seq_flags); + arch_spin_lock(&rcu_state.ofl_lock); raw_spin_lock_irqsave_rcu_node(rnp, flags); /* Enforce GP memory-order guarantee. */ rdp->rcu_ofl_gp_seq = READ_ONCE(rcu_state.gp_seq); rdp->rcu_ofl_gp_flags = READ_ONCE(rcu_state.gp_flags); @@ -4354,10 +4357,8 @@ void rcu_report_dead(unsigned int cpu) } WRITE_ONCE(rnp->qsmaskinitnext, rnp->qsmaskinitnext & ~mask); raw_spin_unlock_irqrestore_rcu_node(rnp, flags); - raw_spin_unlock(&rcu_state.ofl_lock); - smp_mb(); // Pair with rcu_gp_cleanup()'s ->ofl_seq barrier(). - WRITE_ONCE(rnp->ofl_seq, rnp->ofl_seq + 1); - WARN_ON_ONCE(rnp->ofl_seq & 0x1); + arch_spin_unlock(&rcu_state.ofl_lock); + local_irq_restore(seq_flags); rdp->cpu_started = false; } diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 486fc901bd08..4b4bcef8a974 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -56,8 +56,6 @@ struct rcu_node { /* Initialized from ->qsmaskinitnext at the */ /* beginning of each grace period. */ unsigned long qsmaskinitnext; - unsigned long ofl_seq; /* CPU-hotplug operation sequence count. */ - /* Online CPUs for next grace period. */ unsigned long expmask; /* CPUs or groups that need to check in */ /* to allow the current expedited GP */ /* to complete. */ @@ -355,7 +353,7 @@ struct rcu_state { const char *name; /* Name of structure. */ char abbr; /* Abbreviated name. */ - raw_spinlock_t ofl_lock ____cacheline_internodealigned_in_smp; + arch_spinlock_t ofl_lock ____cacheline_internodealigned_in_smp; /* Synchronize offline with */ /* GP pre-initialization. */ }; From 0cabb47af3cfaeb6007ba3868379bbd4daee64cc Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 10 Dec 2021 16:25:20 -0800 Subject: [PATCH 078/294] rcu: Refactor rcu_barrier() empty-list handling This commit saves a few lines by checking first for an empty callback list. If the callback list is empty, then that CPU is taken care of, regardless of its online or nocb state. Also simplify tracing accordingly and fold a few lines together. Signed-off-by: Paul E. McKenney --- include/trace/events/rcu.h | 9 ++++----- kernel/rcu/tree.c | 25 ++++++++----------------- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/include/trace/events/rcu.h b/include/trace/events/rcu.h index 670e41783edd..90b2fb0292cb 100644 --- a/include/trace/events/rcu.h +++ b/include/trace/events/rcu.h @@ -794,16 +794,15 @@ TRACE_EVENT_RCU(rcu_torture_read, * Tracepoint for rcu_barrier() execution. The string "s" describes * the rcu_barrier phase: * "Begin": rcu_barrier() started. + * "CB": An rcu_barrier_callback() invoked a callback, not the last. * "EarlyExit": rcu_barrier() piggybacked, thus early exit. * "Inc1": rcu_barrier() piggyback check counter incremented. - * "OfflineNoCBQ": rcu_barrier() found offline no-CBs CPU with callbacks. - * "OnlineQ": rcu_barrier() found online CPU with callbacks. - * "OnlineNQ": rcu_barrier() found online CPU, no callbacks. + * "Inc2": rcu_barrier() piggyback check counter incremented. * "IRQ": An rcu_barrier_callback() callback posted on remote CPU. * "IRQNQ": An rcu_barrier_callback() callback found no callbacks. - * "CB": An rcu_barrier_callback() invoked a callback, not the last. * "LastCB": An rcu_barrier_callback() invoked the last callback. - * "Inc2": rcu_barrier() piggyback check counter incremented. + * "NQ": rcu_barrier() found a CPU with no callbacks. + * "OnlineQ": rcu_barrier() found online CPU with callbacks. * The "cpu" argument is the CPU or -1 if meaningless, the "cnt" argument * is the count of remaining callbacks, and "done" is the piggybacking count. */ diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 73a4c9d07b86..57a7a0065750 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -4030,8 +4030,7 @@ void rcu_barrier(void) /* Did someone else do our work for us? */ if (rcu_seq_done(&rcu_state.barrier_sequence, s)) { - rcu_barrier_trace(TPS("EarlyExit"), -1, - rcu_state.barrier_sequence); + rcu_barrier_trace(TPS("EarlyExit"), -1, rcu_state.barrier_sequence); smp_mb(); /* caller's subsequent code after above check. */ mutex_unlock(&rcu_state.barrier_mutex); return; @@ -4059,26 +4058,18 @@ void rcu_barrier(void) */ for_each_possible_cpu(cpu) { rdp = per_cpu_ptr(&rcu_data, cpu); - if (cpu_is_offline(cpu) && - !rcu_rdp_is_offloaded(rdp)) + if (!rcu_segcblist_n_cbs(&rdp->cblist)) { + rcu_barrier_trace(TPS("NQ"), cpu, rcu_state.barrier_sequence); continue; - if (rcu_segcblist_n_cbs(&rdp->cblist) && cpu_online(cpu)) { - rcu_barrier_trace(TPS("OnlineQ"), cpu, - rcu_state.barrier_sequence); + } + if (cpu_online(cpu)) { + rcu_barrier_trace(TPS("OnlineQ"), cpu, rcu_state.barrier_sequence); smp_call_function_single(cpu, rcu_barrier_func, (void *)cpu, 1); - } else if (rcu_segcblist_n_cbs(&rdp->cblist) && - cpu_is_offline(cpu)) { - rcu_barrier_trace(TPS("OfflineNoCBQ"), cpu, - rcu_state.barrier_sequence); + } else { + rcu_barrier_trace(TPS("OfflineNoCBQ"), cpu, rcu_state.barrier_sequence); local_irq_disable(); rcu_barrier_func((void *)cpu); local_irq_enable(); - } else if (cpu_is_offline(cpu)) { - rcu_barrier_trace(TPS("OfflineNoCBNoQ"), cpu, - rcu_state.barrier_sequence); - } else { - rcu_barrier_trace(TPS("OnlineNQ"), cpu, - rcu_state.barrier_sequence); } } cpus_read_unlock(); From a16578dd5e3a44b53ca0699ac2971679dab97484 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 14 Dec 2021 13:15:18 -0800 Subject: [PATCH 079/294] rcu: Rework rcu_barrier() and callback-migration logic This commit reworks rcu_barrier() and callback-migration logic to permit allowing rcu_barrier() to run concurrently with CPU-hotplug operations. The key trick is for callback migration to check to see if an rcu_barrier() is in flight, and, if so, enqueue the ->barrier_head callback on its behalf. This commit adds synchronization with RCU's CPU-hotplug notifiers. Taken together, this will permit a later commit to remove the cpus_read_lock() and cpus_read_unlock() calls from rcu_barrier(). [ paulmck: Updated per kbuild test robot feedback. ] [ paulmck: Updated per reviews session with Neeraj, Frederic, Uladzislau, and Boqun. ] Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 77 +++++++++++++++++++++++++++++++++++++---------- kernel/rcu/tree.h | 2 ++ 2 files changed, 63 insertions(+), 16 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 57a7a0065750..004ff1c0d192 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -3987,13 +3987,16 @@ static void rcu_barrier_callback(struct rcu_head *rhp) } /* - * Called with preemption disabled, and from cross-cpu IRQ context. + * If needed, entrain an rcu_barrier() callback on rdp->cblist. */ -static void rcu_barrier_func(void *cpu_in) +static void rcu_barrier_entrain(struct rcu_data *rdp) { - uintptr_t cpu = (uintptr_t)cpu_in; - struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu); + unsigned long gseq = READ_ONCE(rcu_state.barrier_sequence); + unsigned long lseq = READ_ONCE(rdp->barrier_seq_snap); + lockdep_assert_held(&rdp->barrier_lock); + if (rcu_seq_state(lseq) || !rcu_seq_state(gseq) || rcu_seq_ctr(lseq) != rcu_seq_ctr(gseq)) + return; rcu_barrier_trace(TPS("IRQ"), -1, rcu_state.barrier_sequence); rdp->barrier_head.func = rcu_barrier_callback; debug_rcu_head_queue(&rdp->barrier_head); @@ -4003,10 +4006,26 @@ static void rcu_barrier_func(void *cpu_in) atomic_inc(&rcu_state.barrier_cpu_count); } else { debug_rcu_head_unqueue(&rdp->barrier_head); - rcu_barrier_trace(TPS("IRQNQ"), -1, - rcu_state.barrier_sequence); + rcu_barrier_trace(TPS("IRQNQ"), -1, rcu_state.barrier_sequence); } rcu_nocb_unlock(rdp); + smp_store_release(&rdp->barrier_seq_snap, gseq); +} + +/* + * Called with preemption disabled, and from cross-cpu IRQ context. + */ +static void rcu_barrier_handler(void *cpu_in) +{ + uintptr_t cpu = (uintptr_t)cpu_in; + struct rcu_data *rdp = per_cpu_ptr(&rcu_data, cpu); + + lockdep_assert_irqs_disabled(); + WARN_ON_ONCE(cpu != rdp->cpu); + WARN_ON_ONCE(cpu != smp_processor_id()); + raw_spin_lock(&rdp->barrier_lock); + rcu_barrier_entrain(rdp); + raw_spin_unlock(&rdp->barrier_lock); } /** @@ -4020,6 +4039,8 @@ static void rcu_barrier_func(void *cpu_in) void rcu_barrier(void) { uintptr_t cpu; + unsigned long flags; + unsigned long gseq; struct rcu_data *rdp; unsigned long s = rcu_seq_snap(&rcu_state.barrier_sequence); @@ -4038,6 +4059,7 @@ void rcu_barrier(void) /* Mark the start of the barrier operation. */ rcu_seq_start(&rcu_state.barrier_sequence); + gseq = rcu_state.barrier_sequence; rcu_barrier_trace(TPS("Inc1"), -1, rcu_state.barrier_sequence); /* @@ -4058,19 +4080,30 @@ void rcu_barrier(void) */ for_each_possible_cpu(cpu) { rdp = per_cpu_ptr(&rcu_data, cpu); +retry: + if (smp_load_acquire(&rdp->barrier_seq_snap) == gseq) + continue; + raw_spin_lock_irqsave(&rdp->barrier_lock, flags); if (!rcu_segcblist_n_cbs(&rdp->cblist)) { + WRITE_ONCE(rdp->barrier_seq_snap, gseq); + raw_spin_unlock_irqrestore(&rdp->barrier_lock, flags); rcu_barrier_trace(TPS("NQ"), cpu, rcu_state.barrier_sequence); continue; } - if (cpu_online(cpu)) { - rcu_barrier_trace(TPS("OnlineQ"), cpu, rcu_state.barrier_sequence); - smp_call_function_single(cpu, rcu_barrier_func, (void *)cpu, 1); - } else { + if (!rcu_rdp_cpu_online(rdp)) { + rcu_barrier_entrain(rdp); + WARN_ON_ONCE(READ_ONCE(rdp->barrier_seq_snap) != gseq); + raw_spin_unlock_irqrestore(&rdp->barrier_lock, flags); rcu_barrier_trace(TPS("OfflineNoCBQ"), cpu, rcu_state.barrier_sequence); - local_irq_disable(); - rcu_barrier_func((void *)cpu); - local_irq_enable(); + continue; } + raw_spin_unlock_irqrestore(&rdp->barrier_lock, flags); + if (smp_call_function_single(cpu, rcu_barrier_handler, (void *)cpu, 1)) { + schedule_timeout_uninterruptible(1); + goto retry; + } + WARN_ON_ONCE(READ_ONCE(rdp->barrier_seq_snap) != gseq); + rcu_barrier_trace(TPS("OnlineQ"), cpu, rcu_state.barrier_sequence); } cpus_read_unlock(); @@ -4087,6 +4120,12 @@ void rcu_barrier(void) /* Mark the end of the barrier operation. */ rcu_barrier_trace(TPS("Inc2"), -1, rcu_state.barrier_sequence); rcu_seq_end(&rcu_state.barrier_sequence); + gseq = rcu_state.barrier_sequence; + for_each_possible_cpu(cpu) { + rdp = per_cpu_ptr(&rcu_data, cpu); + + WRITE_ONCE(rdp->barrier_seq_snap, gseq); + } /* Other rcu_barrier() invocations can now safely proceed. */ mutex_unlock(&rcu_state.barrier_mutex); @@ -4134,6 +4173,8 @@ rcu_boot_init_percpu_data(int cpu) INIT_WORK(&rdp->strict_work, strict_work_handler); WARN_ON_ONCE(rdp->dynticks_nesting != 1); WARN_ON_ONCE(rcu_dynticks_in_eqs(rcu_dynticks_snap(rdp))); + raw_spin_lock_init(&rdp->barrier_lock); + rdp->barrier_seq_snap = rcu_state.barrier_sequence; rdp->rcu_ofl_gp_seq = rcu_state.gp_seq; rdp->rcu_ofl_gp_flags = RCU_GP_CLEANED; rdp->rcu_onl_gp_seq = rcu_state.gp_seq; @@ -4284,8 +4325,10 @@ void rcu_cpu_starting(unsigned int cpu) local_irq_save(flags); arch_spin_lock(&rcu_state.ofl_lock); rcu_dynticks_eqs_online(); + raw_spin_lock(&rdp->barrier_lock); raw_spin_lock_rcu_node(rnp); WRITE_ONCE(rnp->qsmaskinitnext, rnp->qsmaskinitnext | mask); + raw_spin_unlock(&rdp->barrier_lock); newcpu = !(rnp->expmaskinitnext & mask); rnp->expmaskinitnext |= mask; /* Allow lockless access for expedited grace periods. */ @@ -4372,7 +4415,9 @@ void rcutree_migrate_callbacks(int cpu) rcu_segcblist_empty(&rdp->cblist)) return; /* No callbacks to migrate. */ - local_irq_save(flags); + raw_spin_lock_irqsave(&rdp->barrier_lock, flags); + WARN_ON_ONCE(rcu_rdp_cpu_online(rdp)); + rcu_barrier_entrain(rdp); my_rdp = this_cpu_ptr(&rcu_data); my_rnp = my_rdp->mynode; rcu_nocb_lock(my_rdp); /* irqs already disabled. */ @@ -4382,10 +4427,10 @@ void rcutree_migrate_callbacks(int cpu) needwake = rcu_advance_cbs(my_rnp, rdp) || rcu_advance_cbs(my_rnp, my_rdp); rcu_segcblist_merge(&my_rdp->cblist, &rdp->cblist); + raw_spin_unlock(&rdp->barrier_lock); /* irqs remain disabled. */ needwake = needwake || rcu_advance_cbs(my_rnp, my_rdp); rcu_segcblist_disable(&rdp->cblist); - WARN_ON_ONCE(rcu_segcblist_empty(&my_rdp->cblist) != - !rcu_segcblist_n_cbs(&my_rdp->cblist)); + WARN_ON_ONCE(rcu_segcblist_empty(&my_rdp->cblist) != !rcu_segcblist_n_cbs(&my_rdp->cblist)); if (rcu_rdp_is_offloaded(my_rdp)) { raw_spin_unlock_rcu_node(my_rnp); /* irqs remain disabled. */ __call_rcu_nocb_wake(my_rdp, true, flags); diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 4b4bcef8a974..84362951ed9e 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -188,6 +188,8 @@ struct rcu_data { bool rcu_forced_tick_exp; /* ... provide QS to expedited GP. */ /* 4) rcu_barrier(), OOM callbacks, and expediting. */ + raw_spinlock_t barrier_lock; /* Protects ->barrier_seq_snap. */ + unsigned long barrier_seq_snap; /* Snap of rcu_state.barrier_sequence. */ struct rcu_head barrier_head; int exp_dynticks_snap; /* Double-check need for IPI. */ From 80b3fd474c91b3ecfd845b4a0bfb58706b877ba5 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 14 Dec 2021 13:35:17 -0800 Subject: [PATCH 080/294] rcu: Make rcu_barrier() no longer block CPU-hotplug operations This commit removes the cpus_read_lock() and cpus_read_unlock() calls from rcu_barrier(), thus allowing CPUs to come and go during the course of rcu_barrier() execution. Posting of the ->barrier_head callbacks does synchronize with portions of RCU's CPU-hotplug notifiers, but these locks are held for short time periods on both sides. Thus, full CPU-hotplug operations could both start and finish during the execution of a given rcu_barrier() invocation. Additional synchronization is provided by a global ->barrier_lock. Since the ->barrier_lock is only used during rcu_barrier() execution and during onlining/offlining a CPU, the contention for this lock should be low. It might be tempting to make use of a per-CPU lock just on general principles, but straightforward attempts to do this have the problems shown below. Initial state: 3 CPUs present, CPU 0 and CPU1 do not have any callback and CPU2 has callbacks. 1. CPU0 calls rcu_barrier(). 2. CPU1 starts offlining for CPU2. CPU1 calls rcutree_migrate_callbacks(). rcu_barrier_entrain() is called from rcutree_migrate_callbacks(), with CPU2's rdp->barrier_lock. It does not entrain ->barrier_head for CPU2, as rcu_barrier() on CPU0 hasn't started the barrier sequence (by calling rcu_seq_start(&rcu_state.barrier_sequence)) yet. 3. CPU0 starts new barrier sequence. It iterates over CPU0 and CPU1, after acquiring their per-cpu ->barrier_lock and finds 0 segcblist length. It updates ->barrier_seq_snap for CPU0 and CPU1 and continues loop iteration to CPU2. for_each_possible_cpu(cpu) { raw_spin_lock_irqsave(&rdp->barrier_lock, flags); if (!rcu_segcblist_n_cbs(&rdp->cblist)) { WRITE_ONCE(rdp->barrier_seq_snap, gseq); raw_spin_unlock_irqrestore(&rdp->barrier_lock, flags); rcu_barrier_trace(TPS("NQ"), cpu, rcu_state.barrier_sequence); continue; } 4. rcutree_migrate_callbacks() completes execution on CPU1. Segcblist len for CPU2 becomes 0. 5. The loop iteration on CPU0, checks rcu_segcblist_n_cbs(&rdp->cblist) for CPU2 and completes the loop iteration after setting ->barrier_seq_snap. 6. As there isn't any ->barrier_head callback entrained; at this point, rcu_barrier() in CPU0 returns. 7. The callbacks, which migrated from CPU2 to CPU1, execute. Straightforward per-CPU locking is also subject to the following race condition noted by Boqun Feng: 1. CPU0 calls rcu_barrier(), starting a new barrier sequence by invoking rcu_seq_start() and init_completion(), but does not yet initialize rcu_state.barrier_cpu_count. 2. CPU1 starts offlining for CPU2, calling rcutree_migrate_callbacks(), which in turn calls rcu_barrier_entrain() holding CPU2's. rdp->barrier_lock. It then entrains ->barrier_head for CPU2 and atomically increments rcu_state.barrier_cpu_count, which is unfortunately not yet initialized to the value 2. 3. The just-entrained RCU callback is invoked. It atomically decrements rcu_state.barrier_cpu_count and sees that it is now zero. This callback therefore invokes complete(). 4. CPU0 continues executing rcu_barrier(), but is not blocked by its call to wait_for_completion(). This results in rcu_barrier() returning before all pre-existing callbacks have been invoked, which is a bug. Therefore, synchronization is provided by rcu_state.barrier_lock, which is also held across the initialization sequence, especially the rcu_seq_start() and the atomic_set() that sets rcu_state.barrier_cpu_count to the value 2. In addition, this lock is held when entraining the rcu_barrier() callback, when deciding whether or not a CPU has callbacks that rcu_barrier() must wait on, when setting the ->qsmaskinitnext for incoming CPUs, and when migrating callbacks from a CPU that is going offline. Reviewed-by: Frederic Weisbecker Co-developed-by: Neeraj Upadhyay Signed-off-by: Neeraj Upadhyay Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 28 ++++++++++++++-------------- kernel/rcu/tree.h | 3 ++- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 004ff1c0d192..2d70b91e3fbc 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -87,6 +87,7 @@ static struct rcu_state rcu_state = { .gp_state = RCU_GP_IDLE, .gp_seq = (0UL - 300UL) << RCU_SEQ_CTR_SHIFT, .barrier_mutex = __MUTEX_INITIALIZER(rcu_state.barrier_mutex), + .barrier_lock = __RAW_SPIN_LOCK_UNLOCKED(rcu_state.barrier_lock), .name = RCU_NAME, .abbr = RCU_ABBR, .exp_mutex = __MUTEX_INITIALIZER(rcu_state.exp_mutex), @@ -3994,7 +3995,7 @@ static void rcu_barrier_entrain(struct rcu_data *rdp) unsigned long gseq = READ_ONCE(rcu_state.barrier_sequence); unsigned long lseq = READ_ONCE(rdp->barrier_seq_snap); - lockdep_assert_held(&rdp->barrier_lock); + lockdep_assert_held(&rcu_state.barrier_lock); if (rcu_seq_state(lseq) || !rcu_seq_state(gseq) || rcu_seq_ctr(lseq) != rcu_seq_ctr(gseq)) return; rcu_barrier_trace(TPS("IRQ"), -1, rcu_state.barrier_sequence); @@ -4023,9 +4024,9 @@ static void rcu_barrier_handler(void *cpu_in) lockdep_assert_irqs_disabled(); WARN_ON_ONCE(cpu != rdp->cpu); WARN_ON_ONCE(cpu != smp_processor_id()); - raw_spin_lock(&rdp->barrier_lock); + raw_spin_lock(&rcu_state.barrier_lock); rcu_barrier_entrain(rdp); - raw_spin_unlock(&rdp->barrier_lock); + raw_spin_unlock(&rcu_state.barrier_lock); } /** @@ -4058,6 +4059,7 @@ void rcu_barrier(void) } /* Mark the start of the barrier operation. */ + raw_spin_lock_irqsave(&rcu_state.barrier_lock, flags); rcu_seq_start(&rcu_state.barrier_sequence); gseq = rcu_state.barrier_sequence; rcu_barrier_trace(TPS("Inc1"), -1, rcu_state.barrier_sequence); @@ -4071,7 +4073,7 @@ void rcu_barrier(void) */ init_completion(&rcu_state.barrier_completion); atomic_set(&rcu_state.barrier_cpu_count, 2); - cpus_read_lock(); + raw_spin_unlock_irqrestore(&rcu_state.barrier_lock, flags); /* * Force each CPU with callbacks to register a new callback. @@ -4083,21 +4085,21 @@ void rcu_barrier(void) retry: if (smp_load_acquire(&rdp->barrier_seq_snap) == gseq) continue; - raw_spin_lock_irqsave(&rdp->barrier_lock, flags); + raw_spin_lock_irqsave(&rcu_state.barrier_lock, flags); if (!rcu_segcblist_n_cbs(&rdp->cblist)) { WRITE_ONCE(rdp->barrier_seq_snap, gseq); - raw_spin_unlock_irqrestore(&rdp->barrier_lock, flags); + raw_spin_unlock_irqrestore(&rcu_state.barrier_lock, flags); rcu_barrier_trace(TPS("NQ"), cpu, rcu_state.barrier_sequence); continue; } if (!rcu_rdp_cpu_online(rdp)) { rcu_barrier_entrain(rdp); WARN_ON_ONCE(READ_ONCE(rdp->barrier_seq_snap) != gseq); - raw_spin_unlock_irqrestore(&rdp->barrier_lock, flags); + raw_spin_unlock_irqrestore(&rcu_state.barrier_lock, flags); rcu_barrier_trace(TPS("OfflineNoCBQ"), cpu, rcu_state.barrier_sequence); continue; } - raw_spin_unlock_irqrestore(&rdp->barrier_lock, flags); + raw_spin_unlock_irqrestore(&rcu_state.barrier_lock, flags); if (smp_call_function_single(cpu, rcu_barrier_handler, (void *)cpu, 1)) { schedule_timeout_uninterruptible(1); goto retry; @@ -4105,7 +4107,6 @@ void rcu_barrier(void) WARN_ON_ONCE(READ_ONCE(rdp->barrier_seq_snap) != gseq); rcu_barrier_trace(TPS("OnlineQ"), cpu, rcu_state.barrier_sequence); } - cpus_read_unlock(); /* * Now that we have an rcu_barrier_callback() callback on each @@ -4173,7 +4174,6 @@ rcu_boot_init_percpu_data(int cpu) INIT_WORK(&rdp->strict_work, strict_work_handler); WARN_ON_ONCE(rdp->dynticks_nesting != 1); WARN_ON_ONCE(rcu_dynticks_in_eqs(rcu_dynticks_snap(rdp))); - raw_spin_lock_init(&rdp->barrier_lock); rdp->barrier_seq_snap = rcu_state.barrier_sequence; rdp->rcu_ofl_gp_seq = rcu_state.gp_seq; rdp->rcu_ofl_gp_flags = RCU_GP_CLEANED; @@ -4325,10 +4325,10 @@ void rcu_cpu_starting(unsigned int cpu) local_irq_save(flags); arch_spin_lock(&rcu_state.ofl_lock); rcu_dynticks_eqs_online(); - raw_spin_lock(&rdp->barrier_lock); + raw_spin_lock(&rcu_state.barrier_lock); raw_spin_lock_rcu_node(rnp); WRITE_ONCE(rnp->qsmaskinitnext, rnp->qsmaskinitnext | mask); - raw_spin_unlock(&rdp->barrier_lock); + raw_spin_unlock(&rcu_state.barrier_lock); newcpu = !(rnp->expmaskinitnext & mask); rnp->expmaskinitnext |= mask; /* Allow lockless access for expedited grace periods. */ @@ -4415,7 +4415,7 @@ void rcutree_migrate_callbacks(int cpu) rcu_segcblist_empty(&rdp->cblist)) return; /* No callbacks to migrate. */ - raw_spin_lock_irqsave(&rdp->barrier_lock, flags); + raw_spin_lock_irqsave(&rcu_state.barrier_lock, flags); WARN_ON_ONCE(rcu_rdp_cpu_online(rdp)); rcu_barrier_entrain(rdp); my_rdp = this_cpu_ptr(&rcu_data); @@ -4427,7 +4427,7 @@ void rcutree_migrate_callbacks(int cpu) needwake = rcu_advance_cbs(my_rnp, rdp) || rcu_advance_cbs(my_rnp, my_rdp); rcu_segcblist_merge(&my_rdp->cblist, &rdp->cblist); - raw_spin_unlock(&rdp->barrier_lock); /* irqs remain disabled. */ + raw_spin_unlock(&rcu_state.barrier_lock); /* irqs remain disabled. */ needwake = needwake || rcu_advance_cbs(my_rnp, my_rdp); rcu_segcblist_disable(&rdp->cblist); WARN_ON_ONCE(rcu_segcblist_empty(&my_rdp->cblist) != !rcu_segcblist_n_cbs(&my_rdp->cblist)); diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 84362951ed9e..a2d7ffd634cc 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -188,7 +188,6 @@ struct rcu_data { bool rcu_forced_tick_exp; /* ... provide QS to expedited GP. */ /* 4) rcu_barrier(), OOM callbacks, and expediting. */ - raw_spinlock_t barrier_lock; /* Protects ->barrier_seq_snap. */ unsigned long barrier_seq_snap; /* Snap of rcu_state.barrier_sequence. */ struct rcu_head barrier_head; int exp_dynticks_snap; /* Double-check need for IPI. */ @@ -323,6 +322,8 @@ struct rcu_state { /* rcu_barrier(). */ /* End of fields guarded by barrier_mutex. */ + raw_spinlock_t barrier_lock; /* Protects ->barrier_seq_snap. */ + struct mutex exp_mutex; /* Serialize expedited GP. */ struct mutex exp_wake_mutex; /* Serialize wakeup. */ unsigned long expedited_sequence; /* Take a ticket. */ From 5ae0f1b58b28b53f4ab3708ef9337a2665e79664 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Fri, 10 Dec 2021 13:44:17 -0800 Subject: [PATCH 081/294] rcu: Create and use an rcu_rdp_cpu_online() The pattern "rdp->grpmask & rcu_rnp_online_cpus(rnp)" occurs frequently in RCU code in order to determine whether rdp->cpu is online from an RCU perspective. This commit therefore creates an rcu_rdp_cpu_online() function to replace it. [ paulmck: Apply kernel test robot unused-variable feedback. ] Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 21 +++++++++++++-------- kernel/rcu/tree_plugin.h | 6 ++---- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index 2d70b91e3fbc..1d3507d563db 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -222,6 +222,16 @@ static unsigned long rcu_rnp_online_cpus(struct rcu_node *rnp) return READ_ONCE(rnp->qsmaskinitnext); } +/* + * Is the CPU corresponding to the specified rcu_data structure online + * from RCU's perspective? This perspective is given by that structure's + * ->qsmaskinitnext field rather than by the global cpu_online_mask. + */ +static bool rcu_rdp_cpu_online(struct rcu_data *rdp) +{ + return !!(rdp->grpmask & rcu_rnp_online_cpus(rdp->mynode)); +} + /* * Return true if an RCU grace period is in progress. The READ_ONCE()s * permit this function to be invoked without holding the root rcu_node @@ -1168,14 +1178,12 @@ void rcu_request_urgent_qs_task(struct task_struct *t) bool rcu_lockdep_current_cpu_online(void) { struct rcu_data *rdp; - struct rcu_node *rnp; bool ret = false; if (in_nmi() || !rcu_scheduler_fully_active) return true; preempt_disable_notrace(); rdp = this_cpu_ptr(&rcu_data); - rnp = rdp->mynode; /* * Strictly, we care here about the case where the current CPU is * in rcu_cpu_starting() and thus has an excuse for rdp->grpmask @@ -1183,8 +1191,7 @@ bool rcu_lockdep_current_cpu_online(void) * false positive if it's held by some *other* CPU, but that's * OK because that just means a false *negative* on the warning. */ - if (rdp->grpmask & rcu_rnp_online_cpus(rnp) || - arch_spin_is_locked(&rcu_state.ofl_lock)) + if (rcu_rdp_cpu_online(rdp) || arch_spin_is_locked(&rcu_state.ofl_lock)) ret = true; preempt_enable_notrace(); return ret; @@ -1269,8 +1276,7 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp) * For more detail, please refer to the "Hotplug CPU" section * of RCU's Requirements documentation. */ - if (WARN_ON_ONCE(!(rdp->grpmask & rcu_rnp_online_cpus(rnp)))) { - bool onl; + if (WARN_ON_ONCE(!rcu_rdp_cpu_online(rdp))) { struct rcu_node *rnp1; pr_info("%s: grp: %d-%d level: %d ->gp_seq %ld ->completedqs %ld\n", @@ -1279,9 +1285,8 @@ static int rcu_implicit_dynticks_qs(struct rcu_data *rdp) for (rnp1 = rnp; rnp1; rnp1 = rnp1->parent) pr_info("%s: %d:%d ->qsmask %#lx ->qsmaskinit %#lx ->qsmaskinitnext %#lx ->rcu_gp_init_mask %#lx\n", __func__, rnp1->grplo, rnp1->grphi, rnp1->qsmask, rnp1->qsmaskinit, rnp1->qsmaskinitnext, rnp1->rcu_gp_init_mask); - onl = !!(rdp->grpmask & rcu_rnp_online_cpus(rnp)); pr_info("%s %d: %c online: %ld(%d) offline: %ld(%d)\n", - __func__, rdp->cpu, ".o"[onl], + __func__, rdp->cpu, ".o"[rcu_rdp_cpu_online(rdp)], (long)rdp->rcu_onl_gp_seq, rdp->rcu_onl_gp_flags, (long)rdp->rcu_ofl_gp_seq, rdp->rcu_ofl_gp_flags); return 1; /* Break things loose after complaining. */ diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index c5b45c2f68a1..d3db2168598e 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -330,7 +330,7 @@ void rcu_note_context_switch(bool preempt) * then queue the task as required based on the states * of any ongoing and expedited grace periods. */ - WARN_ON_ONCE((rdp->grpmask & rcu_rnp_online_cpus(rnp)) == 0); + WARN_ON_ONCE(!rcu_rdp_cpu_online(rdp)); WARN_ON_ONCE(!list_empty(&t->rcu_node_entry)); trace_rcu_preempt_task(rcu_state.name, t->pid, @@ -773,7 +773,6 @@ dump_blkd_tasks(struct rcu_node *rnp, int ncheck) int cpu; int i; struct list_head *lhp; - bool onl; struct rcu_data *rdp; struct rcu_node *rnp1; @@ -797,9 +796,8 @@ dump_blkd_tasks(struct rcu_node *rnp, int ncheck) pr_cont("\n"); for (cpu = rnp->grplo; cpu <= rnp->grphi; cpu++) { rdp = per_cpu_ptr(&rcu_data, cpu); - onl = !!(rdp->grpmask & rcu_rnp_online_cpus(rnp)); pr_info("\t%d: %c online: %ld(%d) offline: %ld(%d)\n", - cpu, ".o"[onl], + cpu, ".o"[rcu_rdp_cpu_online(rdp)], (long)rdp->rcu_onl_gp_seq, rdp->rcu_onl_gp_flags, (long)rdp->rcu_ofl_gp_seq, rdp->rcu_ofl_gp_flags); } From 2bcd18e041fc3c2ae58f41eb5e18790c7c82c674 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 2 Feb 2022 15:42:36 -0800 Subject: [PATCH 082/294] rcu-tasks: Use order_base_2() instead of ilog2() The ilog2() function can be used to generate a shift count, but it will generate the same count for a power of two as for one greater than a power of two. This results in shift counts that are larger than necessary for systems with a power-of-two number of CPUs because the CPUs are numbered from zero, so that the maximum CPU number is one less than that power of two. This commit therefore substitutes order_base_2(), which appears to have been designed for exactly this use case. Suggested-by: Mark Rutland Signed-off-by: Paul E. McKenney --- kernel/rcu/tasks.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/rcu/tasks.h b/kernel/rcu/tasks.h index d64f0b1d8cd3..670c75cbcb98 100644 --- a/kernel/rcu/tasks.h +++ b/kernel/rcu/tasks.h @@ -123,7 +123,7 @@ static struct rcu_tasks rt_name = \ .call_func = call, \ .rtpcpu = &rt_name ## __percpu, \ .name = n, \ - .percpu_enqueue_shift = ilog2(CONFIG_NR_CPUS) + 1, \ + .percpu_enqueue_shift = order_base_2(CONFIG_NR_CPUS), \ .percpu_enqueue_lim = 1, \ .percpu_dequeue_lim = 1, \ .barrier_q_mutex = __MUTEX_INITIALIZER(rt_name.barrier_q_mutex), \ @@ -302,7 +302,7 @@ static void call_rcu_tasks_generic(struct rcu_head *rhp, rcu_callback_t func, if (unlikely(needadjust)) { raw_spin_lock_irqsave(&rtp->cbs_gbl_lock, flags); if (rtp->percpu_enqueue_lim != nr_cpu_ids) { - WRITE_ONCE(rtp->percpu_enqueue_shift, ilog2(nr_cpu_ids) + 1); + WRITE_ONCE(rtp->percpu_enqueue_shift, order_base_2(nr_cpu_ids)); WRITE_ONCE(rtp->percpu_dequeue_lim, nr_cpu_ids); smp_store_release(&rtp->percpu_enqueue_lim, nr_cpu_ids); pr_info("Switching %s to per-CPU callback queuing.\n", rtp->name); @@ -417,7 +417,7 @@ static int rcu_tasks_need_gpcb(struct rcu_tasks *rtp) if (rcu_task_cb_adjust && ncbs <= rcu_task_collapse_lim) { raw_spin_lock_irqsave(&rtp->cbs_gbl_lock, flags); if (rtp->percpu_enqueue_lim > 1) { - WRITE_ONCE(rtp->percpu_enqueue_shift, ilog2(nr_cpu_ids) + 1); + WRITE_ONCE(rtp->percpu_enqueue_shift, order_base_2(nr_cpu_ids)); smp_store_release(&rtp->percpu_enqueue_lim, 1); rtp->percpu_dequeue_gpseq = get_state_synchronize_rcu(); pr_info("Starting switch %s to CPU-0 callback queuing.\n", rtp->name); From 00a8b4b54cd69d9f7ba1730d3b266469a778b1d7 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 2 Feb 2022 16:34:40 -0800 Subject: [PATCH 083/294] rcu-tasks: Set ->percpu_enqueue_shift to zero upon contention Currently, call_rcu_tasks_generic() sets ->percpu_enqueue_shift to order_base_2(nr_cpu_ids) upon encountering sufficient contention. This does not shift to use of non-CPU-0 callback queues as intended, but rather continues using only CPU 0's queue. Although this does provide some decrease in contention due to spreading work over multiple locks, it is not the dramatic decrease that was intended. This commit therefore makes call_rcu_tasks_generic() set ->percpu_enqueue_shift to 0. Reported-by: Neeraj Upadhyay Signed-off-by: Paul E. McKenney --- kernel/rcu/tasks.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/rcu/tasks.h b/kernel/rcu/tasks.h index 670c75cbcb98..ac17348187e4 100644 --- a/kernel/rcu/tasks.h +++ b/kernel/rcu/tasks.h @@ -302,7 +302,7 @@ static void call_rcu_tasks_generic(struct rcu_head *rhp, rcu_callback_t func, if (unlikely(needadjust)) { raw_spin_lock_irqsave(&rtp->cbs_gbl_lock, flags); if (rtp->percpu_enqueue_lim != nr_cpu_ids) { - WRITE_ONCE(rtp->percpu_enqueue_shift, order_base_2(nr_cpu_ids)); + WRITE_ONCE(rtp->percpu_enqueue_shift, 0); WRITE_ONCE(rtp->percpu_dequeue_lim, nr_cpu_ids); smp_store_release(&rtp->percpu_enqueue_lim, nr_cpu_ids); pr_info("Switching %s to per-CPU callback queuing.\n", rtp->name); From f233673cd32a048f2eed69e56b61174c33fb740b Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Thu, 27 Jan 2022 09:39:15 -0800 Subject: [PATCH 084/294] torture: Make torture.sh help message match reality This commit fixes a couple of typos: s/--doall/--do-all/ and s/--doallmodconfig/--do-allmodconfig/. [ paulmck: Add Fixes: supplied by Paul Menzel. ] Fixes: a115a775a8d5 ("torture: Add "make allmodconfig" to torture.sh") Reported-by: Paul Menzel Signed-off-by: Paul E. McKenney --- tools/testing/selftests/rcutorture/bin/torture.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/rcutorture/bin/torture.sh b/tools/testing/selftests/rcutorture/bin/torture.sh index e00e60efb231..bfe09e2829c8 100755 --- a/tools/testing/selftests/rcutorture/bin/torture.sh +++ b/tools/testing/selftests/rcutorture/bin/torture.sh @@ -71,8 +71,8 @@ usage () { echo " --configs-rcutorture \"config-file list w/ repeat factor (3*TINY01)\"" echo " --configs-locktorture \"config-file list w/ repeat factor (10*LOCK01)\"" echo " --configs-scftorture \"config-file list w/ repeat factor (2*CFLIST)\"" - echo " --doall" - echo " --doallmodconfig / --do-no-allmodconfig" + echo " --do-all" + echo " --do-allmodconfig / --do-no-allmodconfig" echo " --do-clocksourcewd / --do-no-clocksourcewd" echo " --do-kasan / --do-no-kasan" echo " --do-kcsan / --do-no-kcsan" From b5597cb36f8bb29b244b2f90030d54bf81bf6fbc Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Mon, 31 Jan 2022 15:03:36 -0800 Subject: [PATCH 085/294] rcutorture: Test SRCU size transitions Thie commit adds kernel boot parameters to the SRCU-N and SRCU-P rcutorture scenarios to cause SRCU-N to test contention-based resizing and SRCU-P to test init_srcu_struct()-time resizing. Note that this also tests never-resizing because the contention-based resizing normally takes some minutes to make the shift. Signed-off-by: Paul E. McKenney --- tools/testing/selftests/rcutorture/configs/rcu/SRCU-N.boot | 1 + tools/testing/selftests/rcutorture/configs/rcu/SRCU-P.boot | 2 ++ 2 files changed, 3 insertions(+) diff --git a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-N.boot b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-N.boot index 238bfe3bd0cc..ce0694fd9b92 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-N.boot +++ b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-N.boot @@ -1 +1,2 @@ rcutorture.torture_type=srcu +rcutorture.fwd_progress=3 diff --git a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-P.boot b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-P.boot index ce48c7b82673..2db39f298d18 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/SRCU-P.boot +++ b/tools/testing/selftests/rcutorture/configs/rcu/SRCU-P.boot @@ -1,2 +1,4 @@ rcutorture.torture_type=srcud rcupdate.rcu_self_test=1 +rcutorture.fwd_progress=3 +srcutree.big_cpu_lim=5 From 8ea7a53daf3c9d26910ee9a115b2fb6b86cf3c01 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 1 Feb 2022 08:23:46 -0800 Subject: [PATCH 086/294] rcutorture: Provide non-power-of-two Tasks RCU scenarios This commit adjusts RUDE01 to 3 CPUs and TRACE01 to 5 CPUs in order to test Tasks RCU's ability to handle non-power-of-two numbers of CPUs. Signed-off-by: Paul E. McKenney --- tools/testing/selftests/rcutorture/configs/rcu/RUDE01 | 2 +- tools/testing/selftests/rcutorture/configs/rcu/TRACE01 | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/testing/selftests/rcutorture/configs/rcu/RUDE01 b/tools/testing/selftests/rcutorture/configs/rcu/RUDE01 index 3ca112444ce7..7093422050f6 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/RUDE01 +++ b/tools/testing/selftests/rcutorture/configs/rcu/RUDE01 @@ -1,5 +1,5 @@ CONFIG_SMP=y -CONFIG_NR_CPUS=4 +CONFIG_NR_CPUS=3 CONFIG_HOTPLUG_CPU=y CONFIG_PREEMPT_NONE=n CONFIG_PREEMPT_VOLUNTARY=n diff --git a/tools/testing/selftests/rcutorture/configs/rcu/TRACE01 b/tools/testing/selftests/rcutorture/configs/rcu/TRACE01 index 34c8ff5a12f2..e4d74e5fc1d0 100644 --- a/tools/testing/selftests/rcutorture/configs/rcu/TRACE01 +++ b/tools/testing/selftests/rcutorture/configs/rcu/TRACE01 @@ -1,5 +1,5 @@ CONFIG_SMP=y -CONFIG_NR_CPUS=4 +CONFIG_NR_CPUS=5 CONFIG_HOTPLUG_CPU=y CONFIG_PREEMPT_NONE=y CONFIG_PREEMPT_VOLUNTARY=n From c9edbe1eb98248c290d93aa2ffdc30cab5e2e62c Mon Sep 17 00:00:00 2001 From: Akira Yokosawa Date: Wed, 29 Dec 2021 20:44:19 +0900 Subject: [PATCH 087/294] docs: sphinx/kfigure.py: Use rsvg-convert(1) for DOT -> PDF conversion On openSUSE, dot(1) command does not support direct PDF output. On other distros, generated PDF images have unnecessarily wide margins, especially for small graphs. By using dot(1) for DOT -> SVG, then rsvg-convert(1) for SVG -> PDF, more optimal PDF images can be obtained, with the bonus of improved portability across various distros. Add rules in kfigure.py so that the above mentioned route is taken when rsvg-convert(1) is available. Note that rsvg-convert(1) is recommended by sphinx_pre_install. So it is most likely that existing systems for building pdfdocs have rsvg-convert(1) installed. Note: SVG features supported by rsvg-convert(1) vary depending on its version and distro config. For example, the one found on Ubuntu Bionic (version 2.40.20) does poor job in rendering some of SVG files drawn by Inkscape. SVG files generated by dot(1) are converted nicely even with such old versions of rsvg-convert. So this change does not affect the quality of such figures in any way. Signed-off-by: Akira Yokosawa Cc: Jonathan Corbet Cc: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/15b56dd3-081a-2469-c3a4-dfc1ca4c6c2d@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/sphinx/kfigure.py | 46 +++++++++++++++++++++++++++++++-- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/Documentation/sphinx/kfigure.py b/Documentation/sphinx/kfigure.py index 3c78828330be..955e3ec5de5a 100644 --- a/Documentation/sphinx/kfigure.py +++ b/Documentation/sphinx/kfigure.py @@ -31,6 +31,8 @@ u""" * ``dot(1)``: Graphviz (https://www.graphviz.org). If Graphviz is not available, the DOT language is inserted as literal-block. + For conversion to PDF, ``rsvg-convert(1)`` of librsvg + (https://gitlab.gnome.org/GNOME/librsvg) is used when available. * SVG to PDF: To generate PDF, you need at least one of this tools: @@ -113,6 +115,9 @@ dot_cmd = None # ImageMagick' convert(1) support convert_cmd = None +# librsvg's rsvg-convert(1) support +rsvg_convert_cmd = None + def setup(app): # check toolchain first @@ -160,11 +165,12 @@ def setupTools(app): This function is called once, when the builder is initiated. """ - global dot_cmd, convert_cmd # pylint: disable=W0603 + global dot_cmd, convert_cmd, rsvg_convert_cmd # pylint: disable=W0603 kernellog.verbose(app, "kfigure: check installed tools ...") dot_cmd = which('dot') convert_cmd = which('convert') + rsvg_convert_cmd = which('rsvg-convert') if dot_cmd: kernellog.verbose(app, "use dot(1) from: " + dot_cmd) @@ -177,6 +183,11 @@ def setupTools(app): kernellog.warn(app, "convert(1) not found, for SVG to PDF conversion install " "ImageMagick (https://www.imagemagick.org)") + if rsvg_convert_cmd: + kernellog.verbose(app, "use rsvg-convert(1) from: " + rsvg_convert_cmd) + else: + kernellog.verbose(app, "rsvg-convert(1) not found, " + "falling back to raster image conversion") # integrate conversion tools @@ -266,7 +277,13 @@ def convert_image(img_node, translator, src_fname=None): if in_ext == '.dot': kernellog.verbose(app, 'convert DOT to: {out}/' + _name) - ok = dot2format(app, src_fname, dst_fname) + if translator.builder.format == 'latex': + svg_fname = path.join(translator.builder.outdir, fname + '.svg') + ok1 = dot2format(app, src_fname, svg_fname) + ok2 = svg2pdf_by_rsvg(app, svg_fname, dst_fname) + ok = ok1 and ok2 + else: + ok = dot2format(app, src_fname, dst_fname) elif in_ext == '.svg': kernellog.verbose(app, 'convert SVG to: {out}/' + _name) @@ -319,6 +336,31 @@ def svg2pdf(app, svg_fname, pdf_fname): kernellog.warn(app, "Error #%d when calling: %s" % (exit_code, " ".join(cmd))) return bool(exit_code == 0) +def svg2pdf_by_rsvg(app, svg_fname, pdf_fname): + """Convert SVG to PDF with ``rsvg-convert(1)`` command. + + * ``svg_fname`` pathname of input SVG file, including extension ``.svg`` + * ``pdf_fname`` pathname of output PDF file, including extension ``.pdf`` + + Input SVG file should be the one generated by ``dot2format()``. + SVG -> PDF conversion is done by ``rsvg-convert(1)``. + + If ``rsvg-convert(1)`` is unavailable, fall back to ``svg2pdf()``. + + """ + + if rsvg_convert_cmd is None: + ok = svg2pdf(app, svg_fname, pdf_fname) + else: + cmd = [rsvg_convert_cmd, '--format=pdf', '-o', pdf_fname, svg_fname] + # use stdout and stderr from parent + exit_code = subprocess.call(cmd) + if exit_code != 0: + kernellog.warn(app, "Error #%d when calling: %s" % (exit_code, " ".join(cmd))) + ok = bool(exit_code == 0) + + return ok + # image handling # --------------------- From ecf5fb58cdcd93b9cf555b95da4ef73b1297de4c Mon Sep 17 00:00:00 2001 From: Akira Yokosawa Date: Wed, 29 Dec 2021 20:45:29 +0900 Subject: [PATCH 088/294] docs: sphinx/kfigure.py: Add check of 'dot -Tpdf' To prevent any regression on existing build systems, limit the fallback of converting DOT -> raster PDF only when both of the following conditions are met. o dot(1) doesn't support -Tpdf o rsvg-convert(1) is not found While we are here, add kernellog.verbose messages related to rsvg-convert, 'dot -Tpdf', and 'dot -Tsvg' commands. Suggested-by: Mauro Carvalho Chehab Signed-off-by: Akira Yokosawa Cc: Jonathan Corbet Link: https://lore.kernel.org/r/e76f61e1-7366-ba00-b119-8ea6a2499861@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/sphinx/kfigure.py | 30 ++++++++++++++++++++++++++---- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/Documentation/sphinx/kfigure.py b/Documentation/sphinx/kfigure.py index 955e3ec5de5a..77b0d15dba31 100644 --- a/Documentation/sphinx/kfigure.py +++ b/Documentation/sphinx/kfigure.py @@ -51,6 +51,7 @@ import os from os import path import subprocess from hashlib import sha1 +import re from docutils import nodes from docutils.statemachine import ViewList from docutils.parsers.rst import directives @@ -111,6 +112,8 @@ def pass_handle(self, node): # pylint: disable=W0613 # Graphviz's dot(1) support dot_cmd = None +# dot(1) -Tpdf should be used +dot_Tpdf = False # ImageMagick' convert(1) support convert_cmd = None @@ -165,7 +168,7 @@ def setupTools(app): This function is called once, when the builder is initiated. """ - global dot_cmd, convert_cmd, rsvg_convert_cmd # pylint: disable=W0603 + global dot_cmd, dot_Tpdf, convert_cmd, rsvg_convert_cmd # pylint: disable=W0603 kernellog.verbose(app, "kfigure: check installed tools ...") dot_cmd = which('dot') @@ -174,6 +177,16 @@ def setupTools(app): if dot_cmd: kernellog.verbose(app, "use dot(1) from: " + dot_cmd) + + try: + dot_Thelp_list = subprocess.check_output([dot_cmd, '-Thelp'], + stderr=subprocess.STDOUT) + except subprocess.CalledProcessError as err: + dot_Thelp_list = err.output + pass + + dot_Tpdf_ptn = b'pdf' + dot_Tpdf = re.search(dot_Tpdf_ptn, dot_Thelp_list) else: kernellog.warn(app, "dot(1) not found, for better output quality install " "graphviz from https://www.graphviz.org") @@ -185,9 +198,17 @@ def setupTools(app): "ImageMagick (https://www.imagemagick.org)") if rsvg_convert_cmd: kernellog.verbose(app, "use rsvg-convert(1) from: " + rsvg_convert_cmd) + kernellog.verbose(app, "use 'dot -Tsvg' and rsvg-convert(1) for DOT -> PDF conversion") + dot_Tpdf = False else: - kernellog.verbose(app, "rsvg-convert(1) not found, " - "falling back to raster image conversion") + kernellog.verbose(app, + "rsvg-convert(1) not found.\n" + " SVG -> PDF conversion by convert() can be poor quality.\n" + " Install librsvg (https://gitlab.gnome.org/GNOME/librsvg)") + if dot_Tpdf: + kernellog.verbose(app, "use 'dot -Tpdf' for DOT -> PDF conversion") + else: + kernellog.verbose(app, "use 'dot -Tsvg' and convert(1) for DOT -> PDF conversion") # integrate conversion tools @@ -277,11 +298,12 @@ def convert_image(img_node, translator, src_fname=None): if in_ext == '.dot': kernellog.verbose(app, 'convert DOT to: {out}/' + _name) - if translator.builder.format == 'latex': + if translator.builder.format == 'latex' and not dot_Tpdf: svg_fname = path.join(translator.builder.outdir, fname + '.svg') ok1 = dot2format(app, src_fname, svg_fname) ok2 = svg2pdf_by_rsvg(app, svg_fname, dst_fname) ok = ok1 and ok2 + else: ok = dot2format(app, src_fname, dst_fname) From 8ccd05697a9d2f837f77a858e81ba13cdb50adac Mon Sep 17 00:00:00 2001 From: Akira Yokosawa Date: Wed, 29 Dec 2021 20:46:58 +0900 Subject: [PATCH 089/294] docs: sphinx/kfigure.py: Use inkscape(1) for SVG -> PDF conversion Using convert(1) of ImageMagick for SVG -> PDF conversion results in PDFs containing raster (bitmap) images which sometimes look blurry. Ideally speaking, SVG to PDF conversion should retain vector graphics in SVG. rsvg-convert(1) can do such conversions with regard to SVG files generated by dot(1). Unfortunately, rsvg-convert(1) does not cover some of SVG features specific to Inkscape. inkscape(1) of Inkscape naturally covers such SVG features. So add a route in svg2pdf() so that inkscape(1) is used when it is available. Note: After this change, if you have Inkscape installed, ImageMagick nor librsvg are not required. Signed-off-by: Akira Yokosawa Cc: Jonathan Corbet Cc: Mauro Carvalho Chehab Link: https://lore.kernel.org/r/3eea2a8d-c52d-ee07-cf7b-83784c6f6e4b@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/sphinx/kfigure.py | 68 +++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 20 deletions(-) diff --git a/Documentation/sphinx/kfigure.py b/Documentation/sphinx/kfigure.py index 77b0d15dba31..e616e49669eb 100644 --- a/Documentation/sphinx/kfigure.py +++ b/Documentation/sphinx/kfigure.py @@ -37,6 +37,7 @@ u""" * SVG to PDF: To generate PDF, you need at least one of this tools: - ``convert(1)``: ImageMagick (https://www.imagemagick.org) + - ``inkscape(1)``: Inkscape (https://inkscape.org/) List of customizations: @@ -121,6 +122,11 @@ convert_cmd = None # librsvg's rsvg-convert(1) support rsvg_convert_cmd = None +# Inkscape's inkscape(1) support +inkscape_cmd = None +# Inkscape prior to 1.0 uses different command options +inkscape_ver_one = False + def setup(app): # check toolchain first @@ -169,11 +175,13 @@ def setupTools(app): This function is called once, when the builder is initiated. """ global dot_cmd, dot_Tpdf, convert_cmd, rsvg_convert_cmd # pylint: disable=W0603 + global inkscape_cmd, inkscape_ver_one # pylint: disable=W0603 kernellog.verbose(app, "kfigure: check installed tools ...") dot_cmd = which('dot') convert_cmd = which('convert') rsvg_convert_cmd = which('rsvg-convert') + inkscape_cmd = which('inkscape') if dot_cmd: kernellog.verbose(app, "use dot(1) from: " + dot_cmd) @@ -190,25 +198,37 @@ def setupTools(app): else: kernellog.warn(app, "dot(1) not found, for better output quality install " "graphviz from https://www.graphviz.org") - if convert_cmd: - kernellog.verbose(app, "use convert(1) from: " + convert_cmd) - else: - kernellog.warn(app, - "convert(1) not found, for SVG to PDF conversion install " - "ImageMagick (https://www.imagemagick.org)") - if rsvg_convert_cmd: - kernellog.verbose(app, "use rsvg-convert(1) from: " + rsvg_convert_cmd) - kernellog.verbose(app, "use 'dot -Tsvg' and rsvg-convert(1) for DOT -> PDF conversion") + if inkscape_cmd: + kernellog.verbose(app, "use inkscape(1) from: " + inkscape_cmd) + inkscape_ver = subprocess.check_output([inkscape_cmd, '--version']) + ver_one_ptn = b'Inkscape 1' + inkscape_ver_one = re.search(ver_one_ptn, inkscape_ver) + convert_cmd = None + rsvg_convert_cmd = None dot_Tpdf = False + else: - kernellog.verbose(app, - "rsvg-convert(1) not found.\n" - " SVG -> PDF conversion by convert() can be poor quality.\n" - " Install librsvg (https://gitlab.gnome.org/GNOME/librsvg)") - if dot_Tpdf: - kernellog.verbose(app, "use 'dot -Tpdf' for DOT -> PDF conversion") + if convert_cmd: + kernellog.verbose(app, "use convert(1) from: " + convert_cmd) else: - kernellog.verbose(app, "use 'dot -Tsvg' and convert(1) for DOT -> PDF conversion") + kernellog.warn(app, + "Neither inkscape(1) nor convert(1) found.\n" + "For SVG to PDF conversion, " + "install either Inkscape (https://inkscape.org/) (preferred) or\n" + "ImageMagick (https://www.imagemagick.org)") + + if rsvg_convert_cmd: + kernellog.verbose(app, "use rsvg-convert(1) from: " + rsvg_convert_cmd) + kernellog.verbose(app, "use 'dot -Tsvg' and rsvg-convert(1) for DOT -> PDF conversion") + dot_Tpdf = False + else: + kernellog.verbose(app, + "rsvg-convert(1) not found.\n" + " SVG rendering of convert(1) is done by ImageMagick-native renderer.") + if dot_Tpdf: + kernellog.verbose(app, "use 'dot -Tpdf' for DOT -> PDF conversion") + else: + kernellog.verbose(app, "use 'dot -Tsvg' and convert(1) for DOT -> PDF conversion") # integrate conversion tools @@ -274,7 +294,7 @@ def convert_image(img_node, translator, src_fname=None): elif in_ext == '.svg': if translator.builder.format == 'latex': - if convert_cmd is None: + if not inkscape_cmd and convert_cmd is None: kernellog.verbose(app, "no SVG to PDF conversion available / include SVG raw.") img_node.replace_self(file2literal(src_fname)) @@ -342,16 +362,24 @@ def dot2format(app, dot_fname, out_fname): return bool(exit_code == 0) def svg2pdf(app, svg_fname, pdf_fname): - """Converts SVG to PDF with ``convert(1)`` command. + """Converts SVG to PDF with ``inkscape(1)`` or ``convert(1)`` command. - Uses ``convert(1)`` from ImageMagick (https://www.imagemagick.org) for - conversion. Returns ``True`` on success and ``False`` if an error occurred. + Uses ``inkscape(1)`` from Inkscape (https://inkscape.org/) or ``convert(1)`` + from ImageMagick (https://www.imagemagick.org) for conversion. + Returns ``True`` on success and ``False`` if an error occurred. * ``svg_fname`` pathname of the input SVG file with extension (``.svg``) * ``pdf_name`` pathname of the output PDF file with extension (``.pdf``) """ cmd = [convert_cmd, svg_fname, pdf_fname] + + if inkscape_cmd: + if inkscape_ver_one: + cmd = [inkscape_cmd, '-o', pdf_fname, svg_fname] + else: + cmd = [inkscape_cmd, '-z', '--export-pdf=%s' % pdf_fname, svg_fname] + # use stdout and stderr from parent exit_code = subprocess.call(cmd) if exit_code != 0: From f30a7ac8c6100e88dc416b675425541a337a46c8 Mon Sep 17 00:00:00 2001 From: Akira Yokosawa Date: Wed, 29 Dec 2021 20:47:56 +0900 Subject: [PATCH 090/294] docs: sphinx/kfigure.py: Delegate inkscape msg to kernellog.verbose Depending on its version, distro config, and system-setup type, inkscape(1) emits various warning messages which are harmless in command-line uses. List of such warning messages (incomplete, long ones wrapped): - Gtk-Message: hh:mm:ss.nnn: Failed to load module "canberra-gtk-module" - Unable to init server: Could not connect: Connection refused - Failed to get connection - ** (inkscape:xxx): CRITICAL **: hh:mm:ss.nnn: dbus_g_proxy_new_for_name: assertion 'connection != NULL' failed - ** (inkscape:xxx): CRITICAL **: hh:mm:ss.nnn: dbus_g_proxy_call: assertion 'DBUS_IS_G_PROXY (proxy)' failed - ** (inkscape:xxx): CRITICAL **: hh:mm:ss.nnn: dbus_g_connection_register_g_object: assertion 'connection != NULL' failed - ** (inkscape:xxx): WARNING **: hh:mm:ss.nnn: Fonts dir '/usr/share/inkscape/fonts' does not exist and will be ignored. To avoid unnecessary anxiety, capture the message and output it via kernellog.verbose or kernellog.warn depending on the exit code. Signed-off-by: Akira Yokosawa Cc: Jonathan Corbet Cc: Mauro Carvalho Chehab Cc: Randy Dunlap Link: https://lore.kernel.org/r/e26a7b53-9155-8394-4a31-6006379b65a5@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/sphinx/kfigure.py | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/Documentation/sphinx/kfigure.py b/Documentation/sphinx/kfigure.py index e616e49669eb..24d2b2addcce 100644 --- a/Documentation/sphinx/kfigure.py +++ b/Documentation/sphinx/kfigure.py @@ -200,7 +200,8 @@ def setupTools(app): "graphviz from https://www.graphviz.org") if inkscape_cmd: kernellog.verbose(app, "use inkscape(1) from: " + inkscape_cmd) - inkscape_ver = subprocess.check_output([inkscape_cmd, '--version']) + inkscape_ver = subprocess.check_output([inkscape_cmd, '--version'], + stderr=subprocess.DEVNULL) ver_one_ptn = b'Inkscape 1' inkscape_ver_one = re.search(ver_one_ptn, inkscape_ver) convert_cmd = None @@ -373,17 +374,32 @@ def svg2pdf(app, svg_fname, pdf_fname): """ cmd = [convert_cmd, svg_fname, pdf_fname] + cmd_name = 'convert(1)' if inkscape_cmd: + cmd_name = 'inkscape(1)' if inkscape_ver_one: cmd = [inkscape_cmd, '-o', pdf_fname, svg_fname] else: cmd = [inkscape_cmd, '-z', '--export-pdf=%s' % pdf_fname, svg_fname] - # use stdout and stderr from parent - exit_code = subprocess.call(cmd) + try: + warning_msg = subprocess.check_output(cmd, stderr=subprocess.STDOUT) + exit_code = 0 + except subprocess.CalledProcessError as err: + warning_msg = err.output + exit_code = err.returncode + pass + if exit_code != 0: kernellog.warn(app, "Error #%d when calling: %s" % (exit_code, " ".join(cmd))) + if warning_msg: + kernellog.warn(app, "Warning msg from %s: %s" + % (cmd_name, str(warning_msg, 'utf-8'))) + elif warning_msg: + kernellog.verbose(app, "Warning msg from %s (likely harmless):\n%s" + % (cmd_name, str(warning_msg, 'utf-8'))) + return bool(exit_code == 0) def svg2pdf_by_rsvg(app, svg_fname, pdf_fname): From c95aa2bab974394809edea28690f6504a15791b6 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 8 Feb 2022 16:15:46 -0800 Subject: [PATCH 091/294] thermal: intel: hfi: INTEL_HFI_THERMAL depends on NET THERMAL_NETLINK depends on NET and since 'select' does not follow any dependency chain, INTEL_HFI_THERMAL also should depend on NET. Fix one Kconfig warning and 48 subsequent build errors: WARNING: unmet direct dependencies detected for THERMAL_NETLINK Depends on [n]: THERMAL [=y] && NET [=n] Selected by [y]: - INTEL_HFI_THERMAL [=y] && THERMAL [=y] && (X86 [=y] || X86_INTEL_QUARK [=n] || COMPILE_TEST [=y]) && CPU_SUP_INTEL [=y] && X86_THERMAL_VECTOR [=y] Fixes: bd30cdfd9bd7 ("thermal: intel: hfi: Notify user space for HFI events") Signed-off-by: Randy Dunlap Reviewed-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki --- drivers/thermal/intel/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/thermal/intel/Kconfig b/drivers/thermal/intel/Kconfig index 6cf3fe36a4ae..f0c845679250 100644 --- a/drivers/thermal/intel/Kconfig +++ b/drivers/thermal/intel/Kconfig @@ -102,6 +102,7 @@ config INTEL_MENLOW config INTEL_HFI_THERMAL bool "Intel Hardware Feedback Interface" + depends on NET depends on CPU_SUP_INTEL depends on X86_THERMAL_VECTOR select THERMAL_NETLINK From bd265aadd1b7eabd4bf381dc4b0bc48ebd569e88 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Wed, 1 Dec 2021 06:27:59 -0800 Subject: [PATCH 092/294] MAINTAINERS: Add Frederic and Neeraj to their RCU files Adding Frederic as an RCU maintainer for kernel/rcu/tree_nocb.h given his work with offloading and de-offloading callbacks from CPUs. Also adding Neeraj for kernel/rcu/tasks.h given his focused work on RCU Tasks Trace. As in I am reasonably certain that each understands the full contents of the corresponding file. Cc: Neeraj Upadhyay Signed-off-by: Paul E. McKenney Acked-by: Frederic Weisbecker --- MAINTAINERS | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index ea3e6c914384..5ad49de532b1 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -16298,6 +16298,8 @@ F: tools/testing/selftests/resctrl/ READ-COPY UPDATE (RCU) M: "Paul E. McKenney" +M: Frederic Weisbecker (kernel/rcu/tree_nocb.h) +M: Neeraj Upadhyay (kernel/rcu/tasks.h) M: Josh Triplett R: Steven Rostedt R: Mathieu Desnoyers From 150154aae4311e7e6458903baecdc8fffe981ed3 Mon Sep 17 00:00:00 2001 From: "Uladzislau Rezki (Sony)" Date: Wed, 1 Dec 2021 10:20:53 +0100 Subject: [PATCH 093/294] rcu: Fix description of kvfree_rcu() The kvfree_rcu() header comment's description of the "ptr" parameter is unclear, therefore rephrase it to make it clear that it is a pointer to the memory to eventually be passed to kvfree(). Reported-by: Steven Rostedt Signed-off-by: Uladzislau Rezki (Sony) Signed-off-by: Paul E. McKenney --- include/linux/rcupdate.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 88b42eb46406..9d7df8d36af0 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -924,7 +924,7 @@ static inline notrace void rcu_read_unlock_sched_notrace(void) * * kvfree_rcu(ptr); * - * where @ptr is a pointer to kvfree(). + * where @ptr is the pointer to be freed by kvfree(). * * Please note, head-less way of freeing is permitted to * use from a context that has to follow might_sleep() From 218b957a6959a2fb5b3967fc824072bb89ac2611 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Wed, 8 Dec 2021 23:41:53 +0000 Subject: [PATCH 094/294] rcu: Add mutex for rcu boost kthread spawning and affinity setting As we handle parallel CPU bringup, we will need to take care to avoid spawning multiple boost threads, or race conditions when setting their affinity. Spotted by Paul McKenney. Signed-off-by: David Woodhouse Reviewed-by: Frederic Weisbecker Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 1 + kernel/rcu/tree.h | 3 +++ kernel/rcu/tree_plugin.h | 10 ++++++++-- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index a4c25a6283b0..d1d1a8c51223 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -4570,6 +4570,7 @@ static void __init rcu_init_one(void) init_waitqueue_head(&rnp->exp_wq[2]); init_waitqueue_head(&rnp->exp_wq[3]); spin_lock_init(&rnp->exp_lock); + mutex_init(&rnp->boost_kthread_mutex); } } diff --git a/kernel/rcu/tree.h b/kernel/rcu/tree.h index 486fc901bd08..3b8b60de07c3 100644 --- a/kernel/rcu/tree.h +++ b/kernel/rcu/tree.h @@ -110,6 +110,9 @@ struct rcu_node { /* side effect, not as a lock. */ unsigned long boost_time; /* When to start boosting (jiffies). */ + struct mutex boost_kthread_mutex; + /* Exclusion for thread spawning and affinity */ + /* manipulation. */ struct task_struct *boost_kthread_task; /* kthread that takes care of priority */ /* boosting for this rcu_node structure. */ diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index c5b45c2f68a1..07845dcd33c5 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -1172,15 +1172,16 @@ static void rcu_spawn_one_boost_kthread(struct rcu_node *rnp) struct sched_param sp; struct task_struct *t; + mutex_lock(&rnp->boost_kthread_mutex); if (rnp->boost_kthread_task || !rcu_scheduler_fully_active) - return; + goto out; rcu_state.boost = 1; t = kthread_create(rcu_boost_kthread, (void *)rnp, "rcub/%d", rnp_index); if (WARN_ON_ONCE(IS_ERR(t))) - return; + goto out; raw_spin_lock_irqsave_rcu_node(rnp, flags); rnp->boost_kthread_task = t; @@ -1188,6 +1189,9 @@ static void rcu_spawn_one_boost_kthread(struct rcu_node *rnp) sp.sched_priority = kthread_prio; sched_setscheduler_nocheck(t, SCHED_FIFO, &sp); wake_up_process(t); /* get to TASK_INTERRUPTIBLE quickly. */ + + out: + mutex_unlock(&rnp->boost_kthread_mutex); } /* @@ -1210,6 +1214,7 @@ static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu) return; if (!zalloc_cpumask_var(&cm, GFP_KERNEL)) return; + mutex_lock(&rnp->boost_kthread_mutex); for_each_leaf_node_possible_cpu(rnp, cpu) if ((mask & leaf_node_cpu_bit(rnp, cpu)) && cpu != outgoingcpu) @@ -1218,6 +1223,7 @@ static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu) if (cpumask_weight(cm) == 0) cpumask_copy(cm, housekeeping_cpumask(HK_FLAG_RCU)); set_cpus_allowed_ptr(t, cm); + mutex_unlock(&rnp->boost_kthread_mutex); free_cpumask_var(cm); } From 1fe09ebe7a9c9907f516779fbe4954165dd01529 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Sat, 18 Dec 2021 09:30:33 -0800 Subject: [PATCH 095/294] rcu: Inline __call_rcu() into call_rcu() Because __call_rcu() is invoked only by call_rcu(), this commit inlines the former into the latter. Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 91 ++++++++++++++++++++++------------------------- 1 file changed, 42 insertions(+), 49 deletions(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index d1d1a8c51223..f1bb7ccc0084 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -2995,9 +2995,47 @@ static void check_cb_ovld(struct rcu_data *rdp) raw_spin_unlock_rcu_node(rnp); } -/* Helper function for call_rcu() and friends. */ -static void -__call_rcu(struct rcu_head *head, rcu_callback_t func) +/** + * call_rcu() - Queue an RCU callback for invocation after a grace period. + * @head: structure to be used for queueing the RCU updates. + * @func: actual callback function to be invoked after the grace period + * + * The callback function will be invoked some time after a full grace + * period elapses, in other words after all pre-existing RCU read-side + * critical sections have completed. However, the callback function + * might well execute concurrently with RCU read-side critical sections + * that started after call_rcu() was invoked. + * + * RCU read-side critical sections are delimited by rcu_read_lock() + * and rcu_read_unlock(), and may be nested. In addition, but only in + * v5.0 and later, regions of code across which interrupts, preemption, + * or softirqs have been disabled also serve as RCU read-side critical + * sections. This includes hardware interrupt handlers, softirq handlers, + * and NMI handlers. + * + * Note that all CPUs must agree that the grace period extended beyond + * all pre-existing RCU read-side critical section. On systems with more + * than one CPU, this means that when "func()" is invoked, each CPU is + * guaranteed to have executed a full memory barrier since the end of its + * last RCU read-side critical section whose beginning preceded the call + * to call_rcu(). It also means that each CPU executing an RCU read-side + * critical section that continues beyond the start of "func()" must have + * executed a memory barrier after the call_rcu() but before the beginning + * of that RCU read-side critical section. Note that these guarantees + * include CPUs that are offline, idle, or executing in user mode, as + * well as CPUs that are executing in the kernel. + * + * Furthermore, if CPU A invoked call_rcu() and CPU B invoked the + * resulting RCU callback function "func()", then both CPU A and CPU B are + * guaranteed to execute a full memory barrier during the time interval + * between the call to call_rcu() and the invocation of "func()" -- even + * if CPU A and CPU B are the same CPU (but again only if the system has + * more than one CPU). + * + * Implementation of these memory-ordering guarantees is described here: + * Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst. + */ +void call_rcu(struct rcu_head *head, rcu_callback_t func) { static atomic_t doublefrees; unsigned long flags; @@ -3011,7 +3049,7 @@ __call_rcu(struct rcu_head *head, rcu_callback_t func) /* * Probable double call_rcu(), so leak the callback. * Use rcu:rcu_callback trace event to find the previous - * time callback was passed to __call_rcu(). + * time callback was passed to call_rcu(). */ if (atomic_inc_return(&doublefrees) < 4) { pr_err("%s(): Double-freed CB %p->%pS()!!! ", __func__, head, head->func); @@ -3060,51 +3098,6 @@ __call_rcu(struct rcu_head *head, rcu_callback_t func) local_irq_restore(flags); } } - -/** - * call_rcu() - Queue an RCU callback for invocation after a grace period. - * @head: structure to be used for queueing the RCU updates. - * @func: actual callback function to be invoked after the grace period - * - * The callback function will be invoked some time after a full grace - * period elapses, in other words after all pre-existing RCU read-side - * critical sections have completed. However, the callback function - * might well execute concurrently with RCU read-side critical sections - * that started after call_rcu() was invoked. - * - * RCU read-side critical sections are delimited by rcu_read_lock() - * and rcu_read_unlock(), and may be nested. In addition, but only in - * v5.0 and later, regions of code across which interrupts, preemption, - * or softirqs have been disabled also serve as RCU read-side critical - * sections. This includes hardware interrupt handlers, softirq handlers, - * and NMI handlers. - * - * Note that all CPUs must agree that the grace period extended beyond - * all pre-existing RCU read-side critical section. On systems with more - * than one CPU, this means that when "func()" is invoked, each CPU is - * guaranteed to have executed a full memory barrier since the end of its - * last RCU read-side critical section whose beginning preceded the call - * to call_rcu(). It also means that each CPU executing an RCU read-side - * critical section that continues beyond the start of "func()" must have - * executed a memory barrier after the call_rcu() but before the beginning - * of that RCU read-side critical section. Note that these guarantees - * include CPUs that are offline, idle, or executing in user mode, as - * well as CPUs that are executing in the kernel. - * - * Furthermore, if CPU A invoked call_rcu() and CPU B invoked the - * resulting RCU callback function "func()", then both CPU A and CPU B are - * guaranteed to execute a full memory barrier during the time interval - * between the call to call_rcu() and the invocation of "func()" -- even - * if CPU A and CPU B are the same CPU (but again only if the system has - * more than one CPU). - * - * Implementation of these memory-ordering guarantees is described here: - * Documentation/RCU/Design/Memory-Ordering/Tree-RCU-Memory-Ordering.rst. - */ -void call_rcu(struct rcu_head *head, rcu_callback_t func) -{ - __call_rcu(head, func); -} EXPORT_SYMBOL_GPL(call_rcu); From d818cc76e2b4d5f6cebf8c7ce1160d652d7e572b Mon Sep 17 00:00:00 2001 From: Zqiang Date: Sun, 26 Dec 2021 08:52:04 +0800 Subject: [PATCH 096/294] kasan: Record work creation stack trace with interrupts enabled Recording the work creation stack trace for KASAN reports in call_rcu() is expensive, due to unwinding the stack, but also due to acquiring depot_lock inside stackdepot (which may be contended). Because calling kasan_record_aux_stack_noalloc() does not require interrupts to already be disabled, this may unnecessarily extend the time with interrupts disabled. Therefore, move calling kasan_record_aux_stack() before the section with interrupts disabled. Acked-by: Marco Elver Signed-off-by: Zqiang Signed-off-by: Paul E. McKenney --- kernel/rcu/tree.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/rcu/tree.c b/kernel/rcu/tree.c index f1bb7ccc0084..ca8d7dd026ee 100644 --- a/kernel/rcu/tree.c +++ b/kernel/rcu/tree.c @@ -3060,8 +3060,8 @@ void call_rcu(struct rcu_head *head, rcu_callback_t func) } head->func = func; head->next = NULL; - local_irq_save(flags); kasan_record_aux_stack_noalloc(head); + local_irq_save(flags); rdp = this_cpu_ptr(&rcu_data); /* Add the callback to our list. */ From c09929031018913b5783872a8b8cdddef4a543c7 Mon Sep 17 00:00:00 2001 From: "Paul E. McKenney" Date: Tue, 4 Jan 2022 10:34:34 -0800 Subject: [PATCH 097/294] rcu: Mark writes to the rcu_segcblist structure's ->flags field KCSAN reports data races between the rcu_segcblist_clear_flags() and rcu_segcblist_set_flags() functions, though misreporting the latter as a call to rcu_segcblist_is_enabled() from call_rcu(). This commit converts the updates of this field to WRITE_ONCE(), relying on the resulting unmarked reads to continue to detect buggy concurrent writes to this field. Reported-by: Zhouyi Zhou Signed-off-by: Paul E. McKenney Cc: Frederic Weisbecker --- kernel/rcu/rcu_segcblist.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/kernel/rcu/rcu_segcblist.h b/kernel/rcu/rcu_segcblist.h index e373fbe44da5..431cee212467 100644 --- a/kernel/rcu/rcu_segcblist.h +++ b/kernel/rcu/rcu_segcblist.h @@ -56,13 +56,13 @@ static inline long rcu_segcblist_n_cbs(struct rcu_segcblist *rsclp) static inline void rcu_segcblist_set_flags(struct rcu_segcblist *rsclp, int flags) { - rsclp->flags |= flags; + WRITE_ONCE(rsclp->flags, rsclp->flags | flags); } static inline void rcu_segcblist_clear_flags(struct rcu_segcblist *rsclp, int flags) { - rsclp->flags &= ~flags; + WRITE_ONCE(rsclp->flags, rsclp->flags & ~flags); } static inline bool rcu_segcblist_test_flags(struct rcu_segcblist *rsclp, From 58d4292bd037b01fbb940a5170817f7d40caa9d5 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Fri, 14 Jan 2022 16:07:28 -0800 Subject: [PATCH 098/294] rcu: Uninline multi-use function: finish_rcuwait() This is a rarely used function, so uninlining its 3 instructions is probably a win or a wash - but the main motivation is to make independent of task_struct details. Signed-off-by: Ingo Molnar Signed-off-by: Paul E. McKenney --- include/linux/rcuwait.h | 6 +----- kernel/rcu/update.c | 7 +++++++ 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/include/linux/rcuwait.h b/include/linux/rcuwait.h index 61c56cca95c4..8052d34da782 100644 --- a/include/linux/rcuwait.h +++ b/include/linux/rcuwait.h @@ -47,11 +47,7 @@ static inline void prepare_to_rcuwait(struct rcuwait *w) rcu_assign_pointer(w->task, current); } -static inline void finish_rcuwait(struct rcuwait *w) -{ - rcu_assign_pointer(w->task, NULL); - __set_current_state(TASK_RUNNING); -} +extern void finish_rcuwait(struct rcuwait *w); #define rcuwait_wait_event(w, condition, state) \ ({ \ diff --git a/kernel/rcu/update.c b/kernel/rcu/update.c index 156892c22bb5..180ff9c41fa8 100644 --- a/kernel/rcu/update.c +++ b/kernel/rcu/update.c @@ -407,6 +407,13 @@ void __wait_rcu_gp(bool checktiny, int n, call_rcu_func_t *crcu_array, } EXPORT_SYMBOL_GPL(__wait_rcu_gp); +void finish_rcuwait(struct rcuwait *w) +{ + rcu_assign_pointer(w->task, NULL); + __set_current_state(TASK_RUNNING); +} +EXPORT_SYMBOL_GPL(finish_rcuwait); + #ifdef CONFIG_DEBUG_OBJECTS_RCU_HEAD void init_rcu_head(struct rcu_head *head) { From e6339d3b443c436c3b8f45eefec2212a8c07065d Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Fri, 14 Jan 2022 16:16:55 -0800 Subject: [PATCH 099/294] rcu: Remove __read_mostly annotations from rcu_scheduler_active externs Remove the __read_mostly attributes from the rcu_scheduler_active extern declarations, because these attributes are ignored for prototypes and we'd have to include the full header to gain this functionally pointless attribute defined. Signed-off-by: Ingo Molnar Signed-off-by: Paul E. McKenney --- include/linux/rcupdate.h | 2 +- include/linux/rcutree.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index 9d7df8d36af0..e7c39c200e2b 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -84,7 +84,7 @@ static inline int rcu_preempt_depth(void) /* Internal to kernel */ void rcu_init(void); -extern int rcu_scheduler_active __read_mostly; +extern int rcu_scheduler_active; void rcu_sched_clock_irq(int user); void rcu_report_dead(unsigned int cpu); void rcutree_migrate_callbacks(int cpu); diff --git a/include/linux/rcutree.h b/include/linux/rcutree.h index 53209d669400..76665db179fa 100644 --- a/include/linux/rcutree.h +++ b/include/linux/rcutree.h @@ -62,7 +62,7 @@ static inline void rcu_irq_exit_check_preempt(void) { } void exit_rcu(void); void rcu_scheduler_starting(void); -extern int rcu_scheduler_active __read_mostly; +extern int rcu_scheduler_active; void rcu_end_inkernel_boot(void); bool rcu_inkernel_boot_has_ended(void); bool rcu_is_watching(void); From 6a2c1d450a6a328027280a854019c55de989e14e Mon Sep 17 00:00:00 2001 From: Yury Norov Date: Sun, 23 Jan 2022 10:38:53 -0800 Subject: [PATCH 100/294] rcu: Replace cpumask_weight with cpumask_empty where appropriate In some places, RCU code calls cpumask_weight() to check if any bit of a given cpumask is set. We can do it more efficiently with cpumask_empty() because cpumask_empty() stops traversing the cpumask as soon as it finds first set bit, while cpumask_weight() counts all bits unconditionally. Signed-off-by: Yury Norov Acked-by: Frederic Weisbecker Signed-off-by: Paul E. McKenney --- kernel/rcu/tree_nocb.h | 4 ++-- kernel/rcu/tree_plugin.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/kernel/rcu/tree_nocb.h b/kernel/rcu/tree_nocb.h index eeafb546a7a0..f83c7b1d6110 100644 --- a/kernel/rcu/tree_nocb.h +++ b/kernel/rcu/tree_nocb.h @@ -1169,7 +1169,7 @@ void __init rcu_init_nohz(void) struct rcu_data *rdp; #if defined(CONFIG_NO_HZ_FULL) - if (tick_nohz_full_running && cpumask_weight(tick_nohz_full_mask)) + if (tick_nohz_full_running && !cpumask_empty(tick_nohz_full_mask)) need_rcu_nocb_mask = true; #endif /* #if defined(CONFIG_NO_HZ_FULL) */ @@ -1348,7 +1348,7 @@ static void __init rcu_organize_nocb_kthreads(void) */ void rcu_bind_current_to_nocb(void) { - if (cpumask_available(rcu_nocb_mask) && cpumask_weight(rcu_nocb_mask)) + if (cpumask_available(rcu_nocb_mask) && !cpumask_empty(rcu_nocb_mask)) WARN_ON(sched_setaffinity(current->pid, rcu_nocb_mask)); } EXPORT_SYMBOL_GPL(rcu_bind_current_to_nocb); diff --git a/kernel/rcu/tree_plugin.h b/kernel/rcu/tree_plugin.h index 07845dcd33c5..efd0c87d2ffa 100644 --- a/kernel/rcu/tree_plugin.h +++ b/kernel/rcu/tree_plugin.h @@ -1220,7 +1220,7 @@ static void rcu_boost_kthread_setaffinity(struct rcu_node *rnp, int outgoingcpu) cpu != outgoingcpu) cpumask_set_cpu(cpu, cm); cpumask_and(cm, cm, housekeeping_cpumask(HK_FLAG_RCU)); - if (cpumask_weight(cm) == 0) + if (cpumask_empty(cm)) cpumask_copy(cm, housekeeping_cpumask(HK_FLAG_RCU)); set_cpus_allowed_ptr(t, cm); mutex_unlock(&rnp->boost_kthread_mutex); From 631e3893c35e116d16b81b41bea6ba2143db4fa4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 1 Feb 2022 20:18:10 +0100 Subject: [PATCH 101/294] ACPI: PM: Print additional debug message in acpi_s2idle_wake() Make acpi_s2idle_wake() print an additional debug message when the SCI is going to be rearmed for system wakeup to help diagnose wakeup-related issues. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/sleep.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/acpi/sleep.c b/drivers/acpi/sleep.c index d4fbea91ab6b..d068ff42fce4 100644 --- a/drivers/acpi/sleep.c +++ b/drivers/acpi/sleep.c @@ -758,6 +758,8 @@ bool acpi_s2idle_wake(void) return true; } + pm_pr_dbg("Rearming ACPI SCI for wakeup\n"); + pm_wakeup_clear(acpi_sci_irq); rearm_wake_irq(acpi_sci_irq); } From 977dc3082285206e7b1fcbc4496671194cfb7980 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 1 Feb 2022 20:18:56 +0100 Subject: [PATCH 102/294] ACPI: EC / PM: Print additional debug message in acpi_ec_dispatch_gpe() Make acpi_ec_dispatch_gpe() print an additional debug message after seeing the EC GPE status bit set to help diagnose wakeup-related issues. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index 46710380a402..f039565d59ad 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -2081,8 +2081,11 @@ bool acpi_ec_dispatch_gpe(void) */ spin_lock_irq(&first_ec->lock); - if (acpi_ec_gpe_status_set(first_ec)) + if (acpi_ec_gpe_status_set(first_ec)) { + pm_pr_dbg("ACPI EC GPE status set\n"); + work_in_progress = advance_transaction(first_ec, false); + } spin_unlock_irq(&first_ec->lock); From 9aa60f3cbafb0facc15a6114df7616a1bf23a88d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Feb 2022 18:40:20 +0100 Subject: [PATCH 103/294] ACPI: EC: Do not return result from advance_transaction() Notice that the if the event state is EC_EVENT_READY, the event handling work cannot be pending, so it is not necessary to check the return value of queue_work() in acpi_ec_submit_event(). Moreover, whether or not there is any EC work pending at the moment can always be checked by looking at the events_in_progress and queries_in_progress counters, so acpi_ec_submit_event() and consequently advance_transaction() need not return results. Accordingly, make acpi_ec_dispatch_gpe() always use the counters mentioned above (for first_ec) to check if there is any pending EC work to flush and turn both acpi_ec_submit_event() and advance_transaction() into void functions (again, because they were void functions in the past). While at it, add a clarifying comment about the acpi_ec_mask_events() call in advance_transaction(). Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index f039565d59ad..e202c0c370b1 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -168,7 +168,7 @@ struct acpi_ec_query { }; static int acpi_ec_submit_query(struct acpi_ec *ec); -static bool advance_transaction(struct acpi_ec *ec, bool interrupt); +static void advance_transaction(struct acpi_ec *ec, bool interrupt); static void acpi_ec_event_handler(struct work_struct *work); struct acpi_ec *first_ec; @@ -441,11 +441,15 @@ static bool acpi_ec_submit_flushable_request(struct acpi_ec *ec) return true; } -static bool acpi_ec_submit_event(struct acpi_ec *ec) +static void acpi_ec_submit_event(struct acpi_ec *ec) { + /* + * It is safe to mask the events here, because acpi_ec_close_event() + * will run at least once after this. + */ acpi_ec_mask_events(ec); if (!acpi_ec_event_enabled(ec)) - return false; + return; if (ec->event_state == EC_EVENT_READY) { ec_dbg_evt("Command(%s) submitted/blocked", @@ -460,17 +464,11 @@ static bool acpi_ec_submit_event(struct acpi_ec *ec) * queue up the event work to start the same loop again. */ if (ec->events_to_process++ > 0) - return true; + return; ec->events_in_progress++; - return queue_work(ec_wq, &ec->work); + queue_work(ec_wq, &ec->work); } - - /* - * The event handling work has not been completed yet, so it needs to be - * flushed. - */ - return true; } static void acpi_ec_complete_event(struct acpi_ec *ec) @@ -655,11 +653,10 @@ static void acpi_ec_spurious_interrupt(struct acpi_ec *ec, struct transaction *t acpi_ec_mask_events(ec); } -static bool advance_transaction(struct acpi_ec *ec, bool interrupt) +static void advance_transaction(struct acpi_ec *ec, bool interrupt) { struct transaction *t = ec->curr; bool wakeup = false; - bool ret = false; u8 status; ec_dbg_stm("%s (%d)", interrupt ? "IRQ" : "TASK", smp_processor_id()); @@ -724,12 +721,10 @@ static bool advance_transaction(struct acpi_ec *ec, bool interrupt) out: if (status & ACPI_EC_FLAG_SCI) - ret = acpi_ec_submit_event(ec); + acpi_ec_submit_event(ec); if (wakeup && interrupt) wake_up(&ec->wait); - - return ret; } static void start_transaction(struct acpi_ec *ec) @@ -2051,6 +2046,11 @@ void acpi_ec_set_gpe_wake_mask(u8 action) acpi_set_gpe_wake_mask(NULL, first_ec->gpe, action); } +static bool acpi_ec_work_in_progress(struct acpi_ec *ec) +{ + return ec->events_in_progress + ec->queries_in_progress > 0; +} + bool acpi_ec_dispatch_gpe(void) { bool work_in_progress = false; @@ -2084,7 +2084,8 @@ bool acpi_ec_dispatch_gpe(void) if (acpi_ec_gpe_status_set(first_ec)) { pm_pr_dbg("ACPI EC GPE status set\n"); - work_in_progress = advance_transaction(first_ec, false); + advance_transaction(first_ec, false); + work_in_progress = acpi_ec_work_in_progress(first_ec); } spin_unlock_irq(&first_ec->lock); @@ -2102,8 +2103,7 @@ bool acpi_ec_dispatch_gpe(void) spin_lock_irq(&first_ec->lock); - work_in_progress = first_ec->events_in_progress + - first_ec->queries_in_progress > 0; + work_in_progress = acpi_ec_work_in_progress(first_ec); spin_unlock_irq(&first_ec->lock); } while (work_in_progress && !pm_wakeup_pending()); From 54b86141d71db2a16743cfa4a3417180d7feb24a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Feb 2022 18:40:55 +0100 Subject: [PATCH 104/294] ACPI: EC: Reduce indentation level in acpi_ec_submit_event() The indentation level in acpi_ec_submit_event() can be reduced, so do that and while at it fix a typo in the comment affected by that change. No intentional functional impact. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index e202c0c370b1..bba96df3b88a 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -451,24 +451,25 @@ static void acpi_ec_submit_event(struct acpi_ec *ec) if (!acpi_ec_event_enabled(ec)) return; - if (ec->event_state == EC_EVENT_READY) { - ec_dbg_evt("Command(%s) submitted/blocked", - acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY)); + if (ec->event_state != EC_EVENT_READY) + return; - ec->event_state = EC_EVENT_IN_PROGRESS; - /* - * If events_to_process is greqter than 0 at this point, the - * while () loop in acpi_ec_event_handler() is still running - * and incrementing events_to_process will cause it to invoke - * acpi_ec_submit_query() once more, so it is not necessary to - * queue up the event work to start the same loop again. - */ - if (ec->events_to_process++ > 0) - return; + ec_dbg_evt("Command(%s) submitted/blocked", + acpi_ec_cmd_string(ACPI_EC_COMMAND_QUERY)); - ec->events_in_progress++; - queue_work(ec_wq, &ec->work); - } + ec->event_state = EC_EVENT_IN_PROGRESS; + /* + * If events_to_process is greater than 0 at this point, the while () + * loop in acpi_ec_event_handler() is still running and incrementing + * events_to_process will cause it to invoke acpi_ec_submit_query() once + * more, so it is not necessary to queue up the event work to start the + * same loop again. + */ + if (ec->events_to_process++ > 0) + return; + + ec->events_in_progress++; + queue_work(ec_wq, &ec->work); } static void acpi_ec_complete_event(struct acpi_ec *ec) From 13a62d0e13308d62426c38223a3b6f78878f2173 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Fri, 4 Feb 2022 18:43:14 +0100 Subject: [PATCH 105/294] ACPI: EC: Rearrange code in acpi_ec_submit_event() Rearange acpi_ec_event_handler() so as to avoid releasing ec->lock and acquiring it again right away in the case when ec_event_clearing is not ACPI_EC_EVT_TIMING_EVENT. This also reduces the number of checks done by acpi_ec_event_handler() in that case. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/ec.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/drivers/acpi/ec.c b/drivers/acpi/ec.c index bba96df3b88a..a1b871a418f8 100644 --- a/drivers/acpi/ec.c +++ b/drivers/acpi/ec.c @@ -1238,6 +1238,7 @@ static void acpi_ec_event_handler(struct work_struct *work) acpi_ec_submit_query(ec); spin_lock_irq(&ec->lock); + ec->events_to_process--; } @@ -1246,27 +1247,30 @@ static void acpi_ec_event_handler(struct work_struct *work) * event handling work again regardless of whether or not the query * queued up above is processed successfully. */ - if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT) + if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT) { + bool guard_timeout; + acpi_ec_complete_event(ec); - else - acpi_ec_close_event(ec); - spin_unlock_irq(&ec->lock); + ec_dbg_evt("Event stopped"); - ec_dbg_evt("Event stopped"); + spin_unlock_irq(&ec->lock); + + guard_timeout = !!ec_guard(ec); - if (ec_event_clearing == ACPI_EC_EVT_TIMING_EVENT && ec_guard(ec)) { spin_lock_irq(&ec->lock); /* Take care of SCI_EVT unless someone else is doing that. */ - if (!ec->curr) + if (guard_timeout && !ec->curr) advance_transaction(ec, false); + } else { + acpi_ec_close_event(ec); - spin_unlock_irq(&ec->lock); + ec_dbg_evt("Event stopped"); } - spin_lock_irq(&ec->lock); ec->events_in_progress--; + spin_unlock_irq(&ec->lock); } From 6b22ce002f755e34e9a92ce5a41ffda0b729b892 Mon Sep 17 00:00:00 2001 From: Akira Yokosawa Date: Thu, 10 Feb 2022 10:26:53 +0900 Subject: [PATCH 106/294] docs: Makefile: Add -no-shell-escape option to LATEXOPTS It turns out that LaTeX enables \write18, which allows (some) shell commands to be executed from the document source, by default. This the often-seen warning during a pdfdocs build: restricted \write18 enabled That is a potential security problem and is entirely unnecessary; nothing in the kernel PDF docs build needs that capability. So disable \write18 explicitly. Signed-off-by: Akira Yokosawa Link: https://lore.kernel.org/r/519bd2d9-1bee-03e1-eeb4-d9883c18be0c@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/Makefile b/Documentation/Makefile index 9f4bd42cef18..64d44c1ecad3 100644 --- a/Documentation/Makefile +++ b/Documentation/Makefile @@ -26,7 +26,7 @@ SPHINX_CONF = conf.py PAPER = BUILDDIR = $(obj)/output PDFLATEX = xelatex -LATEXOPTS = -interaction=batchmode +LATEXOPTS = -interaction=batchmode -no-shell-escape ifeq ($(KBUILD_VERBOSE),0) SPHINXOPTS += "-q" From a693396fd569c0a33e4b0f82398fbd6410a7beec Mon Sep 17 00:00:00 2001 From: Frank Rowand Date: Thu, 10 Feb 2022 17:36:30 -0600 Subject: [PATCH 107/294] Documentation: dev-tools: clarify KTAP specification wording Add the spec version to the title line. Explain likely source of "Unknown lines". "Unknown lines" in nested tests are optionally indented. Add "Unknown lines" items to differences between TAP & KTAP list Convert "Major differences between TAP and KTAP" from a bullet list to a table. The bullet list was being formatted as a single paragraph. Reviewed-by: Tim Bird Reviewed-by: David Gow Reviewed-by: Shuah Khan Signed-off-by: Frank Rowand Reviewed-by: Brendan Higgins Link: https://lore.kernel.org/r/20220210233630.3304495-1-frowand.list@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/dev-tools/ktap.rst | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) diff --git a/Documentation/dev-tools/ktap.rst b/Documentation/dev-tools/ktap.rst index 878530cb9c27..d7fe05de40b4 100644 --- a/Documentation/dev-tools/ktap.rst +++ b/Documentation/dev-tools/ktap.rst @@ -1,8 +1,8 @@ .. SPDX-License-Identifier: GPL-2.0 -======================================== -The Kernel Test Anything Protocol (KTAP) -======================================== +=================================================== +The Kernel Test Anything Protocol (KTAP), version 1 +=================================================== TAP, or the Test Anything Protocol is a format for specifying test results used by a number of projects. It's website and specification are found at this `link @@ -174,6 +174,13 @@ There may be lines within KTAP output that do not follow the format of one of the four formats for lines described above. This is allowed, however, they will not influence the status of the tests. +This is an important difference from TAP. Kernel tests may print messages +to the system console or a log file. Both of these destinations may contain +messages either from unrelated kernel or userspace activity, or kernel +messages from non-test code that is invoked by the test. The kernel code +invoked by the test likely is not aware that a test is in progress and +thus can not print the message as a diagnostic message. + Nested tests ------------ @@ -186,10 +193,13 @@ starting with another KTAP version line and test plan, and end with the overall result. If one of the subtests fail, for example, the parent test should also fail. -Additionally, all result lines in a subtest should be indented. One level of +Additionally, all lines in a subtest should be indented. One level of indentation is two spaces: " ". The indentation should begin at the version line and should end before the parent test's result line. +"Unknown lines" are not considered to be lines in a subtest and thus are +allowed to be either indented or not indented. + An example of a test with two nested subtests: .. code-block:: @@ -224,10 +234,15 @@ An example format with multiple levels of nested testing: Major differences between TAP and KTAP -------------------------------------- -Note the major differences between the TAP and KTAP specification: -- yaml and json are not recommended in diagnostic messages -- TODO directive not recognized -- KTAP allows for an arbitrary number of tests to be nested +================================================== ========= =============== +Feature TAP KTAP +================================================== ========= =============== +yaml and json in diagnosic message ok not recommended +TODO directive ok not recognized +allows an arbitrary number of tests to be nested no yes +"Unknown lines" are in category of "Anything else" yes no +"Unknown lines" are incorrect allowed +================================================== ========= =============== The TAP14 specification does permit nested tests, but instead of using another nested version line, uses a line of the form From 013ebb6d822a51ccc8ad162e7c8d02fbd7e90dd1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pali=20Roh=C3=A1r?= Date: Thu, 10 Feb 2022 20:22:00 +0100 Subject: [PATCH 108/294] Documentation: Fix links for udftools project and pktcdvd tool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Pali Rohár Link: https://lore.kernel.org/r/20220210192200.30828-1-pali@kernel.org Signed-off-by: Jonathan Corbet --- Documentation/cdrom/packet-writing.rst | 4 ++-- Documentation/userspace-api/ioctl/ioctl-number.rst | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/cdrom/packet-writing.rst b/Documentation/cdrom/packet-writing.rst index c5c957195a5a..43db58c50d29 100644 --- a/Documentation/cdrom/packet-writing.rst +++ b/Documentation/cdrom/packet-writing.rst @@ -11,7 +11,7 @@ Getting started quick - Compile and install kernel and modules, reboot. - You need the udftools package (pktsetup, mkudffs, cdrwtool). - Download from http://sourceforge.net/projects/linux-udf/ + Download from https://github.com/pali/udftools - Grab a new CD-RW disc and format it (assuming CD-RW is hdc, substitute as appropriate):: @@ -102,7 +102,7 @@ Using the pktcdvd sysfs interface Since Linux 2.6.20, the pktcdvd module has a sysfs interface and can be controlled by it. For example the "pktcdvd" tool uses -this interface. (see http://tom.ist-im-web.de/download/pktcdvd ) +this interface. (see http://tom.ist-im-web.de/linux/software/pktcdvd ) "pktcdvd" works similar to "pktsetup", e.g.:: diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 687efcf245c1..559c436276c6 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -253,7 +253,7 @@ Code Seq# Include File Comments 'l' 00-3F linux/tcfs_fs.h transparent cryptographic file system 'l' 40-7F linux/udf_fs_i.h in development: - + 'm' 00-09 linux/mmtimer.h conflict! 'm' all linux/mtio.h conflict! 'm' all linux/soundcard.h conflict! From 339cf5a2c6fb8559f30b9d4bd82c1dc4d7a16468 Mon Sep 17 00:00:00 2001 From: Tang Yizhou Date: Tue, 8 Feb 2022 21:37:16 +0800 Subject: [PATCH 109/294] docs/zh_CN: Add energy-model Chinese translation Translate power/energy-model.rst into Chinese. Signed-off-by: Tang Yizhou Reviewed-by: Alex Shi Link: https://lore.kernel.org/r/20220208133716.24070-1-tangyizhou@huawei.com Signed-off-by: Jonathan Corbet --- .../translations/zh_CN/power/energy-model.rst | 190 ++++++++++++++++++ .../translations/zh_CN/power/index.rst | 2 +- 2 files changed, 191 insertions(+), 1 deletion(-) create mode 100644 Documentation/translations/zh_CN/power/energy-model.rst diff --git a/Documentation/translations/zh_CN/power/energy-model.rst b/Documentation/translations/zh_CN/power/energy-model.rst new file mode 100644 index 000000000000..c7da1b6aefee --- /dev/null +++ b/Documentation/translations/zh_CN/power/energy-model.rst @@ -0,0 +1,190 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/power/energy-model.rst + +:翻译: + + 唐艺舟 Tang Yizhou + +============ +设备能量模型 +============ + +1. 概述 +------- + +能量模型(EM)框架是一种驱动程序与内核子系统之间的接口。其中驱动程序了解不同 +性能层级的设备所消耗的功率,而内核子系统愿意使用该信息做出能量感知决策。 + +设备所消耗的功率的信息来源在不同的平台上可能有很大的不同。这些功率成本在某些 +情况下可以使用设备树数据来估算。在其它情况下,固件会更清楚。或者,用户空间可能 +是最清楚的。以此类推。为了避免每一个客户端子系统对每一种可能的信息源自己重新 +实现支持,EM框架作为一个抽象层介入,它在内核中对功率成本表的格式进行标准化, +因此能够避免多余的工作。 + +功率值可以用毫瓦或“抽象刻度”表示。多个子系统可能使用EM,由系统集成商来检查 +功率值刻度类型的要求是否满足。可以在能量感知调度器的文档中找到一个例子 +Documentation/scheduler/sched-energy.rst。对于一些子系统,比如热能或 +powercap,用“抽象刻度”描述功率值可能会导致问题。这些子系统对过去使用的功率的 +估算值更感兴趣,因此可能需要真实的毫瓦。这些要求的一个例子可以在智能功率分配 +Documentation/driver-api/thermal/power_allocator.rst文档中找到。 + +内核子系统可能(基于EM内部标志位)实现了对EM注册设备是否具有不一致刻度的自动 +检查。要记住的重要事情是,当功率值以“抽象刻度”表示时,从中推导以毫焦耳为单位 +的真实能量消耗是不可能的。 + +下图描述了一个驱动的例子(这里是针对Arm的,但该方法适用于任何体系结构),它 +向EM框架提供了功率成本,感兴趣的客户端可从中读取数据:: + + +---------------+ +-----------------+ +---------------+ + | Thermal (IPA) | | Scheduler (EAS) | | Other | + +---------------+ +-----------------+ +---------------+ + | | em_cpu_energy() | + | | em_cpu_get() | + +---------+ | +---------+ + | | | + v v v + +---------------------+ + | Energy Model | + | Framework | + +---------------------+ + ^ ^ ^ + | | | em_dev_register_perf_domain() + +----------+ | +---------+ + | | | + +---------------+ +---------------+ +--------------+ + | cpufreq-dt | | arm_scmi | | Other | + +---------------+ +---------------+ +--------------+ + ^ ^ ^ + | | | + +--------------+ +---------------+ +--------------+ + | Device Tree | | Firmware | | ? | + +--------------+ +---------------+ +--------------+ + +对于CPU设备,EM框架管理着系统中每个“性能域”的功率成本表。一个性能域是一组 +性能一起伸缩的CPU。性能域通常与CPUFreq策略具有1对1映射。一个性能域中的 +所有CPU要求具有相同的微架构。不同性能域中的CPU可以有不同的微架构。 + + +2. 核心API +---------- + +2.1 配置选项 +^^^^^^^^^^^^ + +必须使能CONFIG_ENERGY_MODEL才能使用EM框架。 + + +2.2 性能域的注册 +^^^^^^^^^^^^^^^^ + +“高级”EM的注册 +~~~~~~~~~~~~~~~~ + +“高级”EM因它允许驱动提供更精确的功率模型而得名。它并不受限于框架中的一些已 +实现的数学公式(就像“简单”EM那样)。它可以更好地反映每个性能状态的实际功率 +测量。因此,在EM静态功率(漏电流功率)是重要的情况下,应该首选这种注册方式。 + +驱动程序应通过以下API将性能域注册到EM框架中:: + + int em_dev_register_perf_domain(struct device *dev, unsigned int nr_states, + struct em_data_callback *cb, cpumask_t *cpus, bool milliwatts); + +驱动程序必须提供一个回调函数,为每个性能状态返回<频率,功率>元组。驱动程序 +提供的回调函数可以自由地从任何相关位置(DT、固件......)以及以任何被认为是 +必要的方式获取数据。只有对于CPU设备,驱动程序必须使用cpumask指定性能域的CPU。 +对于CPU以外的其他设备,最后一个参数必须被设置为NULL。 + +最后一个参数“milliwatts”(毫瓦)设置成正确的值是很重要的,使用EM的内核 +子系统可能会依赖这个标志来检查所有的EM设备是否使用相同的刻度。如果有不同的 +刻度,这些子系统可能决定:返回警告/错误,停止工作或崩溃(panic)。 + +关于实现这个回调函数的驱动程序的例子,参见第3节。或者在第2.4节阅读这个API +的更多文档。 + + +“简单”EM的注册 +~~~~~~~~~~~~~~~~ + +“简单”EM是用框架的辅助函数cpufreq_register_em_with_opp()注册的。它实现了 +一个和以下数学公式紧密相关的功率模型:: + + Power = C * V^2 * f + +使用这种方法注册的EM可能无法正确反映真实设备的物理特性,例如当静态功率 +(漏电流功率)很重要时。 + + +2.3 访问性能域 +^^^^^^^^^^^^^^ + +有两个API函数提供对能量模型的访问。em_cpu_get()以CPU id为参数,em_pd_get() +以设备指针为参数。使用哪个接口取决于子系统,但对于CPU设备来说,这两个函数都返 +回相同的性能域。 + +对CPU的能量模型感兴趣的子系统可以通过em_cpu_get() API检索它。在创建性能域时 +分配一次能量模型表,它保存在内存中不被修改。 + +一个性能域所消耗的能量可以使用em_cpu_energy() API来估算。该估算假定CPU设备 +使用的CPUfreq监管器是schedutil。当前该计算不能提供给其它类型的设备。 + +关于上述API的更多细节可以在 ```` 或第2.4节中找到。 + + +2.4 API的细节描述 +^^^^^^^^^^^^^^^^^ +参见 include/linux/energy_model.h 和 kernel/power/energy_model.c 的kernel doc。 + +3. 驱动示例 +----------- + +CPUFreq框架支持专用的回调函数,用于为指定的CPU(们)注册EM: +cpufreq_driver::register_em()。这个回调必须为每个特定的驱动程序正确实现, +因为框架会在设置过程中适时地调用它。本节提供了一个简单的例子,展示CPUFreq驱动 +在能量模型框架中使用(假的)“foo”协议注册性能域。该驱动实现了一个est_power() +函数提供给EM框架:: + + -> drivers/cpufreq/foo_cpufreq.c + + 01 static int est_power(unsigned long *mW, unsigned long *KHz, + 02 struct device *dev) + 03 { + 04 long freq, power; + 05 + 06 /* 使用“foo”协议设置频率上限 */ + 07 freq = foo_get_freq_ceil(dev, *KHz); + 08 if (freq < 0); + 09 return freq; + 10 + 11 /* 估算相关频率下设备的功率成本 */ + 12 power = foo_estimate_power(dev, freq); + 13 if (power < 0); + 14 return power; + 15 + 16 /* 将这些值返回给EM框架 */ + 17 *mW = power; + 18 *KHz = freq; + 19 + 20 return 0; + 21 } + 22 + 23 static void foo_cpufreq_register_em(struct cpufreq_policy *policy) + 24 { + 25 struct em_data_callback em_cb = EM_DATA_CB(est_power); + 26 struct device *cpu_dev; + 27 int nr_opp; + 28 + 29 cpu_dev = get_cpu_device(cpumask_first(policy->cpus)); + 30 + 31 /* 查找该策略支持的OPP数量 */ + 32 nr_opp = foo_get_nr_opp(policy); + 33 + 34 /* 并注册新的性能域 */ + 35 em_dev_register_perf_domain(cpu_dev, nr_opp, &em_cb, policy->cpus, + 36 true); + 37 } + 38 + 39 static struct cpufreq_driver foo_cpufreq_driver = { + 40 .register_em = foo_cpufreq_register_em, + 41 }; diff --git a/Documentation/translations/zh_CN/power/index.rst b/Documentation/translations/zh_CN/power/index.rst index ad80a9e80b7c..bc54983ba515 100644 --- a/Documentation/translations/zh_CN/power/index.rst +++ b/Documentation/translations/zh_CN/power/index.rst @@ -14,6 +14,7 @@ .. toctree:: :maxdepth: 1 + energy-model opp TODOList: @@ -22,7 +23,6 @@ TODOList: * basic-pm-debugging * charger-manager * drivers-testing - * energy-model * freezing-of-tasks * pci * pm_qos_interface From d535e6c25a8131324aef4534e14d09b4d81c98a9 Mon Sep 17 00:00:00 2001 From: Tang Yizhou Date: Tue, 8 Feb 2022 10:01:05 +0800 Subject: [PATCH 110/294] docs/zh_CN: Add sched-energy Chinese translation Translate scheduler/sched-energy.rst into Chinese. Signed-off-by: Tang Yizhou Reviewed-by: Alex Shi Reviewed-by: Yanteng Si Link: https://lore.kernel.org/r/20220208020105.14117-1-tangyizhou@huawei.com Signed-off-by: Jonathan Corbet --- .../translations/zh_CN/scheduler/index.rst | 4 +- .../zh_CN/scheduler/sched-energy.rst | 351 ++++++++++++++++++ 2 files changed, 353 insertions(+), 2 deletions(-) create mode 100644 Documentation/translations/zh_CN/scheduler/sched-energy.rst diff --git a/Documentation/translations/zh_CN/scheduler/index.rst b/Documentation/translations/zh_CN/scheduler/index.rst index f8f8f35d53c7..ab7925980266 100644 --- a/Documentation/translations/zh_CN/scheduler/index.rst +++ b/Documentation/translations/zh_CN/scheduler/index.rst @@ -5,6 +5,7 @@ :翻译: 司延腾 Yanteng Si + 唐艺舟 Tang Yizhou :校译: @@ -23,13 +24,12 @@ Linux调度器 sched-design-CFS sched-domains sched-capacity + sched-energy TODOList: - sched-bwc sched-deadline - sched-energy sched-nice-design sched-rt-group sched-stats diff --git a/Documentation/translations/zh_CN/scheduler/sched-energy.rst b/Documentation/translations/zh_CN/scheduler/sched-energy.rst new file mode 100644 index 000000000000..fdbf6cfeea93 --- /dev/null +++ b/Documentation/translations/zh_CN/scheduler/sched-energy.rst @@ -0,0 +1,351 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: ../disclaimer-zh_CN.rst + +:Original: Documentation/scheduler/sched-energy.rst + +:翻译: + + 唐艺舟 Tang Yizhou + +============ +能量感知调度 +============ + +1. 简介 +------- + +能量感知调度(EAS)使调度器有能力预测其决策对CPU所消耗的能量的影响。EAS依靠 +一个能量模型(EM)来为每个任务选择一个节能的CPU,同时最小化对吞吐率的影响。 +本文档致力于介绍介绍EAS是如何工作的,它背后的主要设计决策是什么,以及使其运行 +所需的条件细节。 + +在进一步阅读之前,请注意,在撰写本文时:: + + /!\ EAS不支持对称CPU拓扑的平台 /!\ + +EAS只在异构CPU拓扑结构(如Arm大小核,big.LITTLE)上运行。因为在这种情况下, +通过调度来节约能量的潜力是最大的。 + +EAS实际使用的EM不是由调度器维护的,而是一个专门的框架。关于这个框架的细节和 +它提供的内容,请参考其文档(见Documentation/power/energy-model.rst)。 + + +2. 背景和术语 +------------- + +从一开始就说清楚定义: + - 能量 = [焦耳] (比如供电设备上的电池提供的资源) + - 功率 = 能量/时间 = [焦耳/秒] = [瓦特] + + EAS的目标是最小化能量消耗,同时仍能将工作完成。也就是说,我们要最大化:: + + 性能 [指令数/秒] + ---------------- + 功率 [瓦特] + +它等效于最小化:: + + 能量 [焦耳] + ----------- + 指令数 + +同时仍然获得“良好”的性能。当前调度器只考虑性能目标,因此该式子本质上是一个 +可选的优化目标,它同时考虑了两个目标:能量效率和性能。 + +引入EM的想法是为了让调度器评估其决策的影响,而不是盲目地应用可能仅在部分 +平台有正面效果的节能技术。同时,EM必须尽可能的简单,以最小化调度器的时延 +影响。 + +简而言之,EAS改变了CFS任务分配给CPU的方式。当调度器决定一个任务应该在哪里 +运行时(在唤醒期间),EM被用来在不损害系统吞吐率的情况下,从几个较好的候选 +CPU中挑选一个经预测能量消耗最优的CPU。EAS的预测依赖于对平台拓扑结构特定元素 +的了解,包括CPU的“算力”,以及它们各自的能量成本。 + + +3. 拓扑信息 +----------- + +EAS(以及调度器的剩余部分)使用“算力”的概念来区分不同计算吞吐率的CPU。一个 +CPU的“算力”代表了它在最高频率下运行时能完成的工作量,且这个值是相对系统中 +算力最大的CPU而言的。算力值被归一化为1024以内,并且可与由实体负载跟踪 +(PELT)机制算出的利用率信号做对比。由于有算力值和利用率值,EAS能够估计一个 +任务/CPU有多大/有多忙,并在评估性能与能量时将其考虑在内。CPU算力由特定体系 +结构实现的arch_scale_cpu_capacity()回调函数提供。 + +EAS使用的其余平台信息是直接从能量模型(EM)框架中读取的。一个平台的EM是一张 +表,表中每项代表系统中一个“性能域”的功率成本。(若要了解更多关于性能域的细节, +见Documentation/power/energy-model.rst) + +当调度域被建立或重新建立时,调度器管理对拓扑代码中EM对象的引用。对于每个根域 +(rd),调度器维护一个与当前rd->span相交的所有性能域的单向链表。链表中的每个 +节点都包含一个指向EM框架所提供的结构体em_perf_domain的指针。 + +链表被附加在根域上,以应对独占的cpuset的配置。由于独占的cpuset的边界不一定与 +性能域的边界一致,不同根域的链表可能包含重复的元素。 + +示例1 + 让我们考虑一个有12个CPU的平台,分成3个性能域,(pd0,pd4和pd8),按以下 + 方式组织:: + + CPUs: 0 1 2 3 4 5 6 7 8 9 10 11 + PDs: |--pd0--|--pd4--|---pd8---| + RDs: |----rd1----|-----rd2-----| + + 现在,考虑用户空间决定将系统分成两个独占的cpusets,因此创建了两个独立的根域, + 每个根域包含6个CPU。这两个根域在上图中被表示为rd1和rd2。由于pd4与rd1和rd2 + 都有交集,它将同时出现于附加在这两个根域的“->pd”链表中: + + * rd1->pd: pd0 -> pd4 + * rd2->pd: pd4 -> pd8 + + 请注意,调度器将为pd4创建两个重复的链表节点(每个链表中各一个)。然而这 + 两个节点持有指向同一个EM框架的共享数据结构的指针。 + +由于对这些链表的访问可能与热插拔及其它事件并发发生,因此它们受RCU锁保护,就像 +被调度器操控的拓扑结构体中剩下字段一样。 + +EAS同样维护了一个静态键(sched_energy_present),当至少有一个根域满足EAS +启动的所有条件时,这个键就会被启动。在第6节中总结了这些条件。 + + +4. 能量感知任务放置 +------------------- + +EAS覆盖了CFS的任务唤醒平衡代码。在唤醒平衡时,它使用平台的EM和PELT信号来选择节能 +的目标CPU。当EAS被启用时,select_task_rq_fair()调用find_energy_efficient_cpu() +来做任务放置决定。这个函数寻找在每个性能域中寻找具有最高剩余算力(CPU算力 - CPU +利用率)的CPU,因为它能让我们保持最低的频率。然后,该函数检查将任务放在新CPU相较 +依然放在之前活动的prev_cpu是否可以节省能量。 + +如果唤醒的任务被迁移,find_energy_efficient_cpu()使用compute_energy()来估算 +系统将消耗多少能量。compute_energy()检查各CPU当前的利用率情况,并尝试调整来 +“模拟”任务迁移。EM框架提供了API em_pd_energy()计算每个性能域在给定的利用率条件 +下的预期能量消耗。 + +下面详细介绍一个优化能量消耗的任务放置决策的例子。 + +示例2 + 让我们考虑一个有两个独立性能域的(伪)平台,每个性能域含有2个CPU。CPU0和CPU1 + 是小核,CPU2和CPU3是大核。 + + 调度器必须决定将任务P放在哪个CPU上,这个任务的util_avg = 200(平均利用率), + prev_cpu = 0(上一次运行在CPU0)。 + + 目前CPU的利用率情况如下图所示。CPU 0-3的util_avg分别为400、100、600和500。 + 每个性能域有三个操作性能值(OPP)。与每个OPP相关的CPU算力和功率成本列在能量 + 模型表中。P的util_avg在图中显示为"PP":: + + CPU util. + 1024 - - - - - - - Energy Model + +-----------+-------------+ + | Little | Big | + 768 ============= +-----+-----+------+------+ + | Cap | Pwr | Cap | Pwr | + +-----+-----+------+------+ + 512 =========== - ##- - - - - | 170 | 50 | 512 | 400 | + ## ## | 341 | 150 | 768 | 800 | + 341 -PP - - - - ## ## | 512 | 300 | 1024 | 1700 | + PP ## ## +-----+-----+------+------+ + 170 -## - - - - ## ## + ## ## ## ## + ------------ ------------- + CPU0 CPU1 CPU2 CPU3 + + Current OPP: ===== Other OPP: - - - util_avg (100 each): ## + + + find_energy_efficient_cpu()将首先在两个性能域中寻找具有最大剩余算力的CPU。 + 在这个例子中是CPU1和CPU3。然后,它将估算,当P被放在它们中的任意一个时,系统的 + 能耗,并检查这样做是否会比把P放在CPU0上节省一些能量。EAS假定OPPs遵循利用率 + (这与CPUFreq监管器schedutil的行为一致。关于这个问题的更多细节,见第6节)。 + + **情况1. P被迁移到CPU1**:: + + 1024 - - - - - - - + + Energy calculation: + 768 ============= * CPU0: 200 / 341 * 150 = 88 + * CPU1: 300 / 341 * 150 = 131 + * CPU2: 600 / 768 * 800 = 625 + 512 - - - - - - - ##- - - - - * CPU3: 500 / 768 * 800 = 520 + ## ## => total_energy = 1364 + 341 =========== ## ## + PP ## ## + 170 -## - - PP- ## ## + ## ## ## ## + ------------ ------------- + CPU0 CPU1 CPU2 CPU3 + + + **情况2. P被迁移到CPU3**:: + + 1024 - - - - - - - + + Energy calculation: + 768 ============= * CPU0: 200 / 341 * 150 = 88 + * CPU1: 100 / 341 * 150 = 43 + PP * CPU2: 600 / 768 * 800 = 625 + 512 - - - - - - - ##- - -PP - * CPU3: 700 / 768 * 800 = 729 + ## ## => total_energy = 1485 + 341 =========== ## ## + ## ## + 170 -## - - - - ## ## + ## ## ## ## + ------------ ------------- + CPU0 CPU1 CPU2 CPU3 + + **情况3. P依旧留在prev_cpu/CPU0**:: + + 1024 - - - - - - - + + Energy calculation: + 768 ============= * CPU0: 400 / 512 * 300 = 234 + * CPU1: 100 / 512 * 300 = 58 + * CPU2: 600 / 768 * 800 = 625 + 512 =========== - ##- - - - - * CPU3: 500 / 768 * 800 = 520 + ## ## => total_energy = 1437 + 341 -PP - - - - ## ## + PP ## ## + 170 -## - - - - ## ## + ## ## ## ## + ------------ ------------- + CPU0 CPU1 CPU2 CPU3 + + 从这些计算结果来看,情况1的总能量最低。所以从节约能量的角度看,CPU1是最佳候选 + 者。 + +大核通常比小核更耗电,因此主要在任务不适合在小核运行时使用。然而,小核并不总是比 +大核节能。举例来说,对于某些系统,小核的高OPP可能比大核的低OPP能量消耗更高。因此, +如果小核在某一特定时间点刚好有足够的利用率,在此刻被唤醒的小任务放在大核执行可能 +会更节能,尽管它在小核上运行也是合适的。 + +即使在大核所有OPP都不如小核OPP节能的情况下,在某些特定条件下,令小任务运行在大核 +上依然可能节能。事实上,将一个任务放在一个小核上可能导致整个性能域的OPP提高,这将 +增加已经在该性能域运行的任务的能量成本。如果唤醒的任务被放在一个大核上,它的执行 +成本可能比放在小核上更高,但这不会影响小核上的其它任务,这些任务将继续以较低的OPP +运行。因此,当考虑CPU消耗的总能量时,在大核上运行一个任务的额外成本可能小于为所有 +其它运行在小核的任务提高OPP的成本。 + +上面的例子几乎不可能以一种通用的方式得到正确的结果;同时,对于所有平台,在不知道 +系统所有CPU每个不同OPP的运行成本时,也无法得到正确的结果。得益于基于EM的设计, +EAS应该能够正确处理这些问题而不会引发太多麻烦。然而,为了确保对高利用率场景的 +吞吐率造成的影响最小化,EAS同样实现了另外一种叫“过度利用率”的机制。 + + +5. 过度利用率 +------------- + +从一般的角度来看,EAS能提供最大帮助的是那些涉及低、中CPU利用率的使用场景。每当CPU +密集型的长任务运行,它们将需要所有的可用CPU算力,调度器将没有什么办法来节省能量同时 +又不损害吞吐率。为了避免EAS损害性能,一旦CPU被使用的算力超过80%,它将被标记为“过度 +利用”。只要根域中没有CPU是过度利用状态,负载均衡被禁用,而EAS将覆盖唤醒平衡代码。 +EAS很可能将负载放置在系统中能量效率最高的CPU而不是其它CPU上,只要不损害吞吐率。 +因此,负载均衡器被禁用以防止它打破EAS发现的节能任务放置。当系统未处于过度利用状态时, +这样做是安全的,因为低于80%的临界点意味着: + + a. 所有的CPU都有一些空闲时间,所以EAS使用的利用率信号很可能准确地代表各种任务 + 的“大小”。 + b. 所有任务,不管它们的nice值是多大,都应该被提供了足够多的CPU算力。 + c. 既然有多余的算力,那么所有的任务都必须定期阻塞/休眠,在唤醒时进行平衡就足够 + 了。 + +只要一个CPU利用率超过80%的临界点,上述三个假设中至少有一个是不正确的。在这种情况下, +整个根域的“过度利用”标志被设置,EAS被禁用,负载均衡器被重新启用。通过这样做,调度器 +又回到了在CPU密集的条件下基于负载的算法做负载均衡。这更好地尊重了任务的nice值。 + +由于过度利用率的概念在很大程度上依赖于检测系统中是否有一些空闲时间,所以必须考虑 +(比CFS)更高优先级的调度类(以及中断)“窃取”的CPU算力。像这样,对过度使用率的检测 +不仅要考虑CFS任务使用的算力,还需要考虑其它调度类和中断。 + + +6. EAS的依赖和要求 +------------------ + +能量感知调度依赖系统的CPU具有特定的硬件属性,以及内核中的其它特性被启用。本节列出 +了这些依赖,并对如何满足这些依赖提供了提示。 + + +6.1 - 非对称CPU拓扑 +^^^^^^^^^^^^^^^^^^^ + + +如简介所提,目前只有非对称CPU拓扑结构的平台支持EAS。通过在运行时查询 +SD_ASYM_CPUCAPACITY_FULL标志位是否在创建调度域时已设置来检查这一要求是否满足。 + +参阅Documentation/scheduler/sched-capacity.rst以了解在sched_domain层次结构 +中设置此标志位所需满足的要求。 + +请注意,EAS并非从根本上与SMP不兼容,但在SMP平台上还没有观察到明显的节能。这一 +限制可以在将来进行修改,如果被证明不是这样的话。 + + +6.2 - 当前的能量模型 +^^^^^^^^^^^^^^^^^^^^ + +EAS使用一个平台的EM来估算调度决策对能量的影响。因此,你的平台必须向EM框架提供 +能量成本表,以启动EAS。要做到这一点,请参阅文档 +Documentation/power/energy-model.rst中的独立EM框架部分。 + +另请注意,调度域需要在EM注册后重建,以便启动EAS。 + +EAS使用EM对能量使用率进行预测决策,因此它在检查任务放置的可能选项时更加注重 +差异。对于EAS来说,EM的功率值是以毫瓦还是以“抽象刻度”为单位表示并不重要。 + + + +6.3 - 能量模型复杂度 +^^^^^^^^^^^^^^^^^^^^ + +任务唤醒路径是时延敏感的。当一个平台的EM太复杂(太多CPU,太多性能域,太多状态 +等),在唤醒路径中使用它的成本就会升高到不可接受。能量感知唤醒算法的复杂度为: + + C = Nd * (Nc + Ns) + +其中:Nd是性能域的数量;Nc是CPU的数量;Ns是OPP的总数(例如:对于两个性能域, +每个域有4个OPP,则Ns = 8)。 + +当调度域建立时,复杂性检查是在根域上进行的。如果一个根域的复杂度C恰好高于完全 +主观设定的EM_MAX_COMPLEXITY阈值(在本文写作时,是2048),则EAS不会在此根域 +启动。 + +如果你的平台的能量模型的复杂度太高,EAS无法在这个根域上使用,但你真的想用, +那么你就只剩下两个可能的选择: + + 1. 将你的系统拆分成分离的、较小的、使用独占cpuset的根域,并在每个根域局部 + 启用EAS。这个方案的好处是开箱即用,但缺点是无法在根域之间实现负载均衡, + 这可能会导致总体系统负载不均衡。 + 2. 提交补丁以降低EAS唤醒算法的复杂度,从而使其能够在合理的时间内处理更大 + 的EM。 + + +6.4 - Schedutil监管器 +^^^^^^^^^^^^^^^^^^^^^ + +EAS试图预测CPU在不久的将来会在哪个OPP下运行,以估算它们的能量消耗。为了做到 +这一点,它假定CPU的OPP跟随CPU利用率变化而变化。 + +尽管在实践中很难对这一假设的准确性提供硬性保证(因为,举例来说硬件可能不会做 +它被要求做的事情),相对于其他CPUFreq监管器,schedutil至少_请求_使用利用率 +信号计算的频率。因此,与EAS一起使用的唯一合理的监管器是schedutil,因为它是 +唯一一个在频率请求和能量预测之间提供某种程度的一致性的监管器。 + +不支持将EAS与schedutil之外的任何其它监管器一起使用。 + + +6.5 刻度不变性使用率信号 +^^^^^^^^^^^^^^^^^^^^^^^^ + +为了对不同的CPU和所有的性能状态做出准确的预测,EAS需要频率不变的和CPU不变的 +PELT信号。这些信号可以通过每个体系结构定义的arch_scale{cpu,freq}_capacity() +回调函数获取。 + +不支持在没有实现这两个回调函数的平台上使用EAS。 + + +6.6 多线程(SMT) +^^^^^^^^^^^^^^^^^ + +当前实现的EAS是不感知SMT的,因此无法利用多线程硬件节约能量。EAS认为线程是独立的 +CPU,这实际上对性能和能量消耗都是不利的。 + +不支持在SMT上使用EAS。 From 4fbe7b19a9485db0a53efc018fe88db25846c89f Mon Sep 17 00:00:00 2001 From: Ethan Dye Date: Mon, 7 Feb 2022 16:54:42 -0700 Subject: [PATCH 111/294] docs: Fix wording in optional zram feature docs This fixes some simple grammar errors in the documentation for zram, specifically errors in the optional feature section of the zram documentation. Signed-off-by: Ethan Dye Link: https://lore.kernel.org/r/20220207235442.95090-1-mrtops03@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/blockdev/zram.rst | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Documentation/admin-guide/blockdev/zram.rst b/Documentation/admin-guide/blockdev/zram.rst index 3e11926a4df9..54fe63745ed8 100644 --- a/Documentation/admin-guide/blockdev/zram.rst +++ b/Documentation/admin-guide/blockdev/zram.rst @@ -315,8 +315,8 @@ To use the feature, admin should set up backing device via:: echo /dev/sda5 > /sys/block/zramX/backing_dev -before disksize setting. It supports only partition at this moment. -If admin wants to use incompressible page writeback, they could do via:: +before disksize setting. It supports only partitions at this moment. +If admin wants to use incompressible page writeback, they could do it via:: echo huge > /sys/block/zramX/writeback @@ -341,9 +341,9 @@ Admin can request writeback of those idle pages at right timing via:: echo idle > /sys/block/zramX/writeback -With the command, zram writeback idle pages from memory to the storage. +With the command, zram will writeback idle pages from memory to the storage. -If admin want to write a specific page in zram device to backing device, +If an admin wants to write a specific page in zram device to the backing device, they could write a page index into the interface. echo "page_index=1251" > /sys/block/zramX/writeback @@ -354,7 +354,7 @@ to guarantee storage health for entire product life. To overcome the concern, zram supports "writeback_limit" feature. The "writeback_limit_enable"'s default value is 0 so that it doesn't limit -any writeback. IOW, if admin wants to apply writeback budget, he should +any writeback. IOW, if admin wants to apply writeback budget, they should enable writeback_limit_enable via:: $ echo 1 > /sys/block/zramX/writeback_limit_enable @@ -365,7 +365,7 @@ until admin sets the budget via /sys/block/zramX/writeback_limit. (If admin doesn't enable writeback_limit_enable, writeback_limit's value assigned via /sys/block/zramX/writeback_limit is meaningless.) -If admin want to limit writeback as per-day 400M, he could do it +If admin wants to limit writeback as per-day 400M, they could do it like below:: $ MB_SHIFT=20 @@ -375,16 +375,16 @@ like below:: $ echo 1 > /sys/block/zram0/writeback_limit_enable If admins want to allow further write again once the budget is exhausted, -he could do it like below:: +they could do it like below:: $ echo $((400<>4K_SHIFT)) > \ /sys/block/zram0/writeback_limit -If admin wants to see remaining writeback budget since last set:: +If an admin wants to see the remaining writeback budget since last set:: $ cat /sys/block/zramX/writeback_limit -If admin want to disable writeback limit, he could do:: +If an admin wants to disable writeback limit, they could do:: $ echo 0 > /sys/block/zramX/writeback_limit_enable @@ -393,7 +393,7 @@ system reboot, echo 1 > /sys/block/zramX/reset) so keeping how many of writeback happened until you reset the zram to allocate extra writeback budget in next setting is user's job. -If admin wants to measure writeback count in a certain period, he could +If admin wants to measure writeback count in a certain period, they could know it via /sys/block/zram0/bd_stat's 3rd column. memory tracking From 8716ef413aa55d9a25481f84eb54f9d571a1f421 Mon Sep 17 00:00:00 2001 From: Akira Yokosawa Date: Tue, 1 Feb 2022 08:59:58 +0900 Subject: [PATCH 112/294] docs: pdfdocs: Tweak width params of TOC Sphinx has its own set of width parameters of Table of Contents (TOC) for LaTeX defined in its class definition of sphinxmanual.cls. It also inherits parameters for chapter entries from report.cls of original LaTeX base. However, they are optimized assuming small documents with tens of pages and chapters/sections of less than 10. To cope with some of kernel-doc documents with more than 1000 pages and several tens of chapters/sections, definitions of those parameters need to be adjusted. Unfortunately, those parameters are hard coded in the class definitions and need low-level LaTeX coding tricks to redefine. As Sphinx 1.7.9 does not have \sphinxtableofcontentshook, which defines those parameters in later Sphinx versions, for compatibility with both pre-1.8 and later Sphinx versions, empty the hook altogether and redefine \@pnumwidth, \l@chapter, \l@section, and \@subsection commands originally defined in report.cls. Summary of parameter changes: Width of page number (\@pnumwidth): 1.55em -> 2.7em Width of chapter number: 1.5em -> 1.8em Indent of section number: 1.5em -> 1.8em Width of section number: 2.6em -> 3.2em Indent of subsection number: 4.1em -> 5em Width of subsection number: 3.5em -> 4.3em Notes: 1. Parameters for subsection become relevant only when ":maxdepth: 3" is specified under "toctree::" (e.g., RCU/index.rst). They can hold subsection numbers up to 5 digits such as "18.7.13" (in RCU.pdf). 2. Number of chapters in driver-api.pdf is getting closer to 100. When it reaches 100, another set of tweaks will be necessary. 3. The low-level LaTeX trick is mentioned in "Unofficial LaTeX2e reference manual" at: http://latexref.xyz/Table-of-contents-etc_002e.html Signed-off-by: Akira Yokosawa Cc: Jonathan Corbet Link: https://lore.kernel.org/r/e52b4718-7909-25be-fbc1-76800aa62ae3@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/conf.py | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/Documentation/conf.py b/Documentation/conf.py index f07f2e9b9f2c..e5c13dee2de8 100644 --- a/Documentation/conf.py +++ b/Documentation/conf.py @@ -409,6 +409,37 @@ latex_elements = { # Additional stuff for the LaTeX preamble. 'preamble': ''' + % Custom width parameters for TOC --- Redefine low-level commands + % defined in report.cls + \\makeatletter + %% Redefine \\@pnumwidth (page number width) + \\renewcommand*\\@pnumwidth{2.7em} + %% Redefine \\l@chapter (chapter list entry) + \\renewcommand*\\l@chapter[2]{% + \\ifnum \\c@tocdepth >\\m@ne + \\addpenalty{-\\@highpenalty}% + \\vskip 1.0em \\@plus\\p@ + \\setlength\\@tempdima{1.8em}% + \\begingroup + \\parindent \\z@ \\rightskip \\@pnumwidth + \\parfillskip -\\@pnumwidth + \\leavevmode \\bfseries + \\advance\\leftskip\\@tempdima + \\hskip -\\leftskip + #1\\nobreak\\hfil + \\nobreak\\hb@xt@\\@pnumwidth{\\hss #2% + \\kern-\\p@\\kern\\p@}\\par + \\penalty\\@highpenalty + \\endgroup + \\fi} + %% Redefine \\l@section and \\l@subsection + \\renewcommand*\\l@section{\\@dottedtocline{1}{1.8em}{3.2em}} + \\renewcommand*\\l@subsection{\\@dottedtocline{2}{5em}{4.3em}} + \\makeatother + %% Sphinx < 1.8 doesn't have \\sphinxtableofcontentshook + \\providecommand{\\sphinxtableofcontentshook}{} + %% Undefine it for compatibility with Sphinx 1.7.9 + \\renewcommand{\\sphinxtableofcontentshook}{} % Empty the hook % Prevent column squeezing of tabulary. \\setlength{\\tymin}{20em} % Use some font with UTF-8 support with XeLaTeX From 66939df53948bbf66753100266076f6a3357b54c Mon Sep 17 00:00:00 2001 From: Akira Yokosawa Date: Tue, 1 Feb 2022 09:02:14 +0900 Subject: [PATCH 113/294] docs: pdfdocs: Switch default CJK font to KR variants xeCJK is enabled in Table of Contents (TOC) so that translations.pdf built by top-level "make pdfdocs" can have its TOC typeset properly. This causes quotation marks and apostrophe symbols appear too wide in Latin-script docs. This is because (1) Sphinx converts ASCII symbols into multi-byte UTF-8 ones in LaTeX and (2) in the SC variant of "Noto CJK" font families, those UTF-8 symbols have full-width glyph. The KR variant of the font families has half-width glyph for those symbols and TOC pages should look nicer when it is used instead. Switch the default CJK font families to the KR variant and teach xeCJK of those symbols' widths. To compensate the switch, teach xeCJK of the width in the SC and TC variants. Signed-off-by: Akira Yokosawa Cc: Jonathan Corbet Link: https://lore.kernel.org/r/0c8ea878-0a6f-ea01-ab45-4e66c5facee9@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/conf.py | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/Documentation/conf.py b/Documentation/conf.py index e5c13dee2de8..e70aa5fd969f 100644 --- a/Documentation/conf.py +++ b/Documentation/conf.py @@ -460,13 +460,15 @@ latex_elements['preamble'] += ''' \\IfFontExistsTF{Noto Sans CJK SC}{ % This is needed for translations \\usepackage{xeCJK} - \\IfFontExistsTF{Noto Serif CJK SC}{ - \\setCJKmainfont{Noto Serif CJK SC}[AutoFakeSlant] + \\IfFontExistsTF{Noto Serif CJK KR}{ + \\setCJKmainfont{Noto Serif CJK KR}[AutoFakeSlant] }{ - \\setCJKmainfont{Noto Sans CJK SC}[AutoFakeSlant] + \\setCJKmainfont{Noto Sans CJK KR}[AutoFakeSlant] } - \\setCJKsansfont{Noto Sans CJK SC}[AutoFakeSlant] - \\setCJKmonofont{Noto Sans Mono CJK SC}[AutoFakeSlant] + \\setCJKsansfont{Noto Sans CJK KR}[AutoFakeSlant] + \\setCJKmonofont{Noto Sans Mono CJK KR}[AutoFakeSlant] + \\xeCJKDeclareCharClass{HalfLeft}{`“,`‘} + \\xeCJKDeclareCharClass{HalfRight}{`”,`’} % CJK Language-specific font choices \\IfFontExistsTF{Noto Serif CJK SC}{ \\newCJKfontfamily[SCmain]\\scmain{Noto Serif CJK SC}[AutoFakeSlant] @@ -513,11 +515,18 @@ latex_elements['preamble'] += ''' \\newcommand{\\kerneldocBeginSC}{% \\begingroup% \\scmain% + \\xeCJKDeclareCharClass{FullLeft}{`“,`‘}% + \\xeCJKDeclareCharClass{FullRight}{`”,`’}% + \\renewcommand{\\CJKrmdefault}{SCserif}% + \\renewcommand{\\CJKsfdefault}{SCsans}% + \\renewcommand{\\CJKttdefault}{SCmono}% } \\newcommand{\\kerneldocEndSC}{\\endgroup} \\newcommand{\\kerneldocBeginTC}{% \\begingroup% \\tcmain% + \\xeCJKDeclareCharClass{FullLeft}{`“,`‘}% + \\xeCJKDeclareCharClass{FullRight}{`”,`’}% \\renewcommand{\\CJKrmdefault}{TCserif}% \\renewcommand{\\CJKsfdefault}{TCsans}% \\renewcommand{\\CJKttdefault}{TCmono}% @@ -525,8 +534,6 @@ latex_elements['preamble'] += ''' \\newcommand{\\kerneldocEndTC}{\\endgroup} \\newcommand{\\kerneldocBeginKR}{% \\begingroup% - \\xeCJKDeclareCharClass{HalfLeft}{`“,`‘}% - \\xeCJKDeclareCharClass{HalfRight}{`”,`’}% \\krmain% \\renewcommand{\\CJKrmdefault}{KRserif}% \\renewcommand{\\CJKsfdefault}{KRsans}% @@ -536,8 +543,6 @@ latex_elements['preamble'] += ''' \\newcommand{\\kerneldocEndKR}{\\endgroup} \\newcommand{\\kerneldocBeginJP}{% \\begingroup% - \\xeCJKDeclareCharClass{HalfLeft}{`“,`‘}% - \\xeCJKDeclareCharClass{HalfRight}{`”,`’}% \\jpmain% \\renewcommand{\\CJKrmdefault}{JPserif}% \\renewcommand{\\CJKsfdefault}{JPsans}% From 7b686a2ea1e41e75c35ff2ec333d68b2b8c032d6 Mon Sep 17 00:00:00 2001 From: Akira Yokosawa Date: Tue, 1 Feb 2022 09:03:16 +0900 Subject: [PATCH 114/294] docs: pdfdocs: Enable CJKspace in TOC for Korean titles Korean (Hangul) titles in Table of Contents of translations.pdf don't have inter-phrase spaces. This is because the CJKspace option of xeCJK is disabled by default. Restore the spaces by enabling the option at the beginning of every document and disable it in the \kerneldocBegin{SC|TC|JP} commands. Signed-off-by: Akira Yokosawa Cc: Jonathan Corbet Link: https://lore.kernel.org/r/19141b3e-01d9-1f6d-5020-42fbda784831@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/conf.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Documentation/conf.py b/Documentation/conf.py index e70aa5fd969f..ded49b8e9bf6 100644 --- a/Documentation/conf.py +++ b/Documentation/conf.py @@ -520,6 +520,7 @@ latex_elements['preamble'] += ''' \\renewcommand{\\CJKrmdefault}{SCserif}% \\renewcommand{\\CJKsfdefault}{SCsans}% \\renewcommand{\\CJKttdefault}{SCmono}% + \\xeCJKsetup{CJKspace = false}% } \\newcommand{\\kerneldocEndSC}{\\endgroup} \\newcommand{\\kerneldocBeginTC}{% @@ -530,6 +531,7 @@ latex_elements['preamble'] += ''' \\renewcommand{\\CJKrmdefault}{TCserif}% \\renewcommand{\\CJKsfdefault}{TCsans}% \\renewcommand{\\CJKttdefault}{TCmono}% + \\xeCJKsetup{CJKspace = false}% } \\newcommand{\\kerneldocEndTC}{\\endgroup} \\newcommand{\\kerneldocBeginKR}{% @@ -538,7 +540,7 @@ latex_elements['preamble'] += ''' \\renewcommand{\\CJKrmdefault}{KRserif}% \\renewcommand{\\CJKsfdefault}{KRsans}% \\renewcommand{\\CJKttdefault}{KRmono}% - \\xeCJKsetup{CJKspace = true} % For inter-phrase space + % \\xeCJKsetup{CJKspace = true} % true by default } \\newcommand{\\kerneldocEndKR}{\\endgroup} \\newcommand{\\kerneldocBeginJP}{% @@ -547,6 +549,7 @@ latex_elements['preamble'] += ''' \\renewcommand{\\CJKrmdefault}{JPserif}% \\renewcommand{\\CJKsfdefault}{JPsans}% \\renewcommand{\\CJKttdefault}{JPmono}% + \\xeCJKsetup{CJKspace = false}% } \\newcommand{\\kerneldocEndJP}{\\endgroup} % Single spacing in literal blocks @@ -555,6 +558,7 @@ latex_elements['preamble'] += ''' \\usepackage{etoolbox} % Inactivate CJK after tableofcontents \\apptocmd{\\sphinxtableofcontents}{\\kerneldocCJKoff}{}{} + \\xeCJKsetup{CJKspace = true} % For inter-phrase space of Korean TOC }{ % No CJK font found % Custom macros to on/off CJK (Dummy) \\newcommand{\\kerneldocCJKon}{} From 5d9158e3c762f0bf1753501d8e64eb6fe19dc437 Mon Sep 17 00:00:00 2001 From: Akira Yokosawa Date: Tue, 1 Feb 2022 09:04:40 +0900 Subject: [PATCH 115/294] docs/translations: Skip CJK contents if suitable fonts not found On systems without "Noto Sans CJK" fonts, CJK chapters in translations.pdf are full of "TOFU" boxes, with a long build time and a large log file containing lots of missing-font warnings. Avoid such waste of time and resources by skipping CJK chapters when CJK fonts are not available. To skip whole chapters, change the definition of \kerneldocBegin{SC|TC|KR|JP} commands so that they can have an argument to be ignored. This works as far as the argument (#1) is not used in the command. In place of skipped contents, put a note on skipped contents at the beginning of the PDF. Change the call sites in index.rst of CJK translations accordingly. When CJK fonts are available, existing command definitions with no argument just work. LaTeX engine will see additional pairs of "{" and "}", which add a level of grouping without having any effect on typesetting. Signed-off-by: Akira Yokosawa Cc: Jonathan Corbet Link: https://lore.kernel.org/r/3359ca41-b81d-b2c7-e437-7618efbe241d@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/conf.py | 13 +++++++++---- Documentation/translations/ja_JP/index.rst | 4 ++-- Documentation/translations/ko_KR/index.rst | 5 ++--- Documentation/translations/zh_CN/index.rst | 4 ++-- Documentation/translations/zh_TW/index.rst | 4 ++-- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Documentation/conf.py b/Documentation/conf.py index ded49b8e9bf6..62cd0e472b3b 100644 --- a/Documentation/conf.py +++ b/Documentation/conf.py @@ -563,13 +563,18 @@ latex_elements['preamble'] += ''' % Custom macros to on/off CJK (Dummy) \\newcommand{\\kerneldocCJKon}{} \\newcommand{\\kerneldocCJKoff}{} - \\newcommand{\\kerneldocBeginSC}{} + \\newcommand{\\kerneldocBeginSC}[1]{% + \\begin{sphinxadmonition}{note}{Note:} + ``Noto Sans CJK'' fonts are not found while building this PDF\\@. + Translations of zh\\_CN, zh\\_TW, ko\\_KR, and ja\\_JP are + skipped. + \\end{sphinxadmonition}} \\newcommand{\\kerneldocEndSC}{} - \\newcommand{\\kerneldocBeginTC}{} + \\newcommand{\\kerneldocBeginTC}[1]{} \\newcommand{\\kerneldocEndTC}{} - \\newcommand{\\kerneldocBeginKR}{} + \\newcommand{\\kerneldocBeginKR}[1]{} \\newcommand{\\kerneldocEndKR}{} - \\newcommand{\\kerneldocBeginJP}{} + \\newcommand{\\kerneldocBeginJP}[1]{} \\newcommand{\\kerneldocEndJP}{} } ''' diff --git a/Documentation/translations/ja_JP/index.rst b/Documentation/translations/ja_JP/index.rst index 88d4d98eed15..20738c931d02 100644 --- a/Documentation/translations/ja_JP/index.rst +++ b/Documentation/translations/ja_JP/index.rst @@ -3,7 +3,7 @@ \renewcommand\thesection* \renewcommand\thesubsection* \kerneldocCJKon - \kerneldocBeginJP + \kerneldocBeginJP{ Japanese translations ===================== @@ -15,4 +15,4 @@ Japanese translations .. raw:: latex - \kerneldocEndJP + }\kerneldocEndJP diff --git a/Documentation/translations/ko_KR/index.rst b/Documentation/translations/ko_KR/index.rst index f636b482fb4c..4add6b2fe1f2 100644 --- a/Documentation/translations/ko_KR/index.rst +++ b/Documentation/translations/ko_KR/index.rst @@ -3,7 +3,7 @@ \renewcommand\thesection* \renewcommand\thesubsection* \kerneldocCJKon - \kerneldocBeginKR + \kerneldocBeginKR{ 한국어 번역 =========== @@ -26,5 +26,4 @@ .. raw:: latex - \normalsize - \kerneldocEndKR + }\kerneldocEndKR diff --git a/Documentation/translations/zh_CN/index.rst b/Documentation/translations/zh_CN/index.rst index f831887eacfb..23f8f4c68e83 100644 --- a/Documentation/translations/zh_CN/index.rst +++ b/Documentation/translations/zh_CN/index.rst @@ -5,7 +5,7 @@ \renewcommand\thesection* \renewcommand\thesubsection* \kerneldocCJKon - \kerneldocBeginSC + \kerneldocBeginSC{ .. _linux_doc_zh: @@ -198,4 +198,4 @@ TODOList: .. raw:: latex - \kerneldocEndSC + }\kerneldocEndSC diff --git a/Documentation/translations/zh_TW/index.rst b/Documentation/translations/zh_TW/index.rst index f56f78ba7860..e1ce9d8c06f8 100644 --- a/Documentation/translations/zh_TW/index.rst +++ b/Documentation/translations/zh_TW/index.rst @@ -5,7 +5,7 @@ \renewcommand\thesection* \renewcommand\thesubsection* \kerneldocCJKon - \kerneldocBeginTC + \kerneldocBeginTC{ .. _linux_doc_zh_tw: @@ -174,4 +174,4 @@ TODOList: .. raw:: latex - \kerneldocEndTC + }\kerneldocEndTC From b774cc46313b3d7c9139f29df67818a8b858c558 Mon Sep 17 00:00:00 2001 From: Akira Yokosawa Date: Tue, 1 Feb 2022 09:05:40 +0900 Subject: [PATCH 116/294] docs: pdfdocs: Move CJK monospace font setting to main conf.py As LaTeX macros for CJK font settings can have Latin-script font settings as well, settings under Documentation/translations/ can be moved to the main conf.py. By this change, translations.pdf built by top-level "make pdfdocs" can have properly aligned ascii-art diagrams except for Korean ones. For the reason of remaining misalignment in Korean diagrams, see changelog of commit a90dad8f610a ("docs: pdfdocs: Add conf.py local to translations for ascii-art alignment"). Signed-off-by: Akira Yokosawa Cc: Jonathan Corbet Link: https://lore.kernel.org/r/eb87790a-03f4-9f29-c8a3-ef2c3e78ca18@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/conf.py | 8 ++++++++ Documentation/translations/conf.py | 12 ------------ 2 files changed, 8 insertions(+), 12 deletions(-) delete mode 100644 Documentation/translations/conf.py diff --git a/Documentation/conf.py b/Documentation/conf.py index 62cd0e472b3b..fb8f69fc4d38 100644 --- a/Documentation/conf.py +++ b/Documentation/conf.py @@ -521,6 +521,8 @@ latex_elements['preamble'] += ''' \\renewcommand{\\CJKsfdefault}{SCsans}% \\renewcommand{\\CJKttdefault}{SCmono}% \\xeCJKsetup{CJKspace = false}% + % For CJK ascii-art alignment + \\setmonofont{Noto Sans Mono CJK SC}[AutoFakeSlant]% } \\newcommand{\\kerneldocEndSC}{\\endgroup} \\newcommand{\\kerneldocBeginTC}{% @@ -532,6 +534,8 @@ latex_elements['preamble'] += ''' \\renewcommand{\\CJKsfdefault}{TCsans}% \\renewcommand{\\CJKttdefault}{TCmono}% \\xeCJKsetup{CJKspace = false}% + % For CJK ascii-art alignment + \\setmonofont{Noto Sans Mono CJK TC}[AutoFakeSlant]% } \\newcommand{\\kerneldocEndTC}{\\endgroup} \\newcommand{\\kerneldocBeginKR}{% @@ -541,6 +545,8 @@ latex_elements['preamble'] += ''' \\renewcommand{\\CJKsfdefault}{KRsans}% \\renewcommand{\\CJKttdefault}{KRmono}% % \\xeCJKsetup{CJKspace = true} % true by default + % For CJK ascii-art alignment (still misaligned for Hangul) + \\setmonofont{Noto Sans Mono CJK KR}[AutoFakeSlant]% } \\newcommand{\\kerneldocEndKR}{\\endgroup} \\newcommand{\\kerneldocBeginJP}{% @@ -550,6 +556,8 @@ latex_elements['preamble'] += ''' \\renewcommand{\\CJKsfdefault}{JPsans}% \\renewcommand{\\CJKttdefault}{JPmono}% \\xeCJKsetup{CJKspace = false}% + % For CJK ascii-art alignment + \\setmonofont{Noto Sans Mono CJK JP}[AutoFakeSlant]% } \\newcommand{\\kerneldocEndJP}{\\endgroup} % Single spacing in literal blocks diff --git a/Documentation/translations/conf.py b/Documentation/translations/conf.py deleted file mode 100644 index 92cdbba74229..000000000000 --- a/Documentation/translations/conf.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -# SPDX-License-Identifier: GPL-2.0 - -# -- Additinal options for LaTeX output ---------------------------------- -# font config for ascii-art alignment - -latex_elements['preamble'] += ''' - \\IfFontExistsTF{Noto Sans CJK SC}{ - % For CJK ascii-art alignment - \\setmonofont{Noto Sans Mono CJK SC}[AutoFakeSlant] - }{} -''' From 3f51aa9e296fe4af785d5761bb12556fb2494761 Mon Sep 17 00:00:00 2001 From: Ye Bin Date: Wed, 9 Feb 2022 19:29:51 +0800 Subject: [PATCH 117/294] PM: hibernate: fix load_image_and_restore() error path As 'swsusp_check' open 'hib_resume_bdev', if call 'create_basic_memory_bitmaps' failed, we need to close 'hib_resume_bdev' in 'load_image_and_restore' function. Signed-off-by: Ye Bin [ rjw: Subject ] Signed-off-by: Rafael J. Wysocki --- kernel/power/hibernate.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index e6af502c2fd7..49d1df0218cb 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -689,8 +689,10 @@ static int load_image_and_restore(void) lock_device_hotplug(); error = create_basic_memory_bitmaps(); - if (error) + if (error) { + swsusp_close(FMODE_READ | FMODE_EXCL); goto Unlock; + } error = swsusp_read(&flags); swsusp_close(FMODE_READ | FMODE_EXCL); From 7fd786dfbd2c55ddee3b87f33c82f1c58bdb1dd6 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 18 Jan 2022 15:02:57 -0800 Subject: [PATCH 118/294] tools/power/x86/intel-speed-select: OOB daemon mode It is possible that some out of band agent changed config level. In this case CPUs need to be online/offline to support this config change. Add a command line option --oob, so that this tool can run as daemon and poll for config level change and take action. The poll interval is configurable in seconds using config option --poll-interval. Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/Build | 2 +- .../x86/intel-speed-select/isst-config.c | 51 +++- .../x86/intel-speed-select/isst-daemon.c | 239 ++++++++++++++++++ tools/power/x86/intel-speed-select/isst.h | 11 + 4 files changed, 292 insertions(+), 11 deletions(-) create mode 100644 tools/power/x86/intel-speed-select/isst-daemon.c diff --git a/tools/power/x86/intel-speed-select/Build b/tools/power/x86/intel-speed-select/Build index b61456d75190..86fb9020cca2 100644 --- a/tools/power/x86/intel-speed-select/Build +++ b/tools/power/x86/intel-speed-select/Build @@ -1 +1 @@ -intel-speed-select-y += isst-config.o isst-core.o isst-display.o +intel-speed-select-y += isst-config.o isst-core.o isst-display.o isst-daemon.o diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index efe72fa48224..de5029b54050 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -368,7 +368,7 @@ int get_topo_max_cpus(void) return topo_max_cpus; } -static void set_cpu_online_offline(int cpu, int state) +void set_cpu_online_offline(int cpu, int state) { char buffer[128]; int fd, ret; @@ -409,12 +409,10 @@ static void force_all_cpus_online(void) unlink("/var/run/isst_cpu_topology.dat"); } -#define MAX_PACKAGE_COUNT 8 -#define MAX_DIE_PER_PACKAGE 2 -static void for_each_online_package_in_set(void (*callback)(int, void *, void *, - void *, void *), - void *arg1, void *arg2, void *arg3, - void *arg4) +void for_each_online_package_in_set(void (*callback)(int, void *, void *, + void *, void *), + void *arg1, void *arg2, void *arg3, + void *arg4) { int max_packages[MAX_PACKAGE_COUNT * MAX_PACKAGE_COUNT]; int pkg_index = 0, i; @@ -2803,7 +2801,9 @@ static void usage(void) printf("\t[-p|--pause] : Delay between two mail box commands in milliseconds\n"); printf("\t[-r|--retry] : Retry count for mail box commands on failure, default 3\n"); printf("\t[-v|--version] : Print version\n"); - + printf("\t[-b|--oob : Start a daemon to process HFI events for perf profile change from Out of Band agent.\n"); + printf("\t[-n|--no-daemon : Don't run as daemon. By default --oob will turn on daemon mode\n"); + printf("\t[-w|--delay : Delay for reading config level state change in OOB poll mode.\n"); printf("\nResult format\n"); printf("\tResult display uses a common format for each command:\n"); printf("\tResults are formatted in text/JSON with\n"); @@ -2837,6 +2837,9 @@ static void cmdline(int argc, char **argv) int opt, force_cpus_online = 0; int option_index = 0; int ret; + int oob_mode = 0; + int poll_interval = -1; + int no_daemon = 0; static struct option long_options[] = { { "all-cpus-online", no_argument, 0, 'a' }, @@ -2849,6 +2852,9 @@ static void cmdline(int argc, char **argv) { "out", required_argument, 0, 'o' }, { "retry", required_argument, 0, 'r' }, { "version", no_argument, 0, 'v' }, + { "oob", no_argument, 0, 'b' }, + { "no-daemon", no_argument, 0, 'n' }, + { "poll-interval", required_argument, 0, 'w' }, { 0, 0, 0, 0 } }; @@ -2875,7 +2881,7 @@ static void cmdline(int argc, char **argv) } progname = argv[0]; - while ((opt = getopt_long_only(argc, argv, "+c:df:hio:va", long_options, + while ((opt = getopt_long_only(argc, argv, "+c:df:hio:vabw:n", long_options, &option_index)) != -1) { switch (opt) { case 'a': @@ -2920,12 +2926,26 @@ static void cmdline(int argc, char **argv) case 'v': print_version(); break; + case 'b': + oob_mode = 1; + break; + case 'n': + no_daemon = 1; + break; + case 'w': + ret = strtol(optarg, &ptr, 10); + if (!ret) { + fprintf(stderr, "Invalid poll interval count\n"); + exit(0); + } + poll_interval = ret; + break; default: usage(); } } - if (optind > (argc - 2)) { + if (optind > (argc - 2) && !oob_mode) { usage(); exit(0); } @@ -2936,6 +2956,17 @@ static void cmdline(int argc, char **argv) set_cpu_present_cpu_mask(); set_cpu_target_cpu_mask(); + if (oob_mode) { + create_cpu_map(); + if (debug_flag) + fprintf(stderr, "OOB mode is enabled in debug mode\n"); + + ret = isst_daemon(debug_flag, poll_interval, no_daemon); + if (ret) + fprintf(stderr, "OOB mode enable failed\n"); + goto out; + } + if (!is_clx_n_platform()) { ret = isst_fill_platform_info(); if (ret) diff --git a/tools/power/x86/intel-speed-select/isst-daemon.c b/tools/power/x86/intel-speed-select/isst-daemon.c new file mode 100644 index 000000000000..15a70bba8d76 --- /dev/null +++ b/tools/power/x86/intel-speed-select/isst-daemon.c @@ -0,0 +1,239 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Speed Select -- Allow speed select to daemonize + * Copyright (c) 2022 Intel Corporation. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "isst.h" + +static int per_package_levels_info[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE]; +static time_t per_package_levels_tm[MAX_PACKAGE_COUNT][MAX_DIE_PER_PACKAGE]; + +static void init_levels(void) +{ + int i, j; + + for (i = 0; i < MAX_PACKAGE_COUNT; ++i) + for (j = 0; j < MAX_DIE_PER_PACKAGE; ++j) + per_package_levels_info[i][j] = -1; +} + +void process_level_change(int cpu) +{ + struct isst_pkg_ctdp_level_info ctdp_level; + int pkg_id = get_physical_package_id(cpu); + int die_id = get_physical_die_id(cpu); + struct isst_pkg_ctdp pkg_dev; + time_t tm; + int ret; + + if (pkg_id >= MAX_PACKAGE_COUNT || die_id > MAX_DIE_PER_PACKAGE) { + debug_printf("Invalid package/die info for cpu:%d\n", cpu); + return; + } + + tm = time(NULL); + if (tm - per_package_levels_tm[pkg_id][die_id] < 2 ) + return; + + per_package_levels_tm[pkg_id][die_id] = tm; + + ret = isst_get_ctdp_levels(cpu, &pkg_dev); + if (ret) { + debug_printf("Can't get tdp levels for cpu:%d\n", cpu); + return; + } + + debug_printf("Get Config level %d pkg:%d die:%d current_level:%d \n", cpu, + pkg_id, die_id, pkg_dev.current_level); + + if (pkg_dev.locked) { + debug_printf("config TDP s locked \n"); + return; + } + + if (per_package_levels_info[pkg_id][die_id] == pkg_dev.current_level) + return; + + debug_printf("**Config level change for cpu:%d pkg:%d die:%d from %d to %d\n", + cpu, pkg_id, die_id, per_package_levels_info[pkg_id][die_id], + pkg_dev.current_level); + + per_package_levels_info[pkg_id][die_id] = pkg_dev.current_level; + + ctdp_level.core_cpumask_size = + alloc_cpu_set(&ctdp_level.core_cpumask); + ret = isst_get_coremask_info(cpu, pkg_dev.current_level, &ctdp_level); + if (ret) { + free_cpu_set(ctdp_level.core_cpumask); + debug_printf("Can't get core_mask:%d\n", cpu); + return; + } + + if (ctdp_level.cpu_count) { + int i, max_cpus = get_topo_max_cpus(); + for (i = 0; i < max_cpus; ++i) { + if (pkg_id != get_physical_package_id(i) || die_id != get_physical_die_id(i)) + continue; + if (CPU_ISSET_S(i, ctdp_level.core_cpumask_size, ctdp_level.core_cpumask)) { + fprintf(stderr, "online cpu %d\n", i); + set_cpu_online_offline(i, 1); + } else { + fprintf(stderr, "offline cpu %d\n", i); + set_cpu_online_offline(i, 0); + } + } + } + + free_cpu_set(ctdp_level.core_cpumask); +} + +static void _poll_for_config_change(int cpu, void *arg1, void *arg2, + void *arg3, void *arg4) +{ + process_level_change(cpu); +} + +static void poll_for_config_change(void) +{ + for_each_online_package_in_set(_poll_for_config_change, NULL, NULL, + NULL, NULL); +} + +static int done = 0; +static int pid_file_handle; + +static void signal_handler(int sig) +{ + switch (sig) { + case SIGINT: + case SIGTERM: + done = 1; + exit(0); + break; + default: + break; + } +} + +static void daemonize(char *rundir, char *pidfile) +{ + int pid, sid, i; + char str[10]; + struct sigaction sig_actions; + sigset_t sig_set; + int ret; + + if (getppid() == 1) + return; + + sigemptyset(&sig_set); + sigaddset(&sig_set, SIGCHLD); + sigaddset(&sig_set, SIGTSTP); + sigaddset(&sig_set, SIGTTOU); + sigaddset(&sig_set, SIGTTIN); + sigprocmask(SIG_BLOCK, &sig_set, NULL); + + sig_actions.sa_handler = signal_handler; + sigemptyset(&sig_actions.sa_mask); + sig_actions.sa_flags = 0; + + sigaction(SIGHUP, &sig_actions, NULL); + sigaction(SIGTERM, &sig_actions, NULL); + sigaction(SIGINT, &sig_actions, NULL); + + pid = fork(); + if (pid < 0) { + /* Could not fork */ + exit(EXIT_FAILURE); + } + if (pid > 0) + exit(EXIT_SUCCESS); + + umask(027); + + sid = setsid(); + if (sid < 0) + exit(EXIT_FAILURE); + + /* close all descriptors */ + for (i = getdtablesize(); i >= 0; --i) + close(i); + + i = open("/dev/null", O_RDWR); + ret = dup(i); + if (ret == -1) + exit(EXIT_FAILURE); + + ret = dup(i); + if (ret == -1) + exit(EXIT_FAILURE); + + ret = chdir(rundir); + if (ret == -1) + exit(EXIT_FAILURE); + + pid_file_handle = open(pidfile, O_RDWR | O_CREAT, 0600); + if (pid_file_handle == -1) { + /* Couldn't open lock file */ + exit(1); + } + /* Try to lock file */ +#ifdef LOCKF_SUPPORT + if (lockf(pid_file_handle, F_TLOCK, 0) == -1) { +#else + if (flock(pid_file_handle, LOCK_EX|LOCK_NB) < 0) { +#endif + /* Couldn't get lock on lock file */ + fprintf(stderr, "Couldn't get lock file %d\n", getpid()); + exit(1); + } + snprintf(str, sizeof(str), "%d\n", getpid()); + ret = write(pid_file_handle, str, strlen(str)); + if (ret == -1) + exit(EXIT_FAILURE); + + close(i); +} + +int isst_daemon(int debug_mode, int poll_interval, int no_daemon) +{ + int ret; + + if (!no_daemon && poll_interval < 0 && !debug_mode) { + fprintf(stderr, "OOB mode is enabled and will run as daemon\n"); + daemonize((char *) "/tmp/", + (char *)"/tmp/hfi-events.pid"); + } else { + signal(SIGINT, signal_handler); + } + + init_levels(); + + if (poll_interval < 0) { + fprintf(stderr, "Must specify poll-interval\n"); + return ret; + } + + debug_printf("Starting loop\n"); + while (!done) { + sleep(poll_interval); + poll_for_config_change(); + } + + return 0; +} diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index 1aa15d5ea57c..b33f2c68d2ce 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -76,6 +76,9 @@ #define DISP_FREQ_MULTIPLIER 100 +#define MAX_PACKAGE_COUNT 8 +#define MAX_DIE_PER_PACKAGE 2 + struct isst_clos_config { int pkg_id; int die_id; @@ -260,4 +263,12 @@ extern int is_skx_based_platform(void); extern int is_spr_platform(void); extern int is_icx_platform(void); extern void isst_trl_display_information(int cpu, FILE *outf, unsigned long long trl); + +extern void set_cpu_online_offline(int cpu, int state); +extern void for_each_online_package_in_set(void (*callback)(int, void *, void *, + void *, void *), + void *arg1, void *arg2, void *arg3, + void *arg4); +extern int isst_daemon(int debug_mode, int poll_interval, int no_daemon); +extern void process_level_change(int cpu); #endif From 7d440da009b6cd2a559cdb63d97e2cb569357dbc Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 18 Jan 2022 16:44:20 -0800 Subject: [PATCH 119/294] tools/power/x86/intel-speed-select: HFI support Read HFI (Hardware Feedback Interface) events to process config level changes in oob mode. When HFI is supported there is no need for polling to check config level change. Subscribe to Linux thermal netlink messages and process message: THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE. This message contains cpu number, performance and energy efficiency. Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/Build | 2 +- tools/power/x86/intel-speed-select/Makefile | 10 +- .../power/x86/intel-speed-select/hfi-events.c | 309 ++++++++++++++++++ .../x86/intel-speed-select/isst-daemon.c | 5 + tools/power/x86/intel-speed-select/isst.h | 2 + 5 files changed, 324 insertions(+), 4 deletions(-) create mode 100644 tools/power/x86/intel-speed-select/hfi-events.c diff --git a/tools/power/x86/intel-speed-select/Build b/tools/power/x86/intel-speed-select/Build index 86fb9020cca2..81e36bd578b1 100644 --- a/tools/power/x86/intel-speed-select/Build +++ b/tools/power/x86/intel-speed-select/Build @@ -1 +1 @@ -intel-speed-select-y += isst-config.o isst-core.o isst-display.o isst-daemon.o +intel-speed-select-y += isst-config.o isst-core.o isst-display.o isst-daemon.o hfi-events.o diff --git a/tools/power/x86/intel-speed-select/Makefile b/tools/power/x86/intel-speed-select/Makefile index 12c6939dca2a..d2fba1297d96 100644 --- a/tools/power/x86/intel-speed-select/Makefile +++ b/tools/power/x86/intel-speed-select/Makefile @@ -13,8 +13,8 @@ endif # Do not use make's built-in rules # (this improves performance and avoids hard-to-debug behaviour); MAKEFLAGS += -r - -override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include +override CFLAGS += -O2 -Wall -g -D_GNU_SOURCE -I$(OUTPUT)include -I/usr/include/libnl3 +override LDFLAGS += -lnl-genl-3 -lnl-3 ALL_TARGETS := intel-speed-select ALL_PROGRAMS := $(patsubst %,$(OUTPUT)%,$(ALL_TARGETS)) @@ -31,7 +31,11 @@ $(OUTPUT)include/linux/isst_if.h: ../../../../include/uapi/linux/isst_if.h mkdir -p $(OUTPUT)include/linux 2>&1 || true ln -sf $(CURDIR)/../../../../include/uapi/linux/isst_if.h $@ -prepare: $(OUTPUT)include/linux/isst_if.h +$(OUTPUT)include/linux/thermal.h: ../../../../include/uapi/linux/thermal.h + mkdir -p $(OUTPUT)include/linux 2>&1 || true + ln -sf $(CURDIR)/../../../../include/uapi/linux/thermal.h $@ + +prepare: $(OUTPUT)include/linux/isst_if.h $(OUTPUT)include/linux/thermal.h ISST_IN := $(OUTPUT)intel-speed-select-in.o diff --git a/tools/power/x86/intel-speed-select/hfi-events.c b/tools/power/x86/intel-speed-select/hfi-events.c new file mode 100644 index 000000000000..e85676711372 --- /dev/null +++ b/tools/power/x86/intel-speed-select/hfi-events.c @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Intel Speed Select -- Read HFI events for OOB + * Copyright (c) 2022 Intel Corporation. + */ + +/* + * This file incorporates work covered by the following copyright and + * permission notice: + + * WPA Supplicant - driver interaction with Linux nl80211/cfg80211 + * Copyright (c) 2003-2008, Jouni Malinen + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of + * BSD license. + * + * Requires + * libnl-genl-3-dev + * + * For Fedora/CenOS + * dnf install libnl3-devel + * For Ubuntu + * apt install libnl-3-dev libnl-genl-3-dev + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "isst.h" + +struct hfi_event_data { + struct nl_sock *nl_handle; + struct nl_cb *nl_cb; +}; + +struct hfi_event_data drv; + +static int ack_handler(struct nl_msg *msg, void *arg) +{ + int *err = arg; + *err = 0; + return NL_STOP; +} + +static int finish_handler(struct nl_msg *msg, void *arg) +{ + int *ret = arg; + *ret = 0; + return NL_SKIP; +} + +static int error_handler(struct sockaddr_nl *nla, struct nlmsgerr *err, + void *arg) +{ + int *ret = arg; + *ret = err->error; + return NL_SKIP; +} + +static int seq_check_handler(struct nl_msg *msg, void *arg) +{ + return NL_OK; +} + +static int send_and_recv_msgs(struct hfi_event_data *drv, + struct nl_msg *msg, + int (*valid_handler)(struct nl_msg *, void *), + void *valid_data) +{ + struct nl_cb *cb; + int err = -ENOMEM; + + cb = nl_cb_clone(drv->nl_cb); + if (!cb) + goto out; + + err = nl_send_auto_complete(drv->nl_handle, msg); + if (err < 0) + goto out; + + err = 1; + + nl_cb_err(cb, NL_CB_CUSTOM, error_handler, &err); + nl_cb_set(cb, NL_CB_FINISH, NL_CB_CUSTOM, finish_handler, &err); + nl_cb_set(cb, NL_CB_ACK, NL_CB_CUSTOM, ack_handler, &err); + + if (valid_handler) + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, + valid_handler, valid_data); + + while (err > 0) + nl_recvmsgs(drv->nl_handle, cb); + out: + nl_cb_put(cb); + nlmsg_free(msg); + return err; +} + +struct family_data { + const char *group; + int id; +}; + +static int family_handler(struct nl_msg *msg, void *arg) +{ + struct family_data *res = arg; + struct nlattr *tb[CTRL_ATTR_MAX + 1]; + struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg)); + struct nlattr *mcgrp; + int i; + + nla_parse(tb, CTRL_ATTR_MAX, genlmsg_attrdata(gnlh, 0), + genlmsg_attrlen(gnlh, 0), NULL); + if (!tb[CTRL_ATTR_MCAST_GROUPS]) + return NL_SKIP; + + nla_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS], i) { + struct nlattr *tb2[CTRL_ATTR_MCAST_GRP_MAX + 1]; + nla_parse(tb2, CTRL_ATTR_MCAST_GRP_MAX, nla_data(mcgrp), + nla_len(mcgrp), NULL); + if (!tb2[CTRL_ATTR_MCAST_GRP_NAME] || + !tb2[CTRL_ATTR_MCAST_GRP_ID] || + strncmp(nla_data(tb2[CTRL_ATTR_MCAST_GRP_NAME]), + res->group, + nla_len(tb2[CTRL_ATTR_MCAST_GRP_NAME])) != 0) + continue; + res->id = nla_get_u32(tb2[CTRL_ATTR_MCAST_GRP_ID]); + break; + }; + + return 0; +} + +static int nl_get_multicast_id(struct hfi_event_data *drv, + const char *family, const char *group) +{ + struct nl_msg *msg; + int ret = -1; + struct family_data res = { group, -ENOENT }; + + msg = nlmsg_alloc(); + if (!msg) + return -ENOMEM; + genlmsg_put(msg, 0, 0, genl_ctrl_resolve(drv->nl_handle, "nlctrl"), + 0, 0, CTRL_CMD_GETFAMILY, 0); + NLA_PUT_STRING(msg, CTRL_ATTR_FAMILY_NAME, family); + + ret = send_and_recv_msgs(drv, msg, family_handler, &res); + msg = NULL; + if (ret == 0) + ret = res.id; + +nla_put_failure: + nlmsg_free(msg); + return ret; +} + +struct perf_cap { + int cpu; + int perf; + int eff; +}; + +static void process_hfi_event(struct perf_cap *perf_cap) +{ + process_level_change(perf_cap->cpu); +} + +static int handle_event(struct nl_msg *n, void *arg) +{ + struct nlmsghdr *nlh = nlmsg_hdr(n); + struct genlmsghdr *genlhdr = genlmsg_hdr(nlh); + struct nlattr *attrs[THERMAL_GENL_ATTR_MAX + 1]; + int ret; + struct perf_cap perf_cap; + + ret = genlmsg_parse(nlh, 0, attrs, THERMAL_GENL_ATTR_MAX, NULL); + + debug_printf("Received event %d parse_rer:%d\n", genlhdr->cmd, ret); + if (genlhdr->cmd == THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE) { + struct nlattr *cap; + int j, index = 0; + + debug_printf("THERMAL_GENL_EVENT_CPU_CAPABILITY_CHANGE\n"); + nla_for_each_nested(cap, attrs[THERMAL_GENL_ATTR_CPU_CAPABILITY], j) { + switch (index) { + case 0: + perf_cap.cpu = nla_get_u32(cap); + break; + case 1: + perf_cap.perf = nla_get_u32(cap); + break; + case 2: + perf_cap.eff = nla_get_u32(cap); + break; + default: + break; + } + ++index; + if (index == 3) { + index = 0; + process_hfi_event(&perf_cap); + } + } + } + + return 0; +} + +static int _hfi_exit; + +static int check_hf_suport(void) +{ + unsigned int eax = 0, ebx = 0, ecx = 0, edx = 0; + + __cpuid(6, eax, ebx, ecx, edx); + if (eax & BIT(19)) + return 1; + + return 0; +} + +int hfi_main(void) +{ + struct nl_sock *sock; + struct nl_cb *cb; + int err = 0; + int mcast_id; + int no_block = 0; + + if (!check_hf_suport()) { + fprintf(stderr, "CPU Doesn't support HFI\n"); + return -1; + } + + sock = nl_socket_alloc(); + if (!sock) { + fprintf(stderr, "nl_socket_alloc failed\n"); + return -1; + } + + if (genl_connect(sock)) { + fprintf(stderr, "genl_connect(sk_event) failed\n"); + goto free_sock; + } + + drv.nl_handle = sock; + drv.nl_cb = cb = nl_cb_alloc(NL_CB_DEFAULT); + if (drv.nl_cb == NULL) { + printf("Failed to allocate netlink callbacks"); + goto free_sock; + } + + mcast_id = nl_get_multicast_id(&drv, THERMAL_GENL_FAMILY_NAME, + THERMAL_GENL_EVENT_GROUP_NAME); + if (mcast_id < 0) { + fprintf(stderr, "nl_get_multicast_id failed\n"); + goto free_sock; + } + + if (nl_socket_add_membership(sock, mcast_id)) { + fprintf(stderr, "nl_socket_add_membership failed"); + goto free_sock; + } + + nl_cb_set(cb, NL_CB_SEQ_CHECK, NL_CB_CUSTOM, seq_check_handler, 0); + nl_cb_set(cb, NL_CB_VALID, NL_CB_CUSTOM, handle_event, NULL); + + if (no_block) + nl_socket_set_nonblocking(sock); + + debug_printf("hfi is initialized\n"); + + while (!_hfi_exit && !err) { + err = nl_recvmsgs(sock, cb); + debug_printf("nl_recv_message err:%d\n", err); + } + + return 0; + + /* Netlink library doesn't have calls to dealloc cb or disconnect */ +free_sock: + nl_socket_free(sock); + + return -1; +} + +void hfi_exit(void) +{ + _hfi_exit = 1; +} diff --git a/tools/power/x86/intel-speed-select/isst-daemon.c b/tools/power/x86/intel-speed-select/isst-daemon.c index 15a70bba8d76..dd372924bc82 100644 --- a/tools/power/x86/intel-speed-select/isst-daemon.c +++ b/tools/power/x86/intel-speed-select/isst-daemon.c @@ -123,6 +123,7 @@ static void signal_handler(int sig) case SIGINT: case SIGTERM: done = 1; + hfi_exit(); exit(0); break; default: @@ -225,6 +226,10 @@ int isst_daemon(int debug_mode, int poll_interval, int no_daemon) init_levels(); if (poll_interval < 0) { + ret = hfi_main(); + if (ret) { + fprintf(stderr, "HFI initialization failed\n"); + } fprintf(stderr, "Must specify poll-interval\n"); return ret; } diff --git a/tools/power/x86/intel-speed-select/isst.h b/tools/power/x86/intel-speed-select/isst.h index b33f2c68d2ce..0796d8c6a882 100644 --- a/tools/power/x86/intel-speed-select/isst.h +++ b/tools/power/x86/intel-speed-select/isst.h @@ -271,4 +271,6 @@ extern void for_each_online_package_in_set(void (*callback)(int, void *, void *, void *arg4); extern int isst_daemon(int debug_mode, int poll_interval, int no_daemon); extern void process_level_change(int cpu); +extern int hfi_main(void); +extern void hfi_exit(void); #endif From f3874e96fad596cd2c2441802be7e1d0c9fa8677 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Tue, 18 Jan 2022 16:52:26 -0800 Subject: [PATCH 120/294] tools/power/x86/intel-speed-select: v1.12 release This version allows out of band SST support, where some remote agent changes SST profiles via some Board Management Controller. Signed-off-by: Srinivas Pandruvada --- tools/power/x86/intel-speed-select/isst-config.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/power/x86/intel-speed-select/isst-config.c b/tools/power/x86/intel-speed-select/isst-config.c index de5029b54050..060390e88e37 100644 --- a/tools/power/x86/intel-speed-select/isst-config.c +++ b/tools/power/x86/intel-speed-select/isst-config.c @@ -15,7 +15,8 @@ struct process_cmd_struct { int arg; }; -static const char *version_str = "v1.11"; +static const char *version_str = "v1.12"; + static const int supported_api_ver = 1; static struct isst_if_platform_info isst_platform_info; static char *progname; From ad2f3b08d1ab17e481df753aae221f085fe1cc8a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 10 Feb 2022 21:05:33 +0100 Subject: [PATCH 121/294] ACPI: scan: Use ida_alloc() instead of ida_simple_get() As recommended in include/linux/idr.h, use ida_alloc() instead of ida_simple_get() for creating unique device object names and for symmetry replace ida_simple_remove() with ida_free() (and fix up the related overly long code line while at it). Also drop the ACPI_MAX_DEVICE_INSTANCES limit that is not necessary any more and may not be sufficient for future platforms. Signed-off-by: Rafael J. Wysocki Reviewed-by: Andy Shevchenko --- drivers/acpi/internal.h | 2 -- drivers/acpi/scan.c | 5 +++-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 457e11d851b8..628bf8f18130 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -96,8 +96,6 @@ void acpi_scan_table_notify(void); extern struct list_head acpi_bus_id_list; -#define ACPI_MAX_DEVICE_INSTANCES 4096 - struct acpi_device_bus_id { const char *bus_id; struct ida instance_ida; diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 1331756d4cfc..5fa34f5316f1 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -477,7 +477,8 @@ static void acpi_device_del(struct acpi_device *device) list_for_each_entry(acpi_device_bus_id, &acpi_bus_id_list, node) if (!strcmp(acpi_device_bus_id->bus_id, acpi_device_hid(device))) { - ida_simple_remove(&acpi_device_bus_id->instance_ida, device->pnp.instance_no); + ida_free(&acpi_device_bus_id->instance_ida, + device->pnp.instance_no); if (ida_is_empty(&acpi_device_bus_id->instance_ida)) { list_del(&acpi_device_bus_id->node); kfree_const(acpi_device_bus_id->bus_id); @@ -642,7 +643,7 @@ static int acpi_device_set_name(struct acpi_device *device, struct ida *instance_ida = &acpi_device_bus_id->instance_ida; int result; - result = ida_simple_get(instance_ida, 0, ACPI_MAX_DEVICE_INSTANCES, GFP_KERNEL); + result = ida_alloc(instance_ida, GFP_KERNEL); if (result < 0) return result; From 9978f446d406a7dae080ca4a682bac927c9b8773 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Wed, 9 Feb 2022 16:53:18 +0200 Subject: [PATCH 122/294] ACPI: property: Get rid of redundant 'else' In the snippets like the following if (...) return / goto / break / continue ...; else ... the 'else' is redundant. Get rid of it. While at it, replace conditional '<= 0' for unsigned type by '== 0' in acpi_data_prop_read(); update comment in the __acpi_node_get_property_reference() on how we parse the reference. Signed-off-by: Andy Shevchenko Signed-off-by: Rafael J. Wysocki --- drivers/acpi/property.c | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c index 3fceb4681ec9..12bbfe833609 100644 --- a/drivers/acpi/property.c +++ b/drivers/acpi/property.c @@ -541,7 +541,8 @@ acpi_device_data_of_node(const struct fwnode_handle *fwnode) if (is_acpi_device_node(fwnode)) { const struct acpi_device *adev = to_acpi_device_node(fwnode); return &adev->data; - } else if (is_acpi_data_node(fwnode)) { + } + if (is_acpi_data_node(fwnode)) { const struct acpi_data_node *dn = to_acpi_data_node(fwnode); return &dn->data; } @@ -739,14 +740,19 @@ int __acpi_node_get_property_reference(const struct fwnode_handle *fwnode, return -EINVAL; } - /* assume following integer elements are all args */ + /* + * Assume the following integer elements are all args. + * Stop counting on the first reference or end of the + * package arguments. In case of neither reference, + * nor integer, return an error, we can't parse it. + */ for (i = 0; element + i < end && i < num_args; i++) { int type = element[i].type; + if (type == ACPI_TYPE_LOCAL_REFERENCE) + break; if (type == ACPI_TYPE_INTEGER) nargs++; - else if (type == ACPI_TYPE_LOCAL_REFERENCE) - break; else return -EINVAL; } @@ -950,7 +956,7 @@ static int acpi_data_prop_read(const struct acpi_device_data *data, if (proptype != DEV_PROP_STRING && nval > obj->package.count) return -EOVERFLOW; - else if (nval <= 0) + if (nval == 0) return -EINVAL; items = obj->package.elements; @@ -1012,14 +1018,10 @@ struct fwnode_handle *acpi_get_next_subnode(const struct fwnode_handle *fwnode, const struct list_head *head; struct list_head *next; - if (!child || is_acpi_device_node(child)) { + if ((!child || is_acpi_device_node(child)) && adev) { struct acpi_device *child_adev; - if (adev) - head = &adev->children; - else - goto nondev; - + head = &adev->children; if (list_empty(head)) goto nondev; @@ -1089,7 +1091,8 @@ acpi_node_get_parent(const struct fwnode_handle *fwnode) if (is_acpi_data_node(fwnode)) { /* All data nodes have parent pointer so just return that */ return to_acpi_data_node(fwnode)->parent; - } else if (is_acpi_device_node(fwnode)) { + } + if (is_acpi_device_node(fwnode)) { struct device *dev = to_acpi_device_node(fwnode)->dev.parent; if (dev) From a8ab8ef437d48173e166f1f44f202097e91bf81c Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Sun, 6 Feb 2022 06:58:03 -0800 Subject: [PATCH 123/294] ACPI: clean up double words in two comments Remove the second 'on' and 'those'. Signed-off-by: Tom Rix [ rjw: Subject adjustments ] Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Kconfig | 2 +- drivers/acpi/pci_link.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index 273741dedfd2..1e34f846508f 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -302,7 +302,7 @@ config ACPI_IPMI help This driver enables the ACPI to access the BMC controller. And it uses the IPMI request/response message to communicate with BMC - controller, which can be found on on the server. + controller, which can be found on the server. To compile this driver as a module, choose M here: the module will be called as acpi_ipmi. diff --git a/drivers/acpi/pci_link.c b/drivers/acpi/pci_link.c index d54fb8e54671..58647051c948 100644 --- a/drivers/acpi/pci_link.c +++ b/drivers/acpi/pci_link.c @@ -185,7 +185,7 @@ static acpi_status acpi_pci_link_check_current(struct acpi_resource *resource, if (!p || !p->interrupt_count) { /* * IRQ descriptors may have no IRQ# bits set, - * particularly those those w/ _STA disabled + * particularly those w/ _STA disabled */ pr_debug("Blank _CRS IRQ resource\n"); return AE_OK; From c8be60c12041145e663249af261286d402b4c5e3 Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Tue, 22 Feb 2022 23:34:17 +0800 Subject: [PATCH 124/294] cpupower: Add AMD P-State capability flag Add AMD P-State capability flag in cpupower to indicate AMD new P-State kernel module support on Ryzen processors. Reviewed-by: Shuah Khan Signed-off-by: Huang Rui Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/helpers/helpers.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index 33ffacee7fcb..b4813efdfb00 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -73,6 +73,7 @@ enum cpupower_cpu_vendor {X86_VENDOR_UNKNOWN = 0, X86_VENDOR_INTEL, #define CPUPOWER_CAP_AMD_HW_PSTATE 0x00000100 #define CPUPOWER_CAP_AMD_PSTATEDEF 0x00000200 #define CPUPOWER_CAP_AMD_CPB_MSR 0x00000400 +#define CPUPOWER_CAP_AMD_PSTATE 0x00000800 #define CPUPOWER_AMD_CPBDIS 0x02000000 From 46c273a0958274f1e1e69f3540ae827a92e0660f Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Tue, 22 Feb 2022 23:34:18 +0800 Subject: [PATCH 125/294] cpupower: Add the function to check AMD P-State enabled The processor with AMD P-State function also supports legacy ACPI hardware P-States feature as well. Once driver sets AMD P-State eanbled, the processor will respond the finer grain AMD P-State feature instead of legacy ACPI P-States. So it introduces the cpupower_amd_pstate_enabled() to check whether the current kernel enables AMD P-State or AMD CPUFreq module. Reviewed-by: Shuah Khan Signed-off-by: Huang Rui Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/helpers/helpers.h | 10 ++++++++++ tools/power/cpupower/utils/helpers/misc.c | 18 ++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index b4813efdfb00..62771a086871 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -11,6 +11,7 @@ #include #include +#include #include "helpers/bitmask.h" #include @@ -136,6 +137,12 @@ extern int decode_pstates(unsigned int cpu, int boost_states, extern int cpufreq_has_boost_support(unsigned int cpu, int *support, int *active, int * states); + +/* AMD P-State stuff **************************/ +bool cpupower_amd_pstate_enabled(void); + +/* AMD P-State stuff **************************/ + /* * CPUID functions returning a single datum */ @@ -168,6 +175,9 @@ static inline int cpufreq_has_boost_support(unsigned int cpu, int *support, int *active, int * states) { return -1; } +static inline bool cpupower_amd_pstate_enabled(void) +{ return false; } + /* cpuid and cpuinfo helpers **************************/ static inline unsigned int cpuid_eax(unsigned int op) { return 0; }; diff --git a/tools/power/cpupower/utils/helpers/misc.c b/tools/power/cpupower/utils/helpers/misc.c index fc6e34511721..0c483cdefcc2 100644 --- a/tools/power/cpupower/utils/helpers/misc.c +++ b/tools/power/cpupower/utils/helpers/misc.c @@ -3,9 +3,11 @@ #include #include #include +#include #include "helpers/helpers.h" #include "helpers/sysfs.h" +#include "cpufreq.h" #if defined(__i386__) || defined(__x86_64__) @@ -83,6 +85,22 @@ int cpupower_intel_set_perf_bias(unsigned int cpu, unsigned int val) return 0; } +bool cpupower_amd_pstate_enabled(void) +{ + char *driver = cpufreq_get_driver(0); + bool ret = false; + + if (!driver) + return ret; + + if (!strcmp(driver, "amd-pstate")) + ret = true; + + cpufreq_put_driver(driver); + + return ret; +} + #endif /* #if defined(__i386__) || defined(__x86_64__) */ /* get_cpustate From 083792f368b8ceea7ae035b6641e9cef3aceb366 Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Tue, 22 Feb 2022 23:34:19 +0800 Subject: [PATCH 126/294] cpupower: Initial AMD P-State capability If kernel starts the AMD P-State module, the cpupower will initial the capability flag as CPUPOWER_CAP_AMD_PSTATE. And once AMD P-State capability is set, it won't need to set legacy ACPI relative capabilities anymore. Reviewed-by: Shuah Khan Signed-off-by: Huang Rui Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/helpers/cpuid.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tools/power/cpupower/utils/helpers/cpuid.c b/tools/power/cpupower/utils/helpers/cpuid.c index 72eb43593180..eae91f11d187 100644 --- a/tools/power/cpupower/utils/helpers/cpuid.c +++ b/tools/power/cpupower/utils/helpers/cpuid.c @@ -149,6 +149,19 @@ int get_cpu_info(struct cpupower_cpu_info *cpu_info) if (ext_cpuid_level >= 0x80000008 && cpuid_ebx(0x80000008) & (1 << 4)) cpu_info->caps |= CPUPOWER_CAP_AMD_RDPRU; + + if (cpupower_amd_pstate_enabled()) { + cpu_info->caps |= CPUPOWER_CAP_AMD_PSTATE; + + /* + * If AMD P-State is enabled, the firmware will treat + * AMD P-State function as high priority. + */ + cpu_info->caps &= ~CPUPOWER_CAP_AMD_CPB; + cpu_info->caps &= ~CPUPOWER_CAP_AMD_CPB_MSR; + cpu_info->caps &= ~CPUPOWER_CAP_AMD_HW_PSTATE; + cpu_info->caps &= ~CPUPOWER_CAP_AMD_PSTATEDEF; + } } if (cpu_info->vendor == X86_VENDOR_INTEL) { From e3ede97657d8cfc4fd75aecad50269534bb55aed Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Tue, 22 Feb 2022 23:34:20 +0800 Subject: [PATCH 127/294] cpupower: Add the function to get the sysfs value from specific table Expose the helper into cpufreq header, then cpufreq driver can use this function to get the sysfs value if it has any specific sysfs interfaces. Reviewed-by: Shuah Khan Signed-off-by: Huang Rui Signed-off-by: Shuah Khan --- tools/power/cpupower/lib/cpufreq.c | 23 ++++++++++++++++------- tools/power/cpupower/lib/cpufreq.h | 12 ++++++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/tools/power/cpupower/lib/cpufreq.c b/tools/power/cpupower/lib/cpufreq.c index c3b56db8b921..1516d23c17c9 100644 --- a/tools/power/cpupower/lib/cpufreq.c +++ b/tools/power/cpupower/lib/cpufreq.c @@ -83,20 +83,21 @@ static const char *cpufreq_value_files[MAX_CPUFREQ_VALUE_READ_FILES] = { [STATS_NUM_TRANSITIONS] = "stats/total_trans" }; - -static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu, - enum cpufreq_value which) +unsigned long cpufreq_get_sysfs_value_from_table(unsigned int cpu, + const char **table, + unsigned int index, + unsigned int size) { unsigned long value; unsigned int len; char linebuf[MAX_LINE_LEN]; char *endp; - if (which >= MAX_CPUFREQ_VALUE_READ_FILES) + if (!table || index >= size || !table[index]) return 0; - len = sysfs_cpufreq_read_file(cpu, cpufreq_value_files[which], - linebuf, sizeof(linebuf)); + len = sysfs_cpufreq_read_file(cpu, table[index], linebuf, + sizeof(linebuf)); if (len == 0) return 0; @@ -109,6 +110,14 @@ static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu, return value; } +static unsigned long sysfs_cpufreq_get_one_value(unsigned int cpu, + enum cpufreq_value which) +{ + return cpufreq_get_sysfs_value_from_table(cpu, cpufreq_value_files, + which, + MAX_CPUFREQ_VALUE_READ_FILES); +} + /* read access to files which contain one string */ enum cpufreq_string { @@ -124,7 +133,7 @@ static const char *cpufreq_string_files[MAX_CPUFREQ_STRING_FILES] = { static char *sysfs_cpufreq_get_one_string(unsigned int cpu, - enum cpufreq_string which) + enum cpufreq_string which) { char linebuf[MAX_LINE_LEN]; char *result; diff --git a/tools/power/cpupower/lib/cpufreq.h b/tools/power/cpupower/lib/cpufreq.h index 95f4fd9e2656..2f3c84035806 100644 --- a/tools/power/cpupower/lib/cpufreq.h +++ b/tools/power/cpupower/lib/cpufreq.h @@ -203,6 +203,18 @@ int cpufreq_modify_policy_governor(unsigned int cpu, char *governor); int cpufreq_set_frequency(unsigned int cpu, unsigned long target_frequency); +/* + * get the sysfs value from specific table + * + * Read the value with the sysfs file name from specific table. Does + * only work if the cpufreq driver has the specific sysfs interfaces. + */ + +unsigned long cpufreq_get_sysfs_value_from_table(unsigned int cpu, + const char **table, + unsigned int index, + unsigned int size); + #ifdef __cplusplus } #endif From 4a06806e5d4a781d2c81f6064985018562b2604b Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Tue, 22 Feb 2022 23:34:21 +0800 Subject: [PATCH 128/294] cpupower: Introduce ACPI CPPC library Kernel ACPI subsytem introduced the sysfs attributes for acpi cppc library in below path: /sys/devices/system/cpu/cpuX/acpi_cppc/ And these attributes will be used for AMD P-State driver to provide some performance and frequency values. Reviewed-by: Shuah Khan Signed-off-by: Huang Rui Signed-off-by: Shuah Khan --- tools/power/cpupower/Makefile | 6 +-- tools/power/cpupower/lib/acpi_cppc.c | 59 ++++++++++++++++++++++++++++ tools/power/cpupower/lib/acpi_cppc.h | 21 ++++++++++ 3 files changed, 83 insertions(+), 3 deletions(-) create mode 100644 tools/power/cpupower/lib/acpi_cppc.c create mode 100644 tools/power/cpupower/lib/acpi_cppc.h diff --git a/tools/power/cpupower/Makefile b/tools/power/cpupower/Makefile index 3b1594447f29..e9b6de314654 100644 --- a/tools/power/cpupower/Makefile +++ b/tools/power/cpupower/Makefile @@ -143,9 +143,9 @@ UTIL_HEADERS = utils/helpers/helpers.h utils/idle_monitor/cpupower-monitor.h \ utils/helpers/bitmask.h \ utils/idle_monitor/idle_monitors.h utils/idle_monitor/idle_monitors.def -LIB_HEADERS = lib/cpufreq.h lib/cpupower.h lib/cpuidle.h -LIB_SRC = lib/cpufreq.c lib/cpupower.c lib/cpuidle.c -LIB_OBJS = lib/cpufreq.o lib/cpupower.o lib/cpuidle.o +LIB_HEADERS = lib/cpufreq.h lib/cpupower.h lib/cpuidle.h lib/acpi_cppc.h +LIB_SRC = lib/cpufreq.c lib/cpupower.c lib/cpuidle.c lib/acpi_cppc.c +LIB_OBJS = lib/cpufreq.o lib/cpupower.o lib/cpuidle.o lib/acpi_cppc.o LIB_OBJS := $(addprefix $(OUTPUT),$(LIB_OBJS)) override CFLAGS += -pipe diff --git a/tools/power/cpupower/lib/acpi_cppc.c b/tools/power/cpupower/lib/acpi_cppc.c new file mode 100644 index 000000000000..c401ac331e9f --- /dev/null +++ b/tools/power/cpupower/lib/acpi_cppc.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0-only + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "cpupower_intern.h" +#include "acpi_cppc.h" + +/* ACPI CPPC sysfs access ***********************************************/ + +static int acpi_cppc_read_file(unsigned int cpu, const char *fname, + char *buf, size_t buflen) +{ + char path[SYSFS_PATH_MAX]; + + snprintf(path, sizeof(path), PATH_TO_CPU "cpu%u/acpi_cppc/%s", + cpu, fname); + return cpupower_read_sysfs(path, buf, buflen); +} + +static const char * const acpi_cppc_value_files[] = { + [HIGHEST_PERF] = "highest_perf", + [LOWEST_PERF] = "lowest_perf", + [NOMINAL_PERF] = "nominal_perf", + [LOWEST_NONLINEAR_PERF] = "lowest_nonlinear_perf", + [LOWEST_FREQ] = "lowest_freq", + [NOMINAL_FREQ] = "nominal_freq", + [REFERENCE_PERF] = "reference_perf", + [WRAPAROUND_TIME] = "wraparound_time" +}; + +unsigned long acpi_cppc_get_data(unsigned int cpu, enum acpi_cppc_value which) +{ + unsigned long long value; + unsigned int len; + char linebuf[MAX_LINE_LEN]; + char *endp; + + if (which >= MAX_CPPC_VALUE_FILES) + return 0; + + len = acpi_cppc_read_file(cpu, acpi_cppc_value_files[which], + linebuf, sizeof(linebuf)); + if (len == 0) + return 0; + + value = strtoull(linebuf, &endp, 0); + + if (endp == linebuf || errno == ERANGE) + return 0; + + return value; +} diff --git a/tools/power/cpupower/lib/acpi_cppc.h b/tools/power/cpupower/lib/acpi_cppc.h new file mode 100644 index 000000000000..85ca83080316 --- /dev/null +++ b/tools/power/cpupower/lib/acpi_cppc.h @@ -0,0 +1,21 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef __ACPI_CPPC_H__ +#define __ACPI_CPPC_H__ + +enum acpi_cppc_value { + HIGHEST_PERF, + LOWEST_PERF, + NOMINAL_PERF, + LOWEST_NONLINEAR_PERF, + LOWEST_FREQ, + NOMINAL_FREQ, + REFERENCE_PERF, + WRAPAROUND_TIME, + MAX_CPPC_VALUE_FILES +}; + +unsigned long acpi_cppc_get_data(unsigned int cpu, + enum acpi_cppc_value which); + +#endif /* _ACPI_CPPC_H */ From 33e43f3636dffe84753847eee79ea0e3527105e6 Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Tue, 22 Feb 2022 23:34:22 +0800 Subject: [PATCH 129/294] cpupower: Add AMD P-State sysfs definition and access helper Introduce the marco definitions and access helper function for AMD P-State sysfs interfaces such as each performance goals and frequency levels in amd helper file. They will be used to read the sysfs attribute from AMD P-State cpufreq driver for cpupower utilities. Reviewed-by: Shuah Khan Signed-off-by: Huang Rui Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/helpers/amd.c | 30 ++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tools/power/cpupower/utils/helpers/amd.c b/tools/power/cpupower/utils/helpers/amd.c index 97f2c857048e..4d45d1b44164 100644 --- a/tools/power/cpupower/utils/helpers/amd.c +++ b/tools/power/cpupower/utils/helpers/amd.c @@ -8,7 +8,10 @@ #include #include "helpers/helpers.h" +#include "cpufreq.h" +#include "acpi_cppc.h" +/* ACPI P-States Helper Functions for AMD Processors ***************/ #define MSR_AMD_PSTATE_STATUS 0xc0010063 #define MSR_AMD_PSTATE 0xc0010064 #define MSR_AMD_PSTATE_LIMIT 0xc0010061 @@ -146,4 +149,31 @@ int amd_pci_get_num_boost_states(int *active, int *states) pci_cleanup(pci_acc); return 0; } + +/* ACPI P-States Helper Functions for AMD Processors ***************/ + +/* AMD P-State Helper Functions ************************************/ +enum amd_pstate_value { + AMD_PSTATE_HIGHEST_PERF, + AMD_PSTATE_MAX_FREQ, + AMD_PSTATE_LOWEST_NONLINEAR_FREQ, + MAX_AMD_PSTATE_VALUE_READ_FILES, +}; + +static const char *amd_pstate_value_files[MAX_AMD_PSTATE_VALUE_READ_FILES] = { + [AMD_PSTATE_HIGHEST_PERF] = "amd_pstate_highest_perf", + [AMD_PSTATE_MAX_FREQ] = "amd_pstate_max_freq", + [AMD_PSTATE_LOWEST_NONLINEAR_FREQ] = "amd_pstate_lowest_nonlinear_freq", +}; + +static unsigned long amd_pstate_get_data(unsigned int cpu, + enum amd_pstate_value value) +{ + return cpufreq_get_sysfs_value_from_table(cpu, + amd_pstate_value_files, + value, + MAX_AMD_PSTATE_VALUE_READ_FILES); +} + +/* AMD P-State Helper Functions ************************************/ #endif /* defined(__i386__) || defined(__x86_64__) */ From bf9801baa81802dac7e2a5318944ca2f4bfa74ef Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Tue, 22 Feb 2022 23:34:23 +0800 Subject: [PATCH 130/294] cpupower: Enable boost state support for AMD P-State module The legacy ACPI hardware P-States function has 3 P-States on ACPI table, the CPU frequency only can be switched between the 3 P-States. While the processor supports the boost state, it will have another boost state that the frequency can be higher than P0 state, and the state can be decoded by the function of decode_pstates() and read by amd_pci_get_num_boost_states(). However, the new AMD P-State function is different than legacy ACPI hardware P-State on AMD processors. That has a finer grain frequency range between the highest and lowest frequency. And boost frequency is actually the frequency which is mapped on highest performance ratio. The similar previous P0 frequency is mapped on nominal performance ratio. If the highest performance on the processor is higher than nominal performance, then we think the current processor supports the boost state. And it uses amd_pstate_boost_init() to initialize boost for AMD P-State function. Reviewed-by: Shuah Khan Signed-off-by: Huang Rui Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/helpers/amd.c | 18 ++++++++++++++++++ tools/power/cpupower/utils/helpers/helpers.h | 5 +++++ tools/power/cpupower/utils/helpers/misc.c | 2 ++ 3 files changed, 25 insertions(+) diff --git a/tools/power/cpupower/utils/helpers/amd.c b/tools/power/cpupower/utils/helpers/amd.c index 4d45d1b44164..f5ba528dc7db 100644 --- a/tools/power/cpupower/utils/helpers/amd.c +++ b/tools/power/cpupower/utils/helpers/amd.c @@ -175,5 +175,23 @@ static unsigned long amd_pstate_get_data(unsigned int cpu, MAX_AMD_PSTATE_VALUE_READ_FILES); } +void amd_pstate_boost_init(unsigned int cpu, int *support, int *active) +{ + unsigned long highest_perf, nominal_perf, cpuinfo_min, + cpuinfo_max, amd_pstate_max; + + highest_perf = amd_pstate_get_data(cpu, AMD_PSTATE_HIGHEST_PERF); + nominal_perf = acpi_cppc_get_data(cpu, NOMINAL_PERF); + + *support = highest_perf > nominal_perf ? 1 : 0; + if (!(*support)) + return; + + cpufreq_get_hardware_limits(cpu, &cpuinfo_min, &cpuinfo_max); + amd_pstate_max = amd_pstate_get_data(cpu, AMD_PSTATE_MAX_FREQ); + + *active = cpuinfo_max == amd_pstate_max ? 1 : 0; +} + /* AMD P-State Helper Functions ************************************/ #endif /* defined(__i386__) || defined(__x86_64__) */ diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index 62771a086871..326491e11c6e 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -140,6 +140,8 @@ extern int cpufreq_has_boost_support(unsigned int cpu, int *support, /* AMD P-State stuff **************************/ bool cpupower_amd_pstate_enabled(void); +void amd_pstate_boost_init(unsigned int cpu, + int *support, int *active); /* AMD P-State stuff **************************/ @@ -177,6 +179,9 @@ static inline int cpufreq_has_boost_support(unsigned int cpu, int *support, static inline bool cpupower_amd_pstate_enabled(void) { return false; } +static inline void amd_pstate_boost_init(unsigned int cpu, int *support, + int *active) +{} /* cpuid and cpuinfo helpers **************************/ diff --git a/tools/power/cpupower/utils/helpers/misc.c b/tools/power/cpupower/utils/helpers/misc.c index 0c483cdefcc2..e0d3145434d3 100644 --- a/tools/power/cpupower/utils/helpers/misc.c +++ b/tools/power/cpupower/utils/helpers/misc.c @@ -41,6 +41,8 @@ int cpufreq_has_boost_support(unsigned int cpu, int *support, int *active, if (ret) return ret; } + } else if (cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATE) { + amd_pstate_boost_init(cpu, support, active); } else if (cpupower_cpu_info.caps & CPUPOWER_CAP_INTEL_IDA) *support = *active = 1; return 0; From 35fdf42d90d09d2d00ef65999fe338027a6b4d8e Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Tue, 22 Feb 2022 23:34:24 +0800 Subject: [PATCH 131/294] cpupower: Move print_speed function into misc helper The print_speed can be as a common function, and expose it into misc helper header. Then it can be used on other helper files as well. Reviewed-by: Shuah Khan Signed-off-by: Huang Rui Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/cpufreq-info.c | 59 ++++---------------- tools/power/cpupower/utils/helpers/helpers.h | 1 + tools/power/cpupower/utils/helpers/misc.c | 40 +++++++++++++ 3 files changed, 52 insertions(+), 48 deletions(-) diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c index f9895e31ff5a..b429454bf3ae 100644 --- a/tools/power/cpupower/utils/cpufreq-info.c +++ b/tools/power/cpupower/utils/cpufreq-info.c @@ -84,43 +84,6 @@ static void proc_cpufreq_output(void) } static int no_rounding; -static void print_speed(unsigned long speed) -{ - unsigned long tmp; - - if (no_rounding) { - if (speed > 1000000) - printf("%u.%06u GHz", ((unsigned int) speed/1000000), - ((unsigned int) speed%1000000)); - else if (speed > 1000) - printf("%u.%03u MHz", ((unsigned int) speed/1000), - (unsigned int) (speed%1000)); - else - printf("%lu kHz", speed); - } else { - if (speed > 1000000) { - tmp = speed%10000; - if (tmp >= 5000) - speed += 10000; - printf("%u.%02u GHz", ((unsigned int) speed/1000000), - ((unsigned int) (speed%1000000)/10000)); - } else if (speed > 100000) { - tmp = speed%1000; - if (tmp >= 500) - speed += 1000; - printf("%u MHz", ((unsigned int) speed/1000)); - } else if (speed > 1000) { - tmp = speed%100; - if (tmp >= 50) - speed += 100; - printf("%u.%01u MHz", ((unsigned int) speed/1000), - ((unsigned int) (speed%1000)/100)); - } - } - - return; -} - static void print_duration(unsigned long duration) { unsigned long tmp; @@ -254,11 +217,11 @@ static int get_boost_mode(unsigned int cpu) if (freqs) { printf(_(" boost frequency steps: ")); while (freqs->next) { - print_speed(freqs->frequency); + print_speed(freqs->frequency, no_rounding); printf(", "); freqs = freqs->next; } - print_speed(freqs->frequency); + print_speed(freqs->frequency, no_rounding); printf("\n"); cpufreq_put_available_frequencies(freqs); } @@ -277,7 +240,7 @@ static int get_freq_kernel(unsigned int cpu, unsigned int human) return -EINVAL; } if (human) { - print_speed(freq); + print_speed(freq, no_rounding); } else printf("%lu", freq); printf(_(" (asserted by call to kernel)\n")); @@ -296,7 +259,7 @@ static int get_freq_hardware(unsigned int cpu, unsigned int human) return -EINVAL; } if (human) { - print_speed(freq); + print_speed(freq, no_rounding); } else printf("%lu", freq); printf(_(" (asserted by call to hardware)\n")); @@ -316,9 +279,9 @@ static int get_hardware_limits(unsigned int cpu, unsigned int human) if (human) { printf(_(" hardware limits: ")); - print_speed(min); + print_speed(min, no_rounding); printf(" - "); - print_speed(max); + print_speed(max, no_rounding); printf("\n"); } else { printf("%lu %lu\n", min, max); @@ -350,9 +313,9 @@ static int get_policy(unsigned int cpu) return -EINVAL; } printf(_(" current policy: frequency should be within ")); - print_speed(policy->min); + print_speed(policy->min, no_rounding); printf(_(" and ")); - print_speed(policy->max); + print_speed(policy->max, no_rounding); printf(".\n "); printf(_("The governor \"%s\" may decide which speed to use\n" @@ -436,7 +399,7 @@ static int get_freq_stats(unsigned int cpu, unsigned int human) struct cpufreq_stats *stats = cpufreq_get_stats(cpu, &total_time); while (stats) { if (human) { - print_speed(stats->frequency); + print_speed(stats->frequency, no_rounding); printf(":%.2f%%", (100.0 * stats->time_in_state) / total_time); } else @@ -486,11 +449,11 @@ static void debug_output_one(unsigned int cpu) if (freqs) { printf(_(" available frequency steps: ")); while (freqs->next) { - print_speed(freqs->frequency); + print_speed(freqs->frequency, no_rounding); printf(", "); freqs = freqs->next; } - print_speed(freqs->frequency); + print_speed(freqs->frequency, no_rounding); printf("\n"); cpufreq_put_available_frequencies(freqs); } diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index 326491e11c6e..fa2a8c1b1d26 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -200,5 +200,6 @@ extern struct bitmask *offline_cpus; void get_cpustate(void); void print_online_cpus(void); void print_offline_cpus(void); +void print_speed(unsigned long speed, int no_rounding); #endif /* __CPUPOWERUTILS_HELPERS__ */ diff --git a/tools/power/cpupower/utils/helpers/misc.c b/tools/power/cpupower/utils/helpers/misc.c index e0d3145434d3..9547b29254a7 100644 --- a/tools/power/cpupower/utils/helpers/misc.c +++ b/tools/power/cpupower/utils/helpers/misc.c @@ -164,3 +164,43 @@ void print_offline_cpus(void) printf(_("cpupower set operation was not performed on them\n")); } } + +/* + * print_speed + * + * Print the exact CPU frequency with appropriate unit + */ +void print_speed(unsigned long speed, int no_rounding) +{ + unsigned long tmp; + + if (no_rounding) { + if (speed > 1000000) + printf("%u.%06u GHz", ((unsigned int)speed / 1000000), + ((unsigned int)speed % 1000000)); + else if (speed > 1000) + printf("%u.%03u MHz", ((unsigned int)speed / 1000), + (unsigned int)(speed % 1000)); + else + printf("%lu kHz", speed); + } else { + if (speed > 1000000) { + tmp = speed % 10000; + if (tmp >= 5000) + speed += 10000; + printf("%u.%02u GHz", ((unsigned int)speed / 1000000), + ((unsigned int)(speed % 1000000) / 10000)); + } else if (speed > 100000) { + tmp = speed % 1000; + if (tmp >= 500) + speed += 1000; + printf("%u MHz", ((unsigned int)speed / 1000)); + } else if (speed > 1000) { + tmp = speed % 100; + if (tmp >= 50) + speed += 100; + printf("%u.%01u MHz", ((unsigned int)speed / 1000), + ((unsigned int)(speed % 1000) / 100)); + } + } +} From d8363e29178249bb505ae388ce1658484396fcde Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Tue, 22 Feb 2022 23:34:25 +0800 Subject: [PATCH 132/294] cpupower: Add function to print AMD P-State performance capabilities AMD P-State kernel module is using the fine grain frequency instead of acpi hardware pstate. So add a function to print performance and frequency values. Reviewed-by: Shuah Khan Signed-off-by: Huang Rui Signed-off-by: Shuah Khan --- tools/power/cpupower/utils/cpufreq-info.c | 9 ++++-- tools/power/cpupower/utils/helpers/amd.c | 29 ++++++++++++++++++++ tools/power/cpupower/utils/helpers/helpers.h | 5 ++++ 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c index b429454bf3ae..235243ec5ce0 100644 --- a/tools/power/cpupower/utils/cpufreq-info.c +++ b/tools/power/cpupower/utils/cpufreq-info.c @@ -146,9 +146,12 @@ static int get_boost_mode_x86(unsigned int cpu) printf(_(" Supported: %s\n"), support ? _("yes") : _("no")); printf(_(" Active: %s\n"), active ? _("yes") : _("no")); - if ((cpupower_cpu_info.vendor == X86_VENDOR_AMD && - cpupower_cpu_info.family >= 0x10) || - cpupower_cpu_info.vendor == X86_VENDOR_HYGON) { + if (cpupower_cpu_info.vendor == X86_VENDOR_AMD && + cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATE) { + return 0; + } else if ((cpupower_cpu_info.vendor == X86_VENDOR_AMD && + cpupower_cpu_info.family >= 0x10) || + cpupower_cpu_info.vendor == X86_VENDOR_HYGON) { ret = decode_pstates(cpu, b_states, pstates, &pstate_no); if (ret) return ret; diff --git a/tools/power/cpupower/utils/helpers/amd.c b/tools/power/cpupower/utils/helpers/amd.c index f5ba528dc7db..c519cc89c97f 100644 --- a/tools/power/cpupower/utils/helpers/amd.c +++ b/tools/power/cpupower/utils/helpers/amd.c @@ -193,5 +193,34 @@ void amd_pstate_boost_init(unsigned int cpu, int *support, int *active) *active = cpuinfo_max == amd_pstate_max ? 1 : 0; } +void amd_pstate_show_perf_and_freq(unsigned int cpu, int no_rounding) +{ + printf(_(" AMD PSTATE Highest Performance: %lu. Maximum Frequency: "), + amd_pstate_get_data(cpu, AMD_PSTATE_HIGHEST_PERF)); + /* + * If boost isn't active, the cpuinfo_max doesn't indicate real max + * frequency. So we read it back from amd-pstate sysfs entry. + */ + print_speed(amd_pstate_get_data(cpu, AMD_PSTATE_MAX_FREQ), no_rounding); + printf(".\n"); + + printf(_(" AMD PSTATE Nominal Performance: %lu. Nominal Frequency: "), + acpi_cppc_get_data(cpu, NOMINAL_PERF)); + print_speed(acpi_cppc_get_data(cpu, NOMINAL_FREQ) * 1000, + no_rounding); + printf(".\n"); + + printf(_(" AMD PSTATE Lowest Non-linear Performance: %lu. Lowest Non-linear Frequency: "), + acpi_cppc_get_data(cpu, LOWEST_NONLINEAR_PERF)); + print_speed(amd_pstate_get_data(cpu, AMD_PSTATE_LOWEST_NONLINEAR_FREQ), + no_rounding); + printf(".\n"); + + printf(_(" AMD PSTATE Lowest Performance: %lu. Lowest Frequency: "), + acpi_cppc_get_data(cpu, LOWEST_PERF)); + print_speed(acpi_cppc_get_data(cpu, LOWEST_FREQ) * 1000, no_rounding); + printf(".\n"); +} + /* AMD P-State Helper Functions ************************************/ #endif /* defined(__i386__) || defined(__x86_64__) */ diff --git a/tools/power/cpupower/utils/helpers/helpers.h b/tools/power/cpupower/utils/helpers/helpers.h index fa2a8c1b1d26..96e4bede078b 100644 --- a/tools/power/cpupower/utils/helpers/helpers.h +++ b/tools/power/cpupower/utils/helpers/helpers.h @@ -142,6 +142,8 @@ extern int cpufreq_has_boost_support(unsigned int cpu, int *support, bool cpupower_amd_pstate_enabled(void); void amd_pstate_boost_init(unsigned int cpu, int *support, int *active); +void amd_pstate_show_perf_and_freq(unsigned int cpu, + int no_rounding); /* AMD P-State stuff **************************/ @@ -182,6 +184,9 @@ static inline bool cpupower_amd_pstate_enabled(void) static inline void amd_pstate_boost_init(unsigned int cpu, int *support, int *active) {} +static inline void amd_pstate_show_perf_and_freq(unsigned int cpu, + int no_rounding) +{} /* cpuid and cpuinfo helpers **************************/ From 7b75bbdf5bedebed387aac6ad8411ed1cf3db5d0 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Sun, 30 Jan 2022 22:02:03 +0100 Subject: [PATCH 133/294] powercap/dtpm: Change locking scheme The different functions are all called through the dtpm_create_hierarchy() which handle the mutex. The different functions are used in this context, consequently with the lock always held. Remove all locks taken in the function and add the lock in the hierarchy creation function. Signed-off-by: Daniel Lezcano Reviewed-by: Ulf Hansson Link: https://lore.kernel.org/r/20220130210210.549877-1-daniel.lezcano@linaro.org --- drivers/powercap/dtpm.c | 95 ++++++++++++----------------------------- 1 file changed, 27 insertions(+), 68 deletions(-) diff --git a/drivers/powercap/dtpm.c b/drivers/powercap/dtpm.c index 414826a1509b..0b0121c37a1b 100644 --- a/drivers/powercap/dtpm.c +++ b/drivers/powercap/dtpm.c @@ -51,9 +51,7 @@ static int get_max_power_range_uw(struct powercap_zone *pcz, u64 *max_power_uw) { struct dtpm *dtpm = to_dtpm(pcz); - mutex_lock(&dtpm_lock); *max_power_uw = dtpm->power_max - dtpm->power_min; - mutex_unlock(&dtpm_lock); return 0; } @@ -83,14 +81,7 @@ static int __get_power_uw(struct dtpm *dtpm, u64 *power_uw) static int get_power_uw(struct powercap_zone *pcz, u64 *power_uw) { - struct dtpm *dtpm = to_dtpm(pcz); - int ret; - - mutex_lock(&dtpm_lock); - ret = __get_power_uw(dtpm, power_uw); - mutex_unlock(&dtpm_lock); - - return ret; + return __get_power_uw(to_dtpm(pcz), power_uw); } static void __dtpm_rebalance_weight(struct dtpm *dtpm) @@ -133,7 +124,16 @@ static void __dtpm_add_power(struct dtpm *dtpm) } } -static int __dtpm_update_power(struct dtpm *dtpm) +/** + * dtpm_update_power - Update the power on the dtpm + * @dtpm: a pointer to a dtpm structure to update + * + * Function to update the power values of the dtpm node specified in + * parameter. These new values will be propagated to the tree. + * + * Return: zero on success, -EINVAL if the values are inconsistent + */ +int dtpm_update_power(struct dtpm *dtpm) { int ret; @@ -155,26 +155,6 @@ static int __dtpm_update_power(struct dtpm *dtpm) return ret; } -/** - * dtpm_update_power - Update the power on the dtpm - * @dtpm: a pointer to a dtpm structure to update - * - * Function to update the power values of the dtpm node specified in - * parameter. These new values will be propagated to the tree. - * - * Return: zero on success, -EINVAL if the values are inconsistent - */ -int dtpm_update_power(struct dtpm *dtpm) -{ - int ret; - - mutex_lock(&dtpm_lock); - ret = __dtpm_update_power(dtpm); - mutex_unlock(&dtpm_lock); - - return ret; -} - /** * dtpm_release_zone - Cleanup when the node is released * @pcz: a pointer to a powercap_zone structure @@ -191,20 +171,14 @@ int dtpm_release_zone(struct powercap_zone *pcz) struct dtpm *dtpm = to_dtpm(pcz); struct dtpm *parent = dtpm->parent; - mutex_lock(&dtpm_lock); - - if (!list_empty(&dtpm->children)) { - mutex_unlock(&dtpm_lock); + if (!list_empty(&dtpm->children)) return -EBUSY; - } if (parent) list_del(&dtpm->sibling); __dtpm_sub_power(dtpm); - mutex_unlock(&dtpm_lock); - if (dtpm->ops) dtpm->ops->release(dtpm); @@ -216,23 +190,12 @@ int dtpm_release_zone(struct powercap_zone *pcz) return 0; } -static int __get_power_limit_uw(struct dtpm *dtpm, int cid, u64 *power_limit) -{ - *power_limit = dtpm->power_limit; - return 0; -} - static int get_power_limit_uw(struct powercap_zone *pcz, int cid, u64 *power_limit) { - struct dtpm *dtpm = to_dtpm(pcz); - int ret; - - mutex_lock(&dtpm_lock); - ret = __get_power_limit_uw(dtpm, cid, power_limit); - mutex_unlock(&dtpm_lock); - - return ret; + *power_limit = to_dtpm(pcz)->power_limit; + + return 0; } /* @@ -292,7 +255,7 @@ static int __set_power_limit_uw(struct dtpm *dtpm, int cid, u64 power_limit) ret = __set_power_limit_uw(child, cid, power); if (!ret) - ret = __get_power_limit_uw(child, cid, &power); + ret = get_power_limit_uw(&child->zone, cid, &power); if (ret) break; @@ -310,8 +273,6 @@ static int set_power_limit_uw(struct powercap_zone *pcz, struct dtpm *dtpm = to_dtpm(pcz); int ret; - mutex_lock(&dtpm_lock); - /* * Don't allow values outside of the power range previously * set when initializing the power numbers. @@ -323,8 +284,6 @@ static int set_power_limit_uw(struct powercap_zone *pcz, pr_debug("%s: power limit: %llu uW, power max: %llu uW\n", dtpm->zone.name, dtpm->power_limit, dtpm->power_max); - mutex_unlock(&dtpm_lock); - return ret; } @@ -335,11 +294,7 @@ static const char *get_constraint_name(struct powercap_zone *pcz, int cid) static int get_max_power_uw(struct powercap_zone *pcz, int id, u64 *max_power) { - struct dtpm *dtpm = to_dtpm(pcz); - - mutex_lock(&dtpm_lock); - *max_power = dtpm->power_max; - mutex_unlock(&dtpm_lock); + *max_power = to_dtpm(pcz)->power_max; return 0; } @@ -442,8 +397,6 @@ int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent) if (IS_ERR(pcz)) return PTR_ERR(pcz); - mutex_lock(&dtpm_lock); - if (parent) { list_add_tail(&dtpm->sibling, &parent->children); dtpm->parent = parent; @@ -459,8 +412,6 @@ int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent) pr_debug("Registered dtpm node '%s' / %llu-%llu uW, \n", dtpm->zone.name, dtpm->power_min, dtpm->power_max); - mutex_unlock(&dtpm_lock); - return 0; } @@ -605,8 +556,12 @@ int dtpm_create_hierarchy(struct of_device_id *dtpm_match_table) struct device_node *np; int i, ret; - if (pct) - return -EBUSY; + mutex_lock(&dtpm_lock); + + if (pct) { + ret = -EBUSY; + goto out_unlock; + } pct = powercap_register_control_type(NULL, "dtpm", NULL); if (IS_ERR(pct)) { @@ -648,12 +603,16 @@ int dtpm_create_hierarchy(struct of_device_id *dtpm_match_table) dtpm_subsys[i]->name, ret); } + mutex_unlock(&dtpm_lock); + return 0; out_err: powercap_unregister_control_type(pct); out_pct: pct = NULL; +out_unlock: + mutex_unlock(&dtpm_lock); return ret; } From 0aea2e4ec2a2bfa2d7e8820e37ba5b5ce04f20a5 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Sun, 30 Jan 2022 22:02:04 +0100 Subject: [PATCH 134/294] powercap/dtpm_cpu: Reset per_cpu variable in the release function The release function does not reset the per cpu variable when it is called. That will prevent creation again as the variable will be already from the previous creation. Fix it by resetting them. Signed-off-by: Daniel Lezcano Reviewed-by: Ulf Hansson Link: https://lore.kernel.org/r/20220130210210.549877-2-daniel.lezcano@linaro.org --- drivers/powercap/dtpm_cpu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/powercap/dtpm_cpu.c b/drivers/powercap/dtpm_cpu.c index eed5ad688d46..71f45d2f5a60 100644 --- a/drivers/powercap/dtpm_cpu.c +++ b/drivers/powercap/dtpm_cpu.c @@ -151,10 +151,17 @@ static int update_pd_power_uw(struct dtpm *dtpm) static void pd_release(struct dtpm *dtpm) { struct dtpm_cpu *dtpm_cpu = to_dtpm_cpu(dtpm); + struct cpufreq_policy *policy; if (freq_qos_request_active(&dtpm_cpu->qos_req)) freq_qos_remove_request(&dtpm_cpu->qos_req); + policy = cpufreq_cpu_get(dtpm_cpu->cpu); + if (policy) { + for_each_cpu(dtpm_cpu->cpu, policy->related_cpus) + per_cpu(dtpm_per_cpu, dtpm_cpu->cpu) = NULL; + } + kfree(dtpm_cpu); } From 690de0b4013f6f35bc9fced12746b9f396c471ae Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Sun, 30 Jan 2022 22:02:05 +0100 Subject: [PATCH 135/294] powercap/dtpm: Fixup kfree for virtual node When the node is virtual there is no release function associated which can free the memory. Free the memory when no 'ops' exists. Signed-off-by: Daniel Lezcano Link: https://lore.kernel.org/r/20220130210210.549877-3-daniel.lezcano@linaro.org --- drivers/powercap/dtpm.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/powercap/dtpm.c b/drivers/powercap/dtpm.c index 0b0121c37a1b..7bddd25a6767 100644 --- a/drivers/powercap/dtpm.c +++ b/drivers/powercap/dtpm.c @@ -181,12 +181,12 @@ int dtpm_release_zone(struct powercap_zone *pcz) if (dtpm->ops) dtpm->ops->release(dtpm); + else + kfree(dtpm); if (root == dtpm) root = NULL; - kfree(dtpm); - return 0; } From c404c64d64bc31bebe8a2015103671f7cd282731 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Sun, 30 Jan 2022 22:02:06 +0100 Subject: [PATCH 136/294] powercap/dtpm: Destroy hierarchy function The hierarchy creation function exits but without a destroy hierarchy function. Due to that, the modules creating the hierarchy can not be unloaded properly because they don't have an exit callback. Provide the dtpm_destroy_hierarchy() function to remove the previously created hierarchy. The function relies on all the release mechanisms implemented by the underlying powercap framework. Signed-off-by: Daniel Lezcano Reviewed-by: Ulf Hansson Link: https://lore.kernel.org/r/20220130210210.549877-4-daniel.lezcano@linaro.org --- drivers/powercap/dtpm.c | 43 +++++++++++++++++++++++++++++++++++++++++ include/linux/dtpm.h | 3 +++ 2 files changed, 46 insertions(+) diff --git a/drivers/powercap/dtpm.c b/drivers/powercap/dtpm.c index 7bddd25a6767..d9d74f981118 100644 --- a/drivers/powercap/dtpm.c +++ b/drivers/powercap/dtpm.c @@ -617,3 +617,46 @@ int dtpm_create_hierarchy(struct of_device_id *dtpm_match_table) return ret; } EXPORT_SYMBOL_GPL(dtpm_create_hierarchy); + +static void __dtpm_destroy_hierarchy(struct dtpm *dtpm) +{ + struct dtpm *child, *aux; + + list_for_each_entry_safe(child, aux, &dtpm->children, sibling) + __dtpm_destroy_hierarchy(child); + + /* + * At this point, we know all children were removed from the + * recursive call before + */ + dtpm_unregister(dtpm); +} + +void dtpm_destroy_hierarchy(void) +{ + int i; + + mutex_lock(&dtpm_lock); + + if (!pct) + goto out_unlock; + + __dtpm_destroy_hierarchy(root); + + + for (i = 0; i < ARRAY_SIZE(dtpm_subsys); i++) { + + if (!dtpm_subsys[i]->exit) + continue; + + dtpm_subsys[i]->exit(); + } + + powercap_unregister_control_type(pct); + + pct = NULL; + +out_unlock: + mutex_unlock(&dtpm_lock); +} +EXPORT_SYMBOL_GPL(dtpm_destroy_hierarchy); diff --git a/include/linux/dtpm.h b/include/linux/dtpm.h index f7a25c70dd4c..a4a13514b730 100644 --- a/include/linux/dtpm.h +++ b/include/linux/dtpm.h @@ -37,6 +37,7 @@ struct device_node; struct dtpm_subsys_ops { const char *name; int (*init)(void); + void (*exit)(void); int (*setup)(struct dtpm *, struct device_node *); }; @@ -67,4 +68,6 @@ void dtpm_unregister(struct dtpm *dtpm); int dtpm_register(const char *name, struct dtpm *dtpm, struct dtpm *parent); int dtpm_create_hierarchy(struct of_device_id *dtpm_match_table); + +void dtpm_destroy_hierarchy(void); #endif From 4712a236db409d5ee5dccb8c7e57fe54d7d3ec66 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Sun, 30 Jan 2022 22:02:07 +0100 Subject: [PATCH 137/294] powercap/dtpm: Move the 'root' reset place The 'root' node is checked everytime a dtpm node is destroyed. When we reach the end of the hierarchy destruction function, we can unconditionnaly set the 'root' node to NULL again. Signed-off-by: Daniel Lezcano Reviewed-by: Ulf Hansson Link: https://lore.kernel.org/r/20220130210210.549877-5-daniel.lezcano@linaro.org --- drivers/powercap/dtpm.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/powercap/dtpm.c b/drivers/powercap/dtpm.c index d9d74f981118..ec931a06d90a 100644 --- a/drivers/powercap/dtpm.c +++ b/drivers/powercap/dtpm.c @@ -184,9 +184,6 @@ int dtpm_release_zone(struct powercap_zone *pcz) else kfree(dtpm); - if (root == dtpm) - root = NULL; - return 0; } @@ -656,6 +653,8 @@ void dtpm_destroy_hierarchy(void) pct = NULL; + root = NULL; + out_unlock: mutex_unlock(&dtpm_lock); } From bfded2ca8f36935ff13b3b30f8e66d6135e178ac Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Sun, 30 Jan 2022 22:02:08 +0100 Subject: [PATCH 138/294] powercap/dtpm_cpu: Add exit function Now that we can destroy the hierarchy, the code must remove what it had put in place at the creation. In our case, the cpu hotplug callbacks. Signed-off-by: Daniel Lezcano Reviewed-by: Ulf Hansson Link: https://lore.kernel.org/r/20220130210210.549877-6-daniel.lezcano@linaro.org --- drivers/powercap/dtpm_cpu.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/powercap/dtpm_cpu.c b/drivers/powercap/dtpm_cpu.c index 71f45d2f5a60..bca2f912d349 100644 --- a/drivers/powercap/dtpm_cpu.c +++ b/drivers/powercap/dtpm_cpu.c @@ -299,8 +299,15 @@ static int dtpm_cpu_init(void) return 0; } +static void dtpm_cpu_exit(void) +{ + cpuhp_remove_state_nocalls(CPUHP_AP_ONLINE_DYN); + cpuhp_remove_state_nocalls(CPUHP_AP_DTPM_CPU_DEAD); +} + struct dtpm_subsys_ops dtpm_cpu_ops = { .name = KBUILD_MODNAME, .init = dtpm_cpu_init, + .exit = dtpm_cpu_exit, .setup = dtpm_cpu_setup, }; From f1ebef9e55f3c49063b575e97d2019832b8f8ef9 Mon Sep 17 00:00:00 2001 From: Daniel Lezcano Date: Sun, 30 Jan 2022 22:02:09 +0100 Subject: [PATCH 139/294] dtpm/soc/rk3399: Add the ability to unload the module The dtpm hierarchy can now be removed with the dtpm_destroy_hierarchy() function. Add the module_exit() callback so the module can be unloaded by removing the previously created hierarchy. Signed-off-by: Daniel Lezcano Reviewed-by: Ulf Hansson Link: https://lore.kernel.org/r/20220130210210.549877-7-daniel.lezcano@linaro.org --- drivers/soc/rockchip/dtpm.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/soc/rockchip/dtpm.c b/drivers/soc/rockchip/dtpm.c index ebebb748488b..5a23784b5221 100644 --- a/drivers/soc/rockchip/dtpm.c +++ b/drivers/soc/rockchip/dtpm.c @@ -52,6 +52,12 @@ static int __init rockchip_dtpm_init(void) } module_init(rockchip_dtpm_init); +static void __exit rockchip_dtpm_exit(void) +{ + return dtpm_destroy_hierarchy(); +} +module_exit(rockchip_dtpm_exit); + MODULE_SOFTDEP("pre: panfrost cpufreq-dt"); MODULE_DESCRIPTION("Rockchip DTPM driver"); MODULE_LICENSE("GPL"); From 8382dce5e4835c045f33b8958a5f559d212cdd11 Mon Sep 17 00:00:00 2001 From: Huang Rui Date: Tue, 22 Feb 2022 23:34:26 +0800 Subject: [PATCH 140/294] cpupower: Add "perf" option to print AMD P-State information Add "-c --perf" option in cpupower-frequency-info to get the performance and frequency values for AMD P-State. Commit message amended: Shuah Khan Reviewed-by: Shuah Khan Signed-off-by: Huang Rui Signed-off-by: Shuah Khan --- .../cpupower/man/cpupower-frequency-info.1 | 3 +++ tools/power/cpupower/utils/cpufreq-info.c | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/tools/power/cpupower/man/cpupower-frequency-info.1 b/tools/power/cpupower/man/cpupower-frequency-info.1 index 6aa8d239dff9..dd545b499480 100644 --- a/tools/power/cpupower/man/cpupower-frequency-info.1 +++ b/tools/power/cpupower/man/cpupower-frequency-info.1 @@ -53,6 +53,9 @@ human\-readable output for the \-f, \-w, \-s and \-y parameters. \fB\-n\fR \fB\-\-no-rounding\fR Output frequencies and latencies without rounding off values. .TP +\fB\-c\fR \fB\-\-perf\fR +Get performances and frequencies capabilities of CPPC, by reading it from hardware (only available on the hardware with CPPC). +.TP .SH "REMARKS" .LP By default only values of core zero are displayed. How to display settings of diff --git a/tools/power/cpupower/utils/cpufreq-info.c b/tools/power/cpupower/utils/cpufreq-info.c index 235243ec5ce0..0646f615fe2d 100644 --- a/tools/power/cpupower/utils/cpufreq-info.c +++ b/tools/power/cpupower/utils/cpufreq-info.c @@ -438,6 +438,17 @@ static int get_latency(unsigned int cpu, unsigned int human) return 0; } +/* --performance / -c */ + +static int get_perf_cap(unsigned int cpu) +{ + if (cpupower_cpu_info.vendor == X86_VENDOR_AMD && + cpupower_cpu_info.caps & CPUPOWER_CAP_AMD_PSTATE) + amd_pstate_show_perf_and_freq(cpu, no_rounding); + + return 0; +} + static void debug_output_one(unsigned int cpu) { struct cpufreq_available_frequencies *freqs; @@ -466,6 +477,7 @@ static void debug_output_one(unsigned int cpu) if (get_freq_hardware(cpu, 1) < 0) get_freq_kernel(cpu, 1); get_boost_mode(cpu); + get_perf_cap(cpu); } static struct option info_opts[] = { @@ -484,6 +496,7 @@ static struct option info_opts[] = { {"proc", no_argument, NULL, 'o'}, {"human", no_argument, NULL, 'm'}, {"no-rounding", no_argument, NULL, 'n'}, + {"performance", no_argument, NULL, 'c'}, { }, }; @@ -497,7 +510,7 @@ int cmd_freq_info(int argc, char **argv) int output_param = 0; do { - ret = getopt_long(argc, argv, "oefwldpgrasmybn", info_opts, + ret = getopt_long(argc, argv, "oefwldpgrasmybnc", info_opts, NULL); switch (ret) { case '?': @@ -520,6 +533,7 @@ int cmd_freq_info(int argc, char **argv) case 'e': case 's': case 'y': + case 'c': if (output_param) { output_param = -1; cont = 0; @@ -626,6 +640,9 @@ int cmd_freq_info(int argc, char **argv) case 'y': ret = get_latency(cpu, human); break; + case 'c': + ret = get_perf_cap(cpu); + break; } if (ret) return ret; From 62a40dad573406cdcb489dd45cfd201717c2e765 Mon Sep 17 00:00:00 2001 From: Oscar Shiang Date: Sun, 20 Feb 2022 20:12:00 +0800 Subject: [PATCH 141/294] docs: Remove duplicated words in trace/osnoise-tracer There are 2 duplicated words found in osnoise tracer documentation. This patch removes them. Signed-off-by: Oscar Shiang Acked-by: Steven Rostedt (Google) Acked-by: Daniel Bristot de Oliveira Link: https://lore.kernel.org/r/TYCP286MB1913117487F390E3BCE38B15A1399@TYCP286MB1913.JPNP286.PROD.OUTLOOK.COM Signed-off-by: Jonathan Corbet --- Documentation/trace/osnoise-tracer.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/trace/osnoise-tracer.rst b/Documentation/trace/osnoise-tracer.rst index b648cb9bf1f0..963def9f97c6 100644 --- a/Documentation/trace/osnoise-tracer.rst +++ b/Documentation/trace/osnoise-tracer.rst @@ -51,7 +51,7 @@ For example:: [root@f32 ~]# cd /sys/kernel/tracing/ [root@f32 tracing]# echo osnoise > current_tracer -It is possible to follow the trace by reading the trace trace file:: +It is possible to follow the trace by reading the trace file:: [root@f32 tracing]# cat trace # tracer: osnoise @@ -108,7 +108,7 @@ The tracer has a set of options inside the osnoise directory, they are: option. - tracing_threshold: the minimum delta between two time() reads to be considered as noise, in us. When set to 0, the default value will - will be used, which is currently 5 us. + be used, which is currently 5 us. Additional Tracing ------------------ From a5cdaea525c32e7def563ba07d9fef9bc6edffab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Warnie=C5=82=C5=82o?= Date: Fri, 18 Feb 2022 19:16:18 +0100 Subject: [PATCH 142/294] scripts: kernel-doc: Add the basic POD sections MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The NAME section provides the doc title, while SYNOPSIS contains the basic syntax and usage description, which will be printed in the help document and in the error output produced on wrong script usage. The rationale is to give users simple and succinct enlightment, at the same time structuring the script internally for the maintainers. In the synopsis, Rst-only options are grouped around rst, and the rest is arranged as in the OPTIONS subsections (yet to be translated into POD, check at the end of the series). The third of the basic sections, DESCRIPTION, is added separately. Signed-off-by: Tomasz Warniełło Tested-by: Randy Dunlap Acked-by: Randy Dunlap Disliked-by: Akira Yokosawa Link: https://lore.kernel.org/r/20220218181628.1411551-2-tomasz.warniello@gmail.com Signed-off-by: Jonathan Corbet --- scripts/kernel-doc | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index 3106b7536b89..c8fbf1d3d5aa 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -16,6 +16,31 @@ use strict; ## This software falls under the GNU General Public License. ## ## Please read the COPYING file for more information ## +=head1 NAME + +kernel-doc - Print formatted kernel documentation to stdout + +=head1 SYNOPSIS + + kernel-doc [-h] [-v] [-Werror] + [ -man | + -rst [-sphinx-version VERSION] [-enable-lineno] | + -none + ] + [ + -export | + -internal | + [-function NAME] ... | + [-nosymbol NAME] ... + ] + [-no-doc-sections] + [-export-file FILE] ... + FILE ... + +Run `kernel-doc -h` for details. + +=cut + # 18/01/2001 - Cleanups # Functions prototyped as foo(void) same as foo() # Stop eval'ing where we don't need to. From 43caf1a6823dc7c156cf38a6c71881c1e90cd3c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Warnie=C5=82=C5=82o?= Date: Fri, 18 Feb 2022 19:16:19 +0100 Subject: [PATCH 143/294] scripts: kernel-doc: Relink argument parsing error handling to pod2usage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The former usage function is substituted, although not as the -h and -help parameter handler yet. Purpose: Use Pod::Usage to handle documentation printing in an integrated way. Signed-off-by: Tomasz Warniełło Tested-by: Randy Dunlap Acked-by: Randy Dunlap Disliked-by: Akira Yokosawa Link: https://lore.kernel.org/r/20220218181628.1411551-3-tomasz.warniello@gmail.com Signed-off-by: Jonathan Corbet --- scripts/kernel-doc | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index c8fbf1d3d5aa..e7f7251771bb 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -16,6 +16,8 @@ use strict; ## This software falls under the GNU General Public License. ## ## Please read the COPYING file for more information ## +use Pod::Usage qw/pod2usage/; + =head1 NAME kernel-doc - Print formatted kernel documentation to stdout @@ -298,7 +300,13 @@ my $blankline_rst = "\n"; # read arguments if ($#ARGV == -1) { - usage(); + pod2usage( + -message => "No arguments!\n", + -exitval => 1, + -verbose => 99, + -sections => 'SYNOPSIS', + -output => \*STDERR, + ); } my $kernelversion; @@ -518,8 +526,14 @@ while ($ARGV[0] =~ m/^--?(.*)/) { die "Sphinx version should either major.minor or major.minor.patch format\n"; } } else { - # Unknown argument - usage(); + # Unknown argument + pod2usage( + -message => "Argument unknown!\n", + -exitval => 1, + -verbose => 99, + -sections => 'SYNOPSIS', + -output => \*STDERR, + ); } } From f1583922bf9383ce0079dfdded959dfc5585dc5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Warnie=C5=82=C5=82o?= Date: Fri, 18 Feb 2022 19:16:20 +0100 Subject: [PATCH 144/294] scripts: kernel-doc: Translate the DESCRIPTION section MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Transition the description section into POD. This is one of the standard documentation sections. This adjustment makes the section available for POD and makes it look better. Notes: - an article addition - paragraphing correction Signed-off-by: Tomasz Warniełło Tested-by: Randy Dunlap Acked-by: Randy Dunlap Disliked-by: Akira Yokosawa Link: https://lore.kernel.org/r/20220218181628.1411551-4-tomasz.warniello@gmail.com Signed-off-by: Jonathan Corbet --- scripts/kernel-doc | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index e7f7251771bb..e4203f13fa93 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -41,6 +41,15 @@ kernel-doc - Print formatted kernel documentation to stdout Run `kernel-doc -h` for details. +=head1 DESCRIPTION + +Read C language source or header FILEs, extract embedded documentation comments, +and print formatted documentation to standard output. + +The documentation comments are identified by the "/**" opening comment mark. + +See Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax. + =cut # 18/01/2001 - Cleanups @@ -72,12 +81,6 @@ sub usage { my $message = <<"EOF"; Usage: $0 [OPTION ...] FILE ... -Read C language source or header FILEs, extract embedded documentation comments, -and print formatted documentation to standard output. - -The documentation comments are identified by "/**" opening comment mark. See -Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax. - Output format selection (mutually exclusive): -man Output troff manual page format. This is the default. -rst Output reStructuredText format. From 2875f78708219feadf0956dcf9e936ec25fb7a8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Warnie=C5=82=C5=82o?= Date: Fri, 18 Feb 2022 19:16:21 +0100 Subject: [PATCH 145/294] scripts: kernel-doc: Translate the "Output format selection" subsection of OPTIONS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Another step in the direction of a uniform POD documentation, which will make users happier. Options land at the end of the script, not to clutter the file top. The default output format is corrected to rst. That's what it is now. A POD delimiting comment is added to the script head, which improves the script logical structure. Signed-off-by: Tomasz Warniełło Tested-by: Randy Dunlap Acked-by: Randy Dunlap Disliked-by: Akira Yokosawa Link: https://lore.kernel.org/r/20220218181628.1411551-5-tomasz.warniello@gmail.com Signed-off-by: Jonathan Corbet --- scripts/kernel-doc | 31 ++++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index e4203f13fa93..18eca172c4b5 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -52,6 +52,8 @@ See Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax. =cut +# more perldoc at the end of the file + # 18/01/2001 - Cleanups # Functions prototyped as foo(void) same as foo() # Stop eval'ing where we don't need to. @@ -81,11 +83,6 @@ sub usage { my $message = <<"EOF"; Usage: $0 [OPTION ...] FILE ... -Output format selection (mutually exclusive): - -man Output troff manual page format. This is the default. - -rst Output reStructuredText format. - -none Do not output documentation, only warnings. - Output format selection modifier (affects only ReST output): -sphinx-version Use the ReST C domain dialect compatible with an @@ -2563,3 +2560,27 @@ if ($Werror && $warnings) { } else { exit($output_mode eq "none" ? 0 : $errors) } + +__END__ + +=head1 OPTIONS + +=head2 Output format selection (mutually exclusive): + +=over 8 + +=item -man + +Output troff manual page format. + +=item -rst + +Output reStructuredText format. This is the default. + +=item -none + +Do not output documentation, only warnings. + +=back + +=cut From dd803b04b0a0af16e43c2af1a3e67d7ce8e1f899 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Warnie=C5=82=C5=82o?= Date: Fri, 18 Feb 2022 19:16:22 +0100 Subject: [PATCH 146/294] scripts: kernel-doc: Translate the "Output format selection modifier" subsection of OPTIONS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aim: unified POD, user more happy This section is renamed to "Output format modifiers" to make it simple. To make it even more simple, a subsection is added: "reStructuredText only". Other notes: - paragraphing correction - article correction Signed-off-by: Tomasz Warniełło Tested-by: Randy Dunlap Acked-by: Randy Dunlap Disliked-by: Akira Yokosawa Link: https://lore.kernel.org/r/20220218181628.1411551-6-tomasz.warniello@gmail.com Signed-off-by: Jonathan Corbet --- scripts/kernel-doc | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index 18eca172c4b5..b926faa16b00 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -83,13 +83,6 @@ sub usage { my $message = <<"EOF"; Usage: $0 [OPTION ...] FILE ... -Output format selection modifier (affects only ReST output): - - -sphinx-version Use the ReST C domain dialect compatible with an - specific Sphinx Version. - If not specified, kernel-doc will auto-detect using - the sphinx-build version found on PATH. - Output selection (mutually exclusive): -export Only output documentation for symbols that have been exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() @@ -2583,4 +2576,19 @@ Do not output documentation, only warnings. =back +=head2 Output format modifiers + +=head3 reStructuredText only + +=over 8 + +=item -sphinx-version VERSION + +Use the ReST C domain dialect compatible with a specific Sphinx Version. + +If not specified, kernel-doc will auto-detect using the sphinx-build version +found on PATH. + +=back + =cut From 9c77f108f43ae08e560f54c817d4aeb4857dc783 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Warnie=C5=82=C5=82o?= Date: Fri, 18 Feb 2022 19:16:23 +0100 Subject: [PATCH 147/294] scripts: kernel-doc: Translate the "Output selection" subsection of OPTIONS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aim: unified POD, user more satisfied, script better structured The plurals in -function and -nosymbol are corrected to singulars. That's how the script works now. I think this describes the syntax better. The plurar suggests multiple FILE arguments might be possible. So this seems more coherent. Other notes: - paragraphing correction - article correction Signed-off-by: Tomasz Warniełło Tested-by: Randy Dunlap Acked-by: Randy Dunlap Disliked-by: Akira Yokosawa Link: https://lore.kernel.org/r/20220218181628.1411551-7-tomasz.warniello@gmail.com Signed-off-by: Jonathan Corbet --- scripts/kernel-doc | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index b926faa16b00..e49cdb307a35 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -83,19 +83,6 @@ sub usage { my $message = <<"EOF"; Usage: $0 [OPTION ...] FILE ... -Output selection (mutually exclusive): - -export Only output documentation for symbols that have been - exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() - in any input FILE or -export-file FILE. - -internal Only output documentation for symbols that have NOT been - exported using EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() - in any input FILE or -export-file FILE. - -function NAME Only output documentation for the given function(s) - or DOC: section title(s). All other functions and DOC: - sections are ignored. May be specified multiple times. - -nosymbol NAME Exclude the specified symbols from the output - documentation. May be specified multiple times. - Output selection modifiers: -no-doc-sections Do not output DOC: sections. -enable-lineno Enable output of #define LINENO lines. Only works with @@ -2591,4 +2578,33 @@ found on PATH. =back +=head2 Output selection (mutually exclusive): + +=over 8 + +=item -export + +Only output documentation for the symbols that have been exported using +EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() in any input FILE or -export-file FILE. + +=item -internal + +Only output documentation for the symbols that have NOT been exported using +EXPORT_SYMBOL() or EXPORT_SYMBOL_GPL() in any input FILE or -export-file FILE. + +=item -function NAME + +Only output documentation for the given function or DOC: section title. +All other functions and DOC: sections are ignored. + +May be specified multiple times. + +=item -nosymbol NAME + +Exclude the specified symbol from the output documentation. + +May be specified multiple times. + +=back + =cut From c15de5a19a2881205f6f893869584c99cbe4fae4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Warnie=C5=82=C5=82o?= Date: Fri, 18 Feb 2022 19:16:24 +0100 Subject: [PATCH 148/294] scripts: kernel-doc: Translate the "Output selection modifiers" subsection of OPTIONS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aim: unified POD, user more satisfied, script better structured A subsection "reStructuredText only" is added for -enable-lineno. Other notes: - paragraphing correction Signed-off-by: Tomasz Warniełło Tested-by: Randy Dunlap Acked-by: Randy Dunlap Disliked-by: Akira Yokosawa Link: https://lore.kernel.org/r/20220218181628.1411551-8-tomasz.warniello@gmail.com Signed-off-by: Jonathan Corbet --- scripts/kernel-doc | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index e49cdb307a35..210e7e3b501b 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -83,14 +83,6 @@ sub usage { my $message = <<"EOF"; Usage: $0 [OPTION ...] FILE ... -Output selection modifiers: - -no-doc-sections Do not output DOC: sections. - -enable-lineno Enable output of #define LINENO lines. Only works with - reStructuredText format. - -export-file FILE Specify an additional FILE in which to look for - EXPORT_SYMBOL() and EXPORT_SYMBOL_GPL(). To be used with - -export or -internal. May be specified multiple times. - Other parameters: -v Verbose output, more warnings and other information. -h Print this help. @@ -2607,4 +2599,33 @@ May be specified multiple times. =back +=head2 Output selection modifiers: + +=over 8 + +=item -no-doc-sections + +Do not output DOC: sections. + +=item -export-file FILE + +Specify an additional FILE in which to look for EXPORT_SYMBOL() and +EXPORT_SYMBOL_GPL(). + +To be used with -export or -internal. + +May be specified multiple times. + +=back + +=head3 reStructuredText only + +=over 8 + +=item -enable-lineno + +Enable output of #define LINENO lines. + +=back + =cut From 834cf6b9039e6f6ebd73cc4da51cc8bc802ca777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Warnie=C5=82=C5=82o?= Date: Fri, 18 Feb 2022 19:16:25 +0100 Subject: [PATCH 149/294] scripts: kernel-doc: Translate the "Other parameters" subsection of OPTIONS MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aim: unified POD, user more satisfied, script better structured Notes: - The -help token is added. - The entries are sorted alphbetically. Signed-off-by: Tomasz Warniełło Tested-by: Randy Dunlap Acked-by: Randy Dunlap Disliked-by: Akira Yokosawa Link: https://lore.kernel.org/r/20220218181628.1411551-9-tomasz.warniello@gmail.com Signed-off-by: Jonathan Corbet --- scripts/kernel-doc | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index 210e7e3b501b..4a26a74318e6 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -83,11 +83,6 @@ sub usage { my $message = <<"EOF"; Usage: $0 [OPTION ...] FILE ... -Other parameters: - -v Verbose output, more warnings and other information. - -h Print this help. - -Werror Treat warnings as errors. - EOF print $message; exit 1; @@ -2628,4 +2623,22 @@ Enable output of #define LINENO lines. =back +=head2 Other parameters: + +=over 8 + +=item -h, -help + +Print this help. + +=item -v + +Verbose output, more warnings and other information. + +=item -Werror + +Treat warnings as errors. + +=back + =cut From 252b47da9fd9eeebbdaed448aea71010261d7dc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Warnie=C5=82=C5=82o?= Date: Fri, 18 Feb 2022 19:16:26 +0100 Subject: [PATCH 150/294] scripts: kernel-doc: Replace the usage function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Aim: unified POD, user more satisfied, script better structured You can see the results with: $ scripts/kernel-doc -help Signed-off-by: Tomasz Warniełło Tested-by: Randy Dunlap Acked-by: Randy Dunlap Disliked-by: Akira Yokosawa Link: https://lore.kernel.org/r/20220218181628.1411551-10-tomasz.warniello@gmail.com Signed-off-by: Jonathan Corbet --- scripts/kernel-doc | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index 4a26a74318e6..d7ca4877eeda 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -79,15 +79,6 @@ See Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax. # 25/07/2012 - Added support for HTML5 # -- Dan Luedtke -sub usage { - my $message = <<"EOF"; -Usage: $0 [OPTION ...] FILE ... - -EOF - print $message; - exit 1; -} - # # format of comments. # In the following table, (...)? signifies optional structure. @@ -468,7 +459,7 @@ while ($ARGV[0] =~ m/^--?(.*)/) { } elsif ($cmd eq "Werror") { $Werror = 1; } elsif (($cmd eq "h") || ($cmd eq "help")) { - usage(); + pod2usage(-exitval => 0, -verbose => 2); } elsif ($cmd eq 'no-doc-sections') { $no_doc_sections = 1; } elsif ($cmd eq 'enable-lineno') { From 258092a89085ed9536da00f27d8ddbe083c9ea0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Warnie=C5=82=C5=82o?= Date: Fri, 18 Feb 2022 19:16:27 +0100 Subject: [PATCH 151/294] scripts: kernel-doc: Drop obsolete comments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit What for? To improve the script maintainability. 1. License As stated by Jonathan Corbet in the reply to my version 1, the SPDX line is enough. 2. The to-do list comment As suggested by Jonathan Corbet in reply to my version 3, this section doesn't need to be transitioned. And so it is removed for clarity. 3. The historical changelog comments As suggested by Jonathan Corbet in a reply to v3, this section can go. I wanted to keep it, but since it doesn't contain copyright notices, let's just have it clean and simple. 4. The "format of comments" comment block As suggested by Jani Nikula in a reply to my first version of this transformation, Documentation/doc-guide/kernel-doc.rst can serve as the information hub for comment formatting. The section DESCRIPTION already points there, so the original comment block can just be removed. Suggested-by: Jonathan Corbet Suggested-by: Jani Nikula Signed-off-by: Tomasz Warniełło Tested-by: Randy Dunlap Acked-by: Randy Dunlap Disliked-by: Akira Yokosawa Link: https://lore.kernel.org/r/20220218181628.1411551-11-tomasz.warniello@gmail.com Signed-off-by: Jonathan Corbet --- scripts/kernel-doc | 143 --------------------------------------------- 1 file changed, 143 deletions(-) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index d7ca4877eeda..a5a397e22ea7 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -12,9 +12,6 @@ use strict; ## ## ## #define enhancements by Armin Kuster ## ## Copyright (c) 2000 MontaVista Software, Inc. ## -## ## -## This software falls under the GNU General Public License. ## -## Please read the COPYING file for more information ## use Pod::Usage qw/pod2usage/; @@ -54,146 +51,6 @@ See Documentation/doc-guide/kernel-doc.rst for the documentation comment syntax. # more perldoc at the end of the file -# 18/01/2001 - Cleanups -# Functions prototyped as foo(void) same as foo() -# Stop eval'ing where we don't need to. -# -- huggie@earth.li - -# 27/06/2001 - Allowed whitespace after initial "/**" and -# allowed comments before function declarations. -# -- Christian Kreibich - -# Still to do: -# - add perldoc documentation -# - Look more closely at some of the scarier bits :) - -# 26/05/2001 - Support for separate source and object trees. -# Return error code. -# Keith Owens - -# 23/09/2001 - Added support for typedefs, structs, enums and unions -# Support for Context section; can be terminated using empty line -# Small fixes (like spaces vs. \s in regex) -# -- Tim Jansen - -# 25/07/2012 - Added support for HTML5 -# -- Dan Luedtke - -# -# format of comments. -# In the following table, (...)? signifies optional structure. -# (...)* signifies 0 or more structure elements -# /** -# * function_name(:)? (- short description)? -# (* @parameterx: (description of parameter x)?)* -# (* a blank line)? -# * (Description:)? (Description of function)? -# * (section header: (section description)? )* -# (*)?*/ -# -# So .. the trivial example would be: -# -# /** -# * my_function -# */ -# -# If the Description: header tag is omitted, then there must be a blank line -# after the last parameter specification. -# e.g. -# /** -# * my_function - does my stuff -# * @my_arg: its mine damnit -# * -# * Does my stuff explained. -# */ -# -# or, could also use: -# /** -# * my_function - does my stuff -# * @my_arg: its mine damnit -# * Description: Does my stuff explained. -# */ -# etc. -# -# Besides functions you can also write documentation for structs, unions, -# enums and typedefs. Instead of the function name you must write the name -# of the declaration; the struct/union/enum/typedef must always precede -# the name. Nesting of declarations is not supported. -# Use the argument mechanism to document members or constants. -# e.g. -# /** -# * struct my_struct - short description -# * @a: first member -# * @b: second member -# * -# * Longer description -# */ -# struct my_struct { -# int a; -# int b; -# /* private: */ -# int c; -# }; -# -# All descriptions can be multiline, except the short function description. -# -# For really longs structs, you can also describe arguments inside the -# body of the struct. -# eg. -# /** -# * struct my_struct - short description -# * @a: first member -# * @b: second member -# * -# * Longer description -# */ -# struct my_struct { -# int a; -# int b; -# /** -# * @c: This is longer description of C -# * -# * You can use paragraphs to describe arguments -# * using this method. -# */ -# int c; -# }; -# -# This should be use only for struct/enum members. -# -# You can also add additional sections. When documenting kernel functions you -# should document the "Context:" of the function, e.g. whether the functions -# can be called form interrupts. Unlike other sections you can end it with an -# empty line. -# A non-void function should have a "Return:" section describing the return -# value(s). -# Example-sections should contain the string EXAMPLE so that they are marked -# appropriately in DocBook. -# -# Example: -# /** -# * user_function - function that can only be called in user context -# * @a: some argument -# * Context: !in_interrupt() -# * -# * Some description -# * Example: -# * user_function(22); -# */ -# ... -# -# -# All descriptive text is further processed, scanning for the following special -# patterns, which are highlighted appropriately. -# -# 'funcname()' - function -# '$ENVVAR' - environmental variable -# '&struct_name' - name of a structure (up to two words including 'struct') -# '&struct_name.member' - name of a structure member -# '@parameter' - name of a parameter -# '%CONST' - name of a constant. -# '``LITERAL``' - literal string without any spaces on it. - ## init lots of data my $errors = 0; From 2b306ecaf57b2b5004dcb671a46ef24a1c369db2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Warnie=C5=82=C5=82o?= Date: Fri, 18 Feb 2022 19:16:28 +0100 Subject: [PATCH 152/294] scripts: kernel-doc: Refresh the copyright lines MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit I wanted to clean up these lines, but in the end decided not to touch the old ones and just add my own about POD. I'll leave the cleanup for lawyers. Signed-off-by: Tomasz Warniełło Tested-by: Randy Dunlap Acked-by: Randy Dunlap Disliked-by: Akira Yokosawa Link: https://lore.kernel.org/r/20220218181628.1411551-12-tomasz.warniello@gmail.com Signed-off-by: Jonathan Corbet --- scripts/kernel-doc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index a5a397e22ea7..f06f68f3c3d9 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -12,6 +12,8 @@ use strict; ## ## ## #define enhancements by Armin Kuster ## ## Copyright (c) 2000 MontaVista Software, Inc. ## +# +# Copyright (C) 2022 Tomasz Warniełło (POD) use Pod::Usage qw/pod2usage/; From e334f873eb4e1638dd0b45200d2d8838a13b0cac Mon Sep 17 00:00:00 2001 From: Akira Yokosawa Date: Thu, 24 Feb 2022 22:02:46 +0900 Subject: [PATCH 153/294] docs: scripts/kernel-doc: Detect absence of FILE arg Currently, when there is no FILE argument following a switch such as -man, -rst, or -none, kernel-doc exits with a warning from perl (long msg folded): Use of uninitialized value $ARGV[0] in pattern match (m//) at ./scripts/kernel-doc line 438. , which is unhelpful. Improve the behavior by adding a check at the bottom of parsing loop. If the argument is absent, display help text and exit with the code of 1 (via usage()). Signed-off-by: Akira Yokosawa Cc: Randy Dunlap Link: https://lore.kernel.org/r/7b136049-a3ba-0eb5-8717-364d773ff914@gmail.com [jc: reworked to fix conflict with pod patches] Signed-off-by: Jonathan Corbet --- scripts/kernel-doc | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/scripts/kernel-doc b/scripts/kernel-doc index f06f68f3c3d9..9c084a2ba3b0 100755 --- a/scripts/kernel-doc +++ b/scripts/kernel-doc @@ -343,14 +343,23 @@ while ($ARGV[0] =~ m/^--?(.*)/) { die "Sphinx version should either major.minor or major.minor.patch format\n"; } } else { - # Unknown argument - pod2usage( - -message => "Argument unknown!\n", - -exitval => 1, - -verbose => 99, - -sections => 'SYNOPSIS', - -output => \*STDERR, - ); + # Unknown argument + pod2usage( + -message => "Argument unknown!\n", + -exitval => 1, + -verbose => 99, + -sections => 'SYNOPSIS', + -output => \*STDERR, + ); + } + if ($#ARGV < 0){ + pod2usage( + -message => "FILE argument missing\n", + -exitval => 1, + -verbose => 99, + -sections => 'SYNOPSIS', + -output => \*STDERR, + ); } } From 024314d6d540a24cfe029421ad7e97e1d6e886b2 Mon Sep 17 00:00:00 2001 From: Yixuan Cao Date: Wed, 23 Feb 2022 21:41:04 +0800 Subject: [PATCH 154/294] Documentation/vm/page_owner.rst: fix language There are some words that need to be fixed. Thanks for Shuah Khan's constructive suggestions. The text has been fixed as follows. a. So, if you'd like to use it, you need to add "page_owner=on" into your boot cmdline. Here, "into" has been replaced with "to". b. ...page owner is disabled in runtime due to no enabling, boot option, runtime overhead is marginal. Here, "no" has been replaced with "not". Signed-off-by: Yixuan Cao Acked-by: Randy Dunlap Link: https://lore.kernel.org/r/20220223134104.2663-1-caoyixuan2019@email.szu.edu.cn Signed-off-by: Jonathan Corbet --- Documentation/vm/page_owner.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/vm/page_owner.rst b/Documentation/vm/page_owner.rst index 9837fc8147dd..bc28edaf3de1 100644 --- a/Documentation/vm/page_owner.rst +++ b/Documentation/vm/page_owner.rst @@ -26,9 +26,9 @@ fragmentation statistics can be obtained through gfp flag information of each page. It is already implemented and activated if page owner is enabled. Other usages are more than welcome. -page owner is disabled in default. So, if you'd like to use it, you need -to add "page_owner=on" into your boot cmdline. If the kernel is built -with page owner and page owner is disabled in runtime due to no enabling +page owner is disabled by default. So, if you'd like to use it, you need +to add "page_owner=on" to your boot cmdline. If the kernel is built +with page owner and page owner is disabled in runtime due to not enabling boot option, runtime overhead is marginal. If disabled in runtime, it doesn't require memory to store owner information, so there is no runtime memory overhead. And, page owner inserts just two unlikely branches into From b089f167c384c7ca0adc50c78dd7a98f747d5e03 Mon Sep 17 00:00:00 2001 From: Naohiro Aota Date: Tue, 22 Feb 2022 10:27:51 +0900 Subject: [PATCH 155/294] Documentation: block/diskstats: update function names __make_request() and end_that_request_last() do no longer exist. Replace them with the current call-site. Signed-off-by: Naohiro Aota Reviewed-by: Chaitanya Kulkarni Reviwed-by: Jens Axboe Reviewed-by: Christoph Hellwig Link: https://lore.kernel.org/r/20220222012751.1933194-1-naohiro.aota@wdc.com Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/iostats.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Documentation/admin-guide/iostats.rst b/Documentation/admin-guide/iostats.rst index 9b14b0c2c9c4..609a3201fd4e 100644 --- a/Documentation/admin-guide/iostats.rst +++ b/Documentation/admin-guide/iostats.rst @@ -76,7 +76,7 @@ Field 3 -- # of sectors read (unsigned long) Field 4 -- # of milliseconds spent reading (unsigned int) This is the total number of milliseconds spent by all reads (as - measured from __make_request() to end_that_request_last()). + measured from blk_mq_alloc_request() to __blk_mq_end_request()). Field 5 -- # of writes completed (unsigned long) This is the total number of writes completed successfully. @@ -89,7 +89,7 @@ Field 7 -- # of sectors written (unsigned long) Field 8 -- # of milliseconds spent writing (unsigned int) This is the total number of milliseconds spent by all writes (as - measured from __make_request() to end_that_request_last()). + measured from blk_mq_alloc_request() to __blk_mq_end_request()). Field 9 -- # of I/Os currently in progress (unsigned int) The only field that should go to zero. Incremented as requests are @@ -120,7 +120,7 @@ Field 14 -- # of sectors discarded (unsigned long) Field 15 -- # of milliseconds spent discarding (unsigned int) This is the total number of milliseconds spent by all discards (as - measured from __make_request() to end_that_request_last()). + measured from blk_mq_alloc_request() to __blk_mq_end_request()). Field 16 -- # of flush requests completed This is the total number of flush requests completed successfully. From 398f7abdcb7e2307facebcbdae5639f7d35916cd Mon Sep 17 00:00:00 2001 From: Akira Yokosawa Date: Fri, 18 Feb 2022 23:11:17 +0900 Subject: [PATCH 156/294] docs: pdfdocs: Pull LaTeX preamble part out of conf.py Quote from Jon's remark [1]: I do notice that Documentation/conf.py is getting large and unapproachable. At some future point, it might be nice to pull all of the latex stuff out into a separate file where it won't scare people who stumble into it by accident. Pull LaTeX preamble settings added since commit 3b4c963243b1 ("docs: conf.py: adjust the LaTeX document output") out into sphinx/kerneldoc-preamble.sty. It will be copied to the build directory by the added "latex_additional_files" setting in conf.py. As a bonus, LaTeX/TeX code can be maintained without escaping backslashes. To compensate the loss of change history in sphinx/kerneldoc-preamble.sty, here is a list of changes made in conf.py: - f7ebe6b76940 ("docs: Activate exCJK only in CJK chapters") - 0afd4df0d16a ("docs: pdfdocs: Prevent column squeezing by tabulary") - 659653c9e546 ("docs: pdfdocs: Refactor config for CJK document") - e291ff6f5a03 ("docs: pdfdocs: Add CJK-language-specific font settings") - 7eb368cc319b ("docs: pdfdocs: Choose Serif font as CJK mainfont if possible") - 35382965bdd2 ("docs: pdfdocs: Preserve inter-phrase space in Korean translations") - 77abc2c230b1 ("docs: pdfdocs: One-half spacing for CJK translations") - 788d28a25799 ("docs: pdfdocs: Permit AutoFakeSlant for CJK fonts") - 29ac9822358f ("docs: pdfdocs: Teach xeCJK about character classes of quotation marks") - 7c5c18bdb656 ("docs: pdfdocs: Fix typo in CJK-language specific font settings") - aa872e0647dc ("docs: pdfdocs: Adjust \headheight for fancyhdr") - 8716ef413aa5 ("docs: pdfdocs: Tweak width params of TOC") - 66939df53948 ("docs: pdfdocs: Switch default CJK font to KR variants") - 7b686a2ea1e4 ("docs: pdfdocs: Enable CJKspace in TOC for Korean titles") - 5d9158e3c762 ("docs/translations: Skip CJK contents if suitable fonts not found") - b774cc46313b ("docs: pdfdocs: Move CJK monospace font setting to main conf.py") [1]: https://lore.kernel.org/all/87zgmr66cn.fsf@meer.lwn.net/ Suggested-by: Jonathan Corbet Signed-off-by: Akira Yokosawa Link: https://lore.kernel.org/r/aaa9dca1-27c0-c414-77f3-c5587db0cc5b@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/conf.py | 184 ++------------------ Documentation/sphinx/kerneldoc-preamble.sty | 184 ++++++++++++++++++++ 2 files changed, 197 insertions(+), 171 deletions(-) create mode 100644 Documentation/sphinx/kerneldoc-preamble.sty diff --git a/Documentation/conf.py b/Documentation/conf.py index fb8f69fc4d38..072ee31a301d 100644 --- a/Documentation/conf.py +++ b/Documentation/conf.py @@ -409,188 +409,25 @@ latex_elements = { # Additional stuff for the LaTeX preamble. 'preamble': ''' - % Custom width parameters for TOC --- Redefine low-level commands - % defined in report.cls - \\makeatletter - %% Redefine \\@pnumwidth (page number width) - \\renewcommand*\\@pnumwidth{2.7em} - %% Redefine \\l@chapter (chapter list entry) - \\renewcommand*\\l@chapter[2]{% - \\ifnum \\c@tocdepth >\\m@ne - \\addpenalty{-\\@highpenalty}% - \\vskip 1.0em \\@plus\\p@ - \\setlength\\@tempdima{1.8em}% - \\begingroup - \\parindent \\z@ \\rightskip \\@pnumwidth - \\parfillskip -\\@pnumwidth - \\leavevmode \\bfseries - \\advance\\leftskip\\@tempdima - \\hskip -\\leftskip - #1\\nobreak\\hfil - \\nobreak\\hb@xt@\\@pnumwidth{\\hss #2% - \\kern-\\p@\\kern\\p@}\\par - \\penalty\\@highpenalty - \\endgroup - \\fi} - %% Redefine \\l@section and \\l@subsection - \\renewcommand*\\l@section{\\@dottedtocline{1}{1.8em}{3.2em}} - \\renewcommand*\\l@subsection{\\@dottedtocline{2}{5em}{4.3em}} - \\makeatother - %% Sphinx < 1.8 doesn't have \\sphinxtableofcontentshook - \\providecommand{\\sphinxtableofcontentshook}{} - %% Undefine it for compatibility with Sphinx 1.7.9 - \\renewcommand{\\sphinxtableofcontentshook}{} % Empty the hook - % Prevent column squeezing of tabulary. - \\setlength{\\tymin}{20em} % Use some font with UTF-8 support with XeLaTeX \\usepackage{fontspec} \\setsansfont{DejaVu Sans} \\setromanfont{DejaVu Serif} \\setmonofont{DejaVu Sans Mono} - % Adjust \\headheight for fancyhdr - \\addtolength{\\headheight}{1.6pt} - \\addtolength{\\topmargin}{-1.6pt} - ''', + ''', } -# Translations have Asian (CJK) characters which are only displayed if -# xeCJK is used - -latex_elements['preamble'] += ''' - \\IfFontExistsTF{Noto Sans CJK SC}{ - % This is needed for translations - \\usepackage{xeCJK} - \\IfFontExistsTF{Noto Serif CJK KR}{ - \\setCJKmainfont{Noto Serif CJK KR}[AutoFakeSlant] - }{ - \\setCJKmainfont{Noto Sans CJK KR}[AutoFakeSlant] - } - \\setCJKsansfont{Noto Sans CJK KR}[AutoFakeSlant] - \\setCJKmonofont{Noto Sans Mono CJK KR}[AutoFakeSlant] - \\xeCJKDeclareCharClass{HalfLeft}{`“,`‘} - \\xeCJKDeclareCharClass{HalfRight}{`”,`’} - % CJK Language-specific font choices - \\IfFontExistsTF{Noto Serif CJK SC}{ - \\newCJKfontfamily[SCmain]\\scmain{Noto Serif CJK SC}[AutoFakeSlant] - \\newCJKfontfamily[SCserif]\\scserif{Noto Serif CJK SC}[AutoFakeSlant] - }{ - \\newCJKfontfamily[SCmain]\\scmain{Noto Sans CJK SC}[AutoFakeSlant] - \\newCJKfontfamily[SCserif]\\scserif{Noto Sans CJK SC}[AutoFakeSlant] - } - \\newCJKfontfamily[SCsans]\\scsans{Noto Sans CJK SC}[AutoFakeSlant] - \\newCJKfontfamily[SCmono]\\scmono{Noto Sans Mono CJK SC}[AutoFakeSlant] - \\IfFontExistsTF{Noto Serif CJK TC}{ - \\newCJKfontfamily[TCmain]\\tcmain{Noto Serif CJK TC}[AutoFakeSlant] - \\newCJKfontfamily[TCserif]\\tcserif{Noto Serif CJK TC}[AutoFakeSlant] - }{ - \\newCJKfontfamily[TCmain]\\tcmain{Noto Sans CJK TC}[AutoFakeSlant] - \\newCJKfontfamily[TCserif]\\tcserif{Noto Sans CJK TC}[AutoFakeSlant] - } - \\newCJKfontfamily[TCsans]\\tcsans{Noto Sans CJK TC}[AutoFakeSlant] - \\newCJKfontfamily[TCmono]\\tcmono{Noto Sans Mono CJK TC}[AutoFakeSlant] - \\IfFontExistsTF{Noto Serif CJK KR}{ - \\newCJKfontfamily[KRmain]\\krmain{Noto Serif CJK KR}[AutoFakeSlant] - \\newCJKfontfamily[KRserif]\\krserif{Noto Serif CJK KR}[AutoFakeSlant] - }{ - \\newCJKfontfamily[KRmain]\\krmain{Noto Sans CJK KR}[AutoFakeSlant] - \\newCJKfontfamily[KRserif]\\krserif{Noto Sans CJK KR}[AutoFakeSlant] - } - \\newCJKfontfamily[KRsans]\\krsans{Noto Sans CJK KR}[AutoFakeSlant] - \\newCJKfontfamily[KRmono]\\krmono{Noto Sans Mono CJK KR}[AutoFakeSlant] - \\IfFontExistsTF{Noto Serif CJK JP}{ - \\newCJKfontfamily[JPmain]\\jpmain{Noto Serif CJK JP}[AutoFakeSlant] - \\newCJKfontfamily[JPserif]\\jpserif{Noto Serif CJK JP}[AutoFakeSlant] - }{ - \\newCJKfontfamily[JPmain]\\jpmain{Noto Sans CJK JP}[AutoFakeSlant] - \\newCJKfontfamily[JPserif]\\jpserif{Noto Sans CJK JP}[AutoFakeSlant] - } - \\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 - \\newcommand{\\kerneldocCJKon}{\\makexeCJKactive\\onehalfspacing} - \\newcommand{\\kerneldocCJKoff}{\\makexeCJKinactive\\singlespacing} - \\newcommand{\\kerneldocBeginSC}{% - \\begingroup% - \\scmain% - \\xeCJKDeclareCharClass{FullLeft}{`“,`‘}% - \\xeCJKDeclareCharClass{FullRight}{`”,`’}% - \\renewcommand{\\CJKrmdefault}{SCserif}% - \\renewcommand{\\CJKsfdefault}{SCsans}% - \\renewcommand{\\CJKttdefault}{SCmono}% - \\xeCJKsetup{CJKspace = false}% - % For CJK ascii-art alignment - \\setmonofont{Noto Sans Mono CJK SC}[AutoFakeSlant]% - } - \\newcommand{\\kerneldocEndSC}{\\endgroup} - \\newcommand{\\kerneldocBeginTC}{% - \\begingroup% - \\tcmain% - \\xeCJKDeclareCharClass{FullLeft}{`“,`‘}% - \\xeCJKDeclareCharClass{FullRight}{`”,`’}% - \\renewcommand{\\CJKrmdefault}{TCserif}% - \\renewcommand{\\CJKsfdefault}{TCsans}% - \\renewcommand{\\CJKttdefault}{TCmono}% - \\xeCJKsetup{CJKspace = false}% - % For CJK ascii-art alignment - \\setmonofont{Noto Sans Mono CJK TC}[AutoFakeSlant]% - } - \\newcommand{\\kerneldocEndTC}{\\endgroup} - \\newcommand{\\kerneldocBeginKR}{% - \\begingroup% - \\krmain% - \\renewcommand{\\CJKrmdefault}{KRserif}% - \\renewcommand{\\CJKsfdefault}{KRsans}% - \\renewcommand{\\CJKttdefault}{KRmono}% - % \\xeCJKsetup{CJKspace = true} % true by default - % For CJK ascii-art alignment (still misaligned for Hangul) - \\setmonofont{Noto Sans Mono CJK KR}[AutoFakeSlant]% - } - \\newcommand{\\kerneldocEndKR}{\\endgroup} - \\newcommand{\\kerneldocBeginJP}{% - \\begingroup% - \\jpmain% - \\renewcommand{\\CJKrmdefault}{JPserif}% - \\renewcommand{\\CJKsfdefault}{JPsans}% - \\renewcommand{\\CJKttdefault}{JPmono}% - \\xeCJKsetup{CJKspace = false}% - % For CJK ascii-art alignment - \\setmonofont{Noto Sans Mono CJK JP}[AutoFakeSlant]% - } - \\newcommand{\\kerneldocEndJP}{\\endgroup} - % Single spacing in literal blocks - \\fvset{baselinestretch=1} - % To customize \\sphinxtableofcontents - \\usepackage{etoolbox} - % Inactivate CJK after tableofcontents - \\apptocmd{\\sphinxtableofcontents}{\\kerneldocCJKoff}{}{} - \\xeCJKsetup{CJKspace = true} % For inter-phrase space of Korean TOC - }{ % No CJK font found - % Custom macros to on/off CJK (Dummy) - \\newcommand{\\kerneldocCJKon}{} - \\newcommand{\\kerneldocCJKoff}{} - \\newcommand{\\kerneldocBeginSC}[1]{% - \\begin{sphinxadmonition}{note}{Note:} - ``Noto Sans CJK'' fonts are not found while building this PDF\\@. - Translations of zh\\_CN, zh\\_TW, ko\\_KR, and ja\\_JP are - skipped. - \\end{sphinxadmonition}} - \\newcommand{\\kerneldocEndSC}{} - \\newcommand{\\kerneldocBeginTC}[1]{} - \\newcommand{\\kerneldocEndTC}{} - \\newcommand{\\kerneldocBeginKR}[1]{} - \\newcommand{\\kerneldocEndKR}{} - \\newcommand{\\kerneldocBeginJP}[1]{} - \\newcommand{\\kerneldocEndJP}{} - } -''' - # Fix reference escape troubles with Sphinx 1.4.x if major == 1: latex_elements['preamble'] += '\\renewcommand*{\\DUrole}[2]{ #2 }\n' + +# Load kerneldoc specific LaTeX settings +latex_elements['preamble'] += ''' + % Load kerneldoc specific LaTeX settings + \\input{kerneldoc-preamble.sty} +''' + # With Sphinx 1.6, it is possible to change the Bg color directly # by using: # \definecolor{sphinxnoteBgColor}{RGB}{204,255,255} @@ -652,6 +489,11 @@ for fn in os.listdir('.'): # If false, no module index is generated. #latex_domain_indices = True +# Additional LaTeX stuff to be copied to build directory +latex_additional_files = [ + 'sphinx/kerneldoc-preamble.sty', +] + # -- Options for manual page output --------------------------------------- diff --git a/Documentation/sphinx/kerneldoc-preamble.sty b/Documentation/sphinx/kerneldoc-preamble.sty new file mode 100644 index 000000000000..4e56ccea1dbd --- /dev/null +++ b/Documentation/sphinx/kerneldoc-preamble.sty @@ -0,0 +1,184 @@ +% -*- coding: utf-8 -*- +% SPDX-License-Identifier: GPL-2.0 +% +% LaTeX preamble for "make latexdocs" or "make pdfdocs" including: +% - TOC width settings +% - Setting of tabulary (\tymin) +% - Headheight setting for fancyhdr +% - Fontfamily settings for CJK (Chinese, Japanese, and Korean) translations +% +% Note on the suffix of .sty: +% This is not implemented as a LaTeX style file, but as a file containing +% plain LaTeX code to be included into preamble. +% ".sty" is chosen because ".tex" would cause the build scripts to confuse +% this file with a LaTeX main file. +% +% Copyright (C) 2022 Akira Yokosawa + +% Custom width parameters for TOC --- Redefine low-level commands +% defined in report.cls +\makeatletter +%% Redefine \@pnumwidth (page number width) +\renewcommand*\@pnumwidth{2.7em} +%% Redefine \l@chapter (chapter list entry) +\renewcommand*\l@chapter[2]{% + \ifnum \c@tocdepth >\m@ne + \addpenalty{-\@highpenalty}% + \vskip 1.0em \@plus\p@ + \setlength\@tempdima{1.8em}% + \begingroup + \parindent \z@ \rightskip \@pnumwidth + \parfillskip -\@pnumwidth + \leavevmode \bfseries + \advance\leftskip\@tempdima + \hskip -\leftskip + #1\nobreak\hfil + \nobreak\hb@xt@\@pnumwidth{\hss #2% + \kern-\p@\kern\p@}\par + \penalty\@highpenalty + \endgroup + \fi} +%% Redefine \l@section and \l@subsection +\renewcommand*\l@section{\@dottedtocline{1}{1.8em}{3.2em}} +\renewcommand*\l@subsection{\@dottedtocline{2}{5em}{4.3em}} +\makeatother +%% Sphinx < 1.8 doesn't have \sphinxtableofcontentshook +\providecommand{\sphinxtableofcontentshook}{} +%% Undefine it for compatibility with Sphinx 1.7.9 +\renewcommand{\sphinxtableofcontentshook}{} % Empty the hook +% Prevent column squeezing of tabulary. +\setlength{\tymin}{20em} + +% Adjust \headheight for fancyhdr +\addtolength{\headheight}{1.6pt} +\addtolength{\topmargin}{-1.6pt} + +% Translations have Asian (CJK) characters which are only displayed if +% xeCJK is used +\IfFontExistsTF{Noto Sans CJK SC}{ + % This is needed for translations + \usepackage{xeCJK} + \IfFontExistsTF{Noto Serif CJK KR}{ + \setCJKmainfont{Noto Serif CJK KR}[AutoFakeSlant] + }{ + \setCJKmainfont{Noto Sans CJK KR}[AutoFakeSlant] + } + \setCJKsansfont{Noto Sans CJK KR}[AutoFakeSlant] + \setCJKmonofont{Noto Sans Mono CJK KR}[AutoFakeSlant] + \xeCJKDeclareCharClass{HalfLeft}{`“,`‘} + \xeCJKDeclareCharClass{HalfRight}{`”,`’} + % CJK Language-specific font choices + \IfFontExistsTF{Noto Serif CJK SC}{ + \newCJKfontfamily[SCmain]\scmain{Noto Serif CJK SC}[AutoFakeSlant] + \newCJKfontfamily[SCserif]\scserif{Noto Serif CJK SC}[AutoFakeSlant] + }{ + \newCJKfontfamily[SCmain]\scmain{Noto Sans CJK SC}[AutoFakeSlant] + \newCJKfontfamily[SCserif]\scserif{Noto Sans CJK SC}[AutoFakeSlant] + } + \newCJKfontfamily[SCsans]\scsans{Noto Sans CJK SC}[AutoFakeSlant] + \newCJKfontfamily[SCmono]\scmono{Noto Sans Mono CJK SC}[AutoFakeSlant] + \IfFontExistsTF{Noto Serif CJK TC}{ + \newCJKfontfamily[TCmain]\tcmain{Noto Serif CJK TC}[AutoFakeSlant] + \newCJKfontfamily[TCserif]\tcserif{Noto Serif CJK TC}[AutoFakeSlant] + }{ + \newCJKfontfamily[TCmain]\tcmain{Noto Sans CJK TC}[AutoFakeSlant] + \newCJKfontfamily[TCserif]\tcserif{Noto Sans CJK TC}[AutoFakeSlant] + } + \newCJKfontfamily[TCsans]\tcsans{Noto Sans CJK TC}[AutoFakeSlant] + \newCJKfontfamily[TCmono]\tcmono{Noto Sans Mono CJK TC}[AutoFakeSlant] + \IfFontExistsTF{Noto Serif CJK KR}{ + \newCJKfontfamily[KRmain]\krmain{Noto Serif CJK KR}[AutoFakeSlant] + \newCJKfontfamily[KRserif]\krserif{Noto Serif CJK KR}[AutoFakeSlant] + }{ + \newCJKfontfamily[KRmain]\krmain{Noto Sans CJK KR}[AutoFakeSlant] + \newCJKfontfamily[KRserif]\krserif{Noto Sans CJK KR}[AutoFakeSlant] + } + \newCJKfontfamily[KRsans]\krsans{Noto Sans CJK KR}[AutoFakeSlant] + \newCJKfontfamily[KRmono]\krmono{Noto Sans Mono CJK KR}[AutoFakeSlant] + \IfFontExistsTF{Noto Serif CJK JP}{ + \newCJKfontfamily[JPmain]\jpmain{Noto Serif CJK JP}[AutoFakeSlant] + \newCJKfontfamily[JPserif]\jpserif{Noto Serif CJK JP}[AutoFakeSlant] + }{ + \newCJKfontfamily[JPmain]\jpmain{Noto Sans CJK JP}[AutoFakeSlant] + \newCJKfontfamily[JPserif]\jpserif{Noto Sans CJK JP}[AutoFakeSlant] + } + \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 + \newcommand{\kerneldocCJKon}{\makexeCJKactive\onehalfspacing} + \newcommand{\kerneldocCJKoff}{\makexeCJKinactive\singlespacing} + \newcommand{\kerneldocBeginSC}{% + \begingroup% + \scmain% + \xeCJKDeclareCharClass{FullLeft}{`“,`‘}% + \xeCJKDeclareCharClass{FullRight}{`”,`’}% + \renewcommand{\CJKrmdefault}{SCserif}% + \renewcommand{\CJKsfdefault}{SCsans}% + \renewcommand{\CJKttdefault}{SCmono}% + \xeCJKsetup{CJKspace = false}% + % For CJK ascii-art alignment + \setmonofont{Noto Sans Mono CJK SC}[AutoFakeSlant]% + } + \newcommand{\kerneldocEndSC}{\endgroup} + \newcommand{\kerneldocBeginTC}{% + \begingroup% + \tcmain% + \xeCJKDeclareCharClass{FullLeft}{`“,`‘}% + \xeCJKDeclareCharClass{FullRight}{`”,`’}% + \renewcommand{\CJKrmdefault}{TCserif}% + \renewcommand{\CJKsfdefault}{TCsans}% + \renewcommand{\CJKttdefault}{TCmono}% + \xeCJKsetup{CJKspace = false}% + % For CJK ascii-art alignment + \setmonofont{Noto Sans Mono CJK TC}[AutoFakeSlant]% + } + \newcommand{\kerneldocEndTC}{\endgroup} + \newcommand{\kerneldocBeginKR}{% + \begingroup% + \krmain% + \renewcommand{\CJKrmdefault}{KRserif}% + \renewcommand{\CJKsfdefault}{KRsans}% + \renewcommand{\CJKttdefault}{KRmono}% + % \xeCJKsetup{CJKspace = true} % true by default + % For CJK ascii-art alignment (still misaligned for Hangul) + \setmonofont{Noto Sans Mono CJK KR}[AutoFakeSlant]% + } + \newcommand{\kerneldocEndKR}{\endgroup} + \newcommand{\kerneldocBeginJP}{% + \begingroup% + \jpmain% + \renewcommand{\CJKrmdefault}{JPserif}% + \renewcommand{\CJKsfdefault}{JPsans}% + \renewcommand{\CJKttdefault}{JPmono}% + \xeCJKsetup{CJKspace = false}% + % For CJK ascii-art alignment + \setmonofont{Noto Sans Mono CJK JP}[AutoFakeSlant]% + } + \newcommand{\kerneldocEndJP}{\endgroup} + % Single spacing in literal blocks + \fvset{baselinestretch=1} + % To customize \sphinxtableofcontents + \usepackage{etoolbox} + % Inactivate CJK after tableofcontents + \apptocmd{\sphinxtableofcontents}{\kerneldocCJKoff}{}{} + \xeCJKsetup{CJKspace = true} % For inter-phrase space of Korean TOC +}{ % No CJK font found + % Custom macros to on/off CJK (Dummy) + \newcommand{\kerneldocCJKon}{} + \newcommand{\kerneldocCJKoff}{} + \newcommand{\kerneldocBeginSC}[1]{% + \begin{sphinxadmonition}{note}{Note:} + ``Noto Sans CJK'' fonts are not found while building this PDF\@. + Translations of zh\_CN, zh\_TW, ko\_KR, and ja\_JP are skipped. + \end{sphinxadmonition}} + \newcommand{\kerneldocEndSC}{} + \newcommand{\kerneldocBeginTC}[1]{} + \newcommand{\kerneldocEndTC}{} + \newcommand{\kerneldocBeginKR}[1]{} + \newcommand{\kerneldocEndKR}{} + \newcommand{\kerneldocBeginJP}[1]{} + \newcommand{\kerneldocEndJP}{} +} From 7cee33ce237cef25cbd782783a7e0241ef6d0776 Mon Sep 17 00:00:00 2001 From: Akira Yokosawa Date: Fri, 18 Feb 2022 23:13:41 +0900 Subject: [PATCH 157/294] docs: kerneldoc-preamble.sty: Expand comments in LaTeX code Expand comments in LaTeX code and mention some of important points told in changelogs of conf.py changes. Hopefully they can help future contributors in this area. No code change involved. Signed-off-by: Akira Yokosawa Cc: Jonathan Corbet Link: https://lore.kernel.org/r/bce9261b-1950-3146-07b2-07bd2ec79158@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/sphinx/kerneldoc-preamble.sty | 61 ++++++++++++++++----- 1 file changed, 48 insertions(+), 13 deletions(-) diff --git a/Documentation/sphinx/kerneldoc-preamble.sty b/Documentation/sphinx/kerneldoc-preamble.sty index 4e56ccea1dbd..5e59900e994d 100644 --- a/Documentation/sphinx/kerneldoc-preamble.sty +++ b/Documentation/sphinx/kerneldoc-preamble.sty @@ -15,8 +15,20 @@ % % Copyright (C) 2022 Akira Yokosawa -% Custom width parameters for TOC --- Redefine low-level commands -% defined in report.cls +% Custom width parameters for TOC +% - Redefine low-level commands defined in report.cls. +% - Indent of 2 chars is preserved for ease of comparison. +% Summary of changes from default params: +% Width of page number (\@pnumwidth): 1.55em -> 2.7em +% Width of chapter number: 1.5em -> 1.8em +% Indent of section number: 1.5em -> 1.8em +% Width of section number: 2.6em -> 3.2em +% Indent of sebsection number: 4.1em -> 5em +% Width of subsection number: 3.5em -> 4.3em +% +% These params can have 4 digit page counts, 2 digit chapter counts, +% section counts of 4 digits + 1 period (e.g., 18.10), and subsection counts +% of 5 digits + 2 periods (e.g., 18.7.13). \makeatletter %% Redefine \@pnumwidth (page number width) \renewcommand*\@pnumwidth{2.7em} @@ -46,7 +58,10 @@ \providecommand{\sphinxtableofcontentshook}{} %% Undefine it for compatibility with Sphinx 1.7.9 \renewcommand{\sphinxtableofcontentshook}{} % Empty the hook -% Prevent column squeezing of tabulary. + +% Prevent column squeezing of tabulary. \tymin is set by Sphinx as: +% \setlength{\tymin}{3\fontcharwd\font`0 } +% , which is too short. \setlength{\tymin}{20em} % Adjust \headheight for fancyhdr @@ -56,8 +71,12 @@ % Translations have Asian (CJK) characters which are only displayed if % xeCJK is used \IfFontExistsTF{Noto Sans CJK SC}{ - % This is needed for translations + % Load xeCJK when CJK font is available \usepackage{xeCJK} + % Noto CJK fonts don't provide slant shape. [AutoFakeSlant] permits + % its emulation. + % Select KR variant at the beginning of each document so that quotation + % and apostorph symbols of half-width is used in TOC of Latin documents. \IfFontExistsTF{Noto Serif CJK KR}{ \setCJKmainfont{Noto Serif CJK KR}[AutoFakeSlant] }{ @@ -65,9 +84,11 @@ } \setCJKsansfont{Noto Sans CJK KR}[AutoFakeSlant] \setCJKmonofont{Noto Sans Mono CJK KR}[AutoFakeSlant] + % Teach xeCJK of half-width symbols \xeCJKDeclareCharClass{HalfLeft}{`“,`‘} \xeCJKDeclareCharClass{HalfRight}{`”,`’} % CJK Language-specific font choices + %% for Simplified Chinese \IfFontExistsTF{Noto Serif CJK SC}{ \newCJKfontfamily[SCmain]\scmain{Noto Serif CJK SC}[AutoFakeSlant] \newCJKfontfamily[SCserif]\scserif{Noto Serif CJK SC}[AutoFakeSlant] @@ -77,6 +98,7 @@ } \newCJKfontfamily[SCsans]\scsans{Noto Sans CJK SC}[AutoFakeSlant] \newCJKfontfamily[SCmono]\scmono{Noto Sans Mono CJK SC}[AutoFakeSlant] + %% for Traditional Chinese \IfFontExistsTF{Noto Serif CJK TC}{ \newCJKfontfamily[TCmain]\tcmain{Noto Serif CJK TC}[AutoFakeSlant] \newCJKfontfamily[TCserif]\tcserif{Noto Serif CJK TC}[AutoFakeSlant] @@ -86,6 +108,7 @@ } \newCJKfontfamily[TCsans]\tcsans{Noto Sans CJK TC}[AutoFakeSlant] \newCJKfontfamily[TCmono]\tcmono{Noto Sans Mono CJK TC}[AutoFakeSlant] + %% for Korean \IfFontExistsTF{Noto Serif CJK KR}{ \newCJKfontfamily[KRmain]\krmain{Noto Serif CJK KR}[AutoFakeSlant] \newCJKfontfamily[KRserif]\krserif{Noto Serif CJK KR}[AutoFakeSlant] @@ -95,6 +118,7 @@ } \newCJKfontfamily[KRsans]\krsans{Noto Sans CJK KR}[AutoFakeSlant] \newCJKfontfamily[KRmono]\krmono{Noto Sans Mono CJK KR}[AutoFakeSlant] + %% for Japanese \IfFontExistsTF{Noto Serif CJK JP}{ \newCJKfontfamily[JPmain]\jpmain{Noto Serif CJK JP}[AutoFakeSlant] \newCJKfontfamily[JPserif]\jpserif{Noto Serif CJK JP}[AutoFakeSlant] @@ -108,34 +132,39 @@ \providecommand{\onehalfspacing}{} \providecommand{\singlespacing}{} % Define custom macros to on/off CJK + %% One and half spacing for CJK contents \newcommand{\kerneldocCJKon}{\makexeCJKactive\onehalfspacing} \newcommand{\kerneldocCJKoff}{\makexeCJKinactive\singlespacing} + % Define custom macros for switching CJK font setting + %% for Simplified Chinese \newcommand{\kerneldocBeginSC}{% \begingroup% \scmain% - \xeCJKDeclareCharClass{FullLeft}{`“,`‘}% - \xeCJKDeclareCharClass{FullRight}{`”,`’}% + \xeCJKDeclareCharClass{FullLeft}{`“,`‘}% Full-width in SC + \xeCJKDeclareCharClass{FullRight}{`”,`’}% Full-width in SC \renewcommand{\CJKrmdefault}{SCserif}% \renewcommand{\CJKsfdefault}{SCsans}% \renewcommand{\CJKttdefault}{SCmono}% - \xeCJKsetup{CJKspace = false}% + \xeCJKsetup{CJKspace = false}% gobble white spaces by ' ' % For CJK ascii-art alignment \setmonofont{Noto Sans Mono CJK SC}[AutoFakeSlant]% } \newcommand{\kerneldocEndSC}{\endgroup} + %% for Traditional Chinese \newcommand{\kerneldocBeginTC}{% \begingroup% \tcmain% - \xeCJKDeclareCharClass{FullLeft}{`“,`‘}% - \xeCJKDeclareCharClass{FullRight}{`”,`’}% + \xeCJKDeclareCharClass{FullLeft}{`“,`‘}% Full-width in TC + \xeCJKDeclareCharClass{FullRight}{`”,`’}% Full-width in TC \renewcommand{\CJKrmdefault}{TCserif}% \renewcommand{\CJKsfdefault}{TCsans}% \renewcommand{\CJKttdefault}{TCmono}% - \xeCJKsetup{CJKspace = false}% + \xeCJKsetup{CJKspace = false}% gobble white spaces by ' ' % For CJK ascii-art alignment \setmonofont{Noto Sans Mono CJK TC}[AutoFakeSlant]% } \newcommand{\kerneldocEndTC}{\endgroup} + %% for Korean \newcommand{\kerneldocBeginKR}{% \begingroup% \krmain% @@ -147,29 +176,35 @@ \setmonofont{Noto Sans Mono CJK KR}[AutoFakeSlant]% } \newcommand{\kerneldocEndKR}{\endgroup} + %% for Japanese \newcommand{\kerneldocBeginJP}{% \begingroup% \jpmain% \renewcommand{\CJKrmdefault}{JPserif}% \renewcommand{\CJKsfdefault}{JPsans}% \renewcommand{\CJKttdefault}{JPmono}% - \xeCJKsetup{CJKspace = false}% + \xeCJKsetup{CJKspace = false}% gobble white space by ' ' % For CJK ascii-art alignment \setmonofont{Noto Sans Mono CJK JP}[AutoFakeSlant]% } \newcommand{\kerneldocEndJP}{\endgroup} + % Single spacing in literal blocks \fvset{baselinestretch=1} % To customize \sphinxtableofcontents \usepackage{etoolbox} % Inactivate CJK after tableofcontents \apptocmd{\sphinxtableofcontents}{\kerneldocCJKoff}{}{} - \xeCJKsetup{CJKspace = true} % For inter-phrase space of Korean TOC + \xeCJKsetup{CJKspace = true}% For inter-phrase space of Korean TOC }{ % No CJK font found - % Custom macros to on/off CJK (Dummy) + % Custom macros to on/off CJK and switch CJK fonts (Dummy) \newcommand{\kerneldocCJKon}{} \newcommand{\kerneldocCJKoff}{} + %% By defining \kerneldocBegin(SC|TC|KR|JP) as commands with an argument + %% and ignore the argument (#1) in their definitions, whole contents of + %% CJK chapters can be ignored. \newcommand{\kerneldocBeginSC}[1]{% + %% Put a note on missing CJK fonts in place of zh_CN translation. \begin{sphinxadmonition}{note}{Note:} ``Noto Sans CJK'' fonts are not found while building this PDF\@. Translations of zh\_CN, zh\_TW, ko\_KR, and ja\_JP are skipped. From 96c7f3b53cc34d8d11ce0b8c6baabba1c5ee16e0 Mon Sep 17 00:00:00 2001 From: Akira Yokosawa Date: Fri, 18 Feb 2022 23:26:34 +0900 Subject: [PATCH 158/294] Reword note on missing CJK fonts Use past tense as the fonts can be installed after the fact. Add suggestion to install "Noto Sans CJK" and "Noto Serif CJK" font families. ("Noto Serif CJK" is optional.) Signed-off-by: Akira Yokosawa Cc: Jonathan Corbet Link: https://lore.kernel.org/r/dfefa601-c58d-c86c-953f-5e4454db9409@gmail.com Signed-off-by: Jonathan Corbet --- Documentation/sphinx/kerneldoc-preamble.sty | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/Documentation/sphinx/kerneldoc-preamble.sty b/Documentation/sphinx/kerneldoc-preamble.sty index 5e59900e994d..9d0204dc38be 100644 --- a/Documentation/sphinx/kerneldoc-preamble.sty +++ b/Documentation/sphinx/kerneldoc-preamble.sty @@ -205,9 +205,16 @@ %% CJK chapters can be ignored. \newcommand{\kerneldocBeginSC}[1]{% %% Put a note on missing CJK fonts in place of zh_CN translation. - \begin{sphinxadmonition}{note}{Note:} - ``Noto Sans CJK'' fonts are not found while building this PDF\@. - Translations of zh\_CN, zh\_TW, ko\_KR, and ja\_JP are skipped. + \begin{sphinxadmonition}{note}{Note on missing fonts:} + Translations of Simplified Chinese (zh\_CN), Traditional Chinese + (zh\_TW), Korean (ko\_KR), and Japanese (ja\_JP) were skipped + due to the lack of suitable font families. + + If you want them, please install ``Noto Sans CJK'' font families + by following instructions from + \sphinxcode{./scripts/sphinx-pre-install}. + Having optional ``Noto Serif CJK'' font families will improve + the looks of those translations. \end{sphinxadmonition}} \newcommand{\kerneldocEndSC}{} \newcommand{\kerneldocBeginTC}[1]{} From 18ab307823bb643fc985d316448f2d70eb1cb7c3 Mon Sep 17 00:00:00 2001 From: Jonathan Corbet Date: Thu, 24 Feb 2022 12:35:07 -0700 Subject: [PATCH 159/294] docs: fix RST error in vm/page_owner.rst Commit f7df2b1cf03a ("tools/vm/page_owner_sort.c: count and sort by mem") added a literal text block without the necessary markup, leading to these warnings in the docs build: Documentation/vm/page_owner.rst:92: WARNING: Unexpected indentation. Documentation/vm/page_owner.rst:96: WARNING: Unexpected indentation. Documentation/vm/page_owner.rst:107: WARNING: Unexpected indentation. Add the necessary colons and make the build quieter. Signed-off-by: Jonathan Corbet --- Documentation/vm/page_owner.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/vm/page_owner.rst b/Documentation/vm/page_owner.rst index bc28edaf3de1..905555e3e483 100644 --- a/Documentation/vm/page_owner.rst +++ b/Documentation/vm/page_owner.rst @@ -85,7 +85,7 @@ Usage cat /sys/kernel/debug/page_owner > page_owner_full.txt ./page_owner_sort page_owner_full.txt sorted_page_owner.txt - The general output of ``page_owner_full.txt`` is as follows: + The general output of ``page_owner_full.txt`` is as follows:: Page allocated via order XXX, ... PFN XXX ... @@ -100,7 +100,7 @@ Usage and pages of buf, and finally sorts them according to the times. See the result about who allocated each page - in the ``sorted_page_owner.txt``. General output: + in the ``sorted_page_owner.txt``. General output:: XXX times, XXX pages: Page allocated via order XXX, ... From 1ecf393fc5a5962ebbe8d011dede6cab880f349b Mon Sep 17 00:00:00 2001 From: Thorsten Leemhuis Date: Wed, 16 Feb 2022 07:51:33 +0100 Subject: [PATCH 160/294] docs: add two documents about regression handling Create two documents explaining various aspects around regression handling and tracking; one is aimed at users, the other targets developers. The texts among others describes the first rule of Linux kernel development and what it means in practice. They also explain what a regression actually is and how to report one properly. Both texts additionally provide a brief introduction to the bot the kernel's regression tracker uses to facilitate the work, but mention the use is optional. To sum things up, provide a few quotes from Linus in the document for developers to show how serious we take regressions. Signed-off-by: Thorsten Leemhuis Link: https://lore.kernel.org/r/34e56d3588f22d7e0b4d635ef9c9c3b33ca4ac04.1644994117.git.linux@leemhuis.info Signed-off-by: Jonathan Corbet --- Documentation/admin-guide/index.rst | 1 + .../admin-guide/reporting-regressions.rst | 439 ++++++++++++ .../process/handling-regressions.rst | 659 ++++++++++++++++++ Documentation/process/index.rst | 1 + MAINTAINERS | 2 + 5 files changed, 1102 insertions(+) create mode 100644 Documentation/admin-guide/reporting-regressions.rst create mode 100644 Documentation/process/handling-regressions.rst diff --git a/Documentation/admin-guide/index.rst b/Documentation/admin-guide/index.rst index 1bedab498104..5bfafcbb9562 100644 --- a/Documentation/admin-guide/index.rst +++ b/Documentation/admin-guide/index.rst @@ -35,6 +35,7 @@ problems and bugs in particular. :maxdepth: 1 reporting-issues + reporting-regressions security-bugs bug-hunting bug-bisect diff --git a/Documentation/admin-guide/reporting-regressions.rst b/Documentation/admin-guide/reporting-regressions.rst new file mode 100644 index 000000000000..6fbd24ceb3bf --- /dev/null +++ b/Documentation/admin-guide/reporting-regressions.rst @@ -0,0 +1,439 @@ +.. SPDX-License-Identifier: (GPL-2.0+ OR CC-BY-4.0) +.. [see the bottom of this file for redistribution information] + +Reporting regressions ++++++++++++++++++++++ + +"*We don't cause regressions*" is the first rule of Linux kernel development; +Linux founder and lead developer Linus Torvalds established it himself and +ensures it's obeyed. + +This document describes what the rule means for users and how the Linux kernel's +development model ensures to address all reported regressions; aspects relevant +for kernel developers are left to Documentation/process/handling-regressions.rst. + + +The important bits (aka "TL;DR") +================================ + +#. It's a regression if something running fine with one Linux kernel works worse + or not at all with a newer version. Note, the newer kernel has to be compiled + using a similar configuration; the detailed explanations below describes this + and other fine print in more detail. + +#. Report your issue as outlined in Documentation/admin-guide/reporting-issues.rst, + it already covers all aspects important for regressions and repeated + below for convenience. Two of them are important: start your report's subject + with "[REGRESSION]" and CC or forward it to `the regression mailing list + `_ (regressions@lists.linux.dev). + +#. Optional, but recommended: when sending or forwarding your report, make the + Linux kernel regression tracking bot "regzbot" track the issue by specifying + when the regression started like this:: + + #regzbot introduced v5.13..v5.14-rc1 + + +All the details on Linux kernel regressions relevant for users +============================================================== + + +The important basics +-------------------- + + +What is a "regression" and what is the "no regressions rule"? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It's a regression if some application or practical use case running fine with +one Linux kernel works worse or not at all with a newer version compiled using a +similar configuration. The "no regressions rule" forbids this to take place; if +it happens by accident, developers that caused it are expected to quickly fix +the issue. + +It thus is a regression when a WiFi driver from Linux 5.13 works fine, but with +5.14 doesn't work at all, works significantly slower, or misbehaves somehow. +It's also a regression if a perfectly working application suddenly shows erratic +behavior with a newer kernel version; such issues can be caused by changes in +procfs, sysfs, or one of the many other interfaces Linux provides to userland +software. But keep in mind, as mentioned earlier: 5.14 in this example needs to +be built from a configuration similar to the one from 5.13. This can be achieved +using ``make olddefconfig``, as explained in more detail below. + +Note the "practical use case" in the first sentence of this section: developers +despite the "no regressions" rule are free to change any aspect of the kernel +and even APIs or ABIs to userland, as long as no existing application or use +case breaks. + +Also be aware the "no regressions" rule covers only interfaces the kernel +provides to the userland. It thus does not apply to kernel-internal interfaces +like the module API, which some externally developed drivers use to hook into +the kernel. + +How do I report a regression? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Just report the issue as outlined in +Documentation/admin-guide/reporting-issues.rst, it already describes the +important points. The following aspects outlined there are especially relevant +for regressions: + + * When checking for existing reports to join, also search the `archives of the + Linux regressions mailing list `_ and + `regzbot's web-interface `_. + + * Start your report's subject with "[REGRESSION]". + + * In your report, clearly mention the last kernel version that worked fine and + the first broken one. Ideally try to find the exact change causing the + regression using a bisection, as explained below in more detail. + + * Remember to let the Linux regressions mailing list + (regressions@lists.linux.dev) know about your report: + + * If you report the regression by mail, CC the regressions list. + + * If you report your regression to some bug tracker, forward the submitted + report by mail to the regressions list while CCing the maintainer and the + mailing list for the subsystem in question. + + If it's a regression within a stable or longterm series (e.g. + v5.15.3..v5.15.5), remember to CC the `Linux stable mailing list + `_ (stable@vger.kernel.org). + + In case you performed a successful bisection, add everyone to the CC the + culprit's commit message mentions in lines starting with "Signed-off-by:". + +When CCing for forwarding your report to the list, consider directly telling the +aforementioned Linux kernel regression tracking bot about your report. To do +that, include a paragraph like this in your mail:: + + #regzbot introduced: v5.13..v5.14-rc1 + +Regzbot will then consider your mail a report for a regression introduced in the +specified version range. In above case Linux v5.13 still worked fine and Linux +v5.14-rc1 was the first version where you encountered the issue. If you +performed a bisection to find the commit that caused the regression, specify the +culprit's commit-id instead:: + + #regzbot introduced: 1f2e3d4c5d + +Placing such a "regzbot command" is in your interest, as it will ensure the +report won't fall through the cracks unnoticed. If you omit this, the Linux +kernel's regressions tracker will take care of telling regzbot about your +regression, as long as you send a copy to the regressions mailing lists. But the +regression tracker is just one human which sometimes has to rest or occasionally +might even enjoy some time away from computers (as crazy as that might sound). +Relying on this person thus will result in an unnecessary delay before the +regressions becomes mentioned `on the list of tracked and unresolved Linux +kernel regressions `_ and the +weekly regression reports sent by regzbot. Such delays can result in Linus +Torvalds being unaware of important regressions when deciding between "continue +development or call this finished and release the final?". + +Are really all regressions fixed? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Nearly all of them are, as long as the change causing the regression (the +"culprit commit") is reliably identified. Some regressions can be fixed without +this, but often it's required. + +Who needs to find the root cause of a regression? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Developers of the affected code area should try to locate the culprit on their +own. But for them that's often impossible to do with reasonable effort, as quite +a lot of issues only occur in a particular environment outside the developer's +reach -- for example, a specific hardware platform, firmware, Linux distro, +system's configuration, or application. That's why in the end it's often up to +the reporter to locate the culprit commit; sometimes users might even need to +run additional tests afterwards to pinpoint the exact root cause. Developers +should offer advice and reasonably help where they can, to make this process +relatively easy and achievable for typical users. + +How can I find the culprit? +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Perform a bisection, as roughly outlined in +Documentation/admin-guide/reporting-issues.rst and described in more detail by +Documentation/admin-guide/bug-bisect.rst. It might sound like a lot of work, but +in many cases finds the culprit relatively quickly. If it's hard or +time-consuming to reliably reproduce the issue, consider teaming up with other +affected users to narrow down the search range together. + +Who can I ask for advice when it comes to regressions? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Send a mail to the regressions mailing list (regressions@lists.linux.dev) while +CCing the Linux kernel's regression tracker (regressions@leemhuis.info); if the +issue might better be dealt with in private, feel free to omit the list. + + +Additional details about regressions +------------------------------------ + + +What is the goal of the "no regressions rule"? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Users should feel safe when updating kernel versions and not have to worry +something might break. This is in the interest of the kernel developers to make +updating attractive: they don't want users to stay on stable or longterm Linux +series that are either abandoned or more than one and a half years old. That's +in everybody's interest, as `those series might have known bugs, security +issues, or other problematic aspects already fixed in later versions +`_. +Additionally, the kernel developers want to make it simple and appealing for +users to test the latest pre-release or regular release. That's also in +everybody's interest, as it's a lot easier to track down and fix problems, if +they are reported shortly after being introduced. + +Is the "no regressions" rule really adhered in practice? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It's taken really seriously, as can be seen by many mailing list posts from +Linux creator and lead developer Linus Torvalds, some of which are quoted in +Documentation/process/handling-regressions.rst. + +Exceptions to this rule are extremely rare; in the past developers almost always +turned out to be wrong when they assumed a particular situation was warranting +an exception. + +Who ensures the "no regressions" is actually followed? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The subsystem maintainers should take care of that, which are watched and +supported by the tree maintainers -- e.g. Linus Torvalds for mainline and +Greg Kroah-Hartman et al. for various stable/longterm series. + +All of them are helped by people trying to ensure no regression report falls +through the cracks. One of them is Thorsten Leemhuis, who's currently acting as +the Linux kernel's "regressions tracker"; to facilitate this work he relies on +regzbot, the Linux kernel regression tracking bot. That's why you want to bring +your report on the radar of these people by CCing or forwarding each report to +the regressions mailing list, ideally with a "regzbot command" in your mail to +get it tracked immediately. + +Is it a regression, if the issue can be avoided by updating some software? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Almost always: yes. If a developer tells you otherwise, ask the regression +tracker for advice as outlined above. + +Is it a regression, if a newer kernel works slower or consumes more energy? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Yes, but the difference has to be significant. A five percent slow-down in a +micro-benchmark thus is unlikely to qualify as regression, unless it also +influences the results of a broad benchmark by more than one percent. If in +doubt, ask for advice. + +Is it a regression, if an external kernel module breaks when updating Linux? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +No, as the "no regression" rule is about interfaces and services the Linux +kernel provides to the userland. It thus does not cover building or running +externally developed kernel modules, as they run in kernel-space and hook into +the kernel using internal interfaces occasionally changed. + +How are regressions handled that are caused by security fixes? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +In extremely rare situations security issues can't be fixed without causing +regressions; those fixes are given way, as they are the lesser evil in the end. +Luckily this middling almost always can be avoided, as key developers for the +affected area and often Linus Torvalds himself try very hard to fix security +issues without causing regressions. + +If you nevertheless face such a case, check the mailing list archives if people +tried their best to avoid the regression. If not, report it; if in doubt, ask +for advice as outlined above. + +What happens if fixing a regression is impossible without causing another? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Sadly these things happen, but luckily not very often; if they occur, expert +developers of the affected code area should look into the issue to find a fix +that avoids regressions or at least their impact. If you run into such a +situation, do what was outlined already for regressions caused by security +fixes: check earlier discussions if people already tried their best and ask for +advice if in doubt. + +A quick note while at it: these situations could be avoided, if people would +regularly give mainline pre-releases (say v5.15-rc1 or -rc3) from each +development cycle a test run. This is best explained by imagining a change +integrated between Linux v5.14 and v5.15-rc1 which causes a regression, but at +the same time is a hard requirement for some other improvement applied for +5.15-rc1. All these changes often can simply be reverted and the regression thus +solved, if someone finds and reports it before 5.15 is released. A few days or +weeks later this solution can become impossible, as some software might have +started to rely on aspects introduced by one of the follow-up changes: reverting +all changes would then cause a regression for users of said software and thus is +out of the question. + +Is it a regression, if some feature I relied on was removed months ago? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is, but often it's hard to fix such regressions due to the aspects outlined +in the previous section. It hence needs to be dealt with on a case-by-case +basis. This is another reason why it's in everybody's interest to regularly test +mainline pre-releases. + +Does the "no regression" rule apply if I seem to be the only affected person? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It does, but only for practical usage: the Linux developers want to be free to +remove support for hardware only to be found in attics and museums anymore. + +Note, sometimes regressions can't be avoided to make progress -- and the latter +is needed to prevent Linux from stagnation. Hence, if only very few users seem +to be affected by a regression, it for the greater good might be in their and +everyone else's interest to lettings things pass. Especially if there is an +easy way to circumvent the regression somehow, for example by updating some +software or using a kernel parameter created just for this purpose. + +Does the regression rule apply for code in the staging tree as well? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Not according to the `help text for the configuration option covering all +staging code `_, +which since its early days states:: + + Please note that these drivers are under heavy development, may or + may not work, and may contain userspace interfaces that most likely + will be changed in the near future. + +The staging developers nevertheless often adhere to the "no regressions" rule, +but sometimes bend it to make progress. That's for example why some users had to +deal with (often negligible) regressions when a WiFi driver from the staging +tree was replaced by a totally different one written from scratch. + +Why do later versions have to be "compiled with a similar configuration"? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Because the Linux kernel developers sometimes integrate changes known to cause +regressions, but make them optional and disable them in the kernel's default +configuration. This trick allows progress, as the "no regressions" rule +otherwise would lead to stagnation. + +Consider for example a new security feature blocking access to some kernel +interfaces often abused by malware, which at the same time are required to run a +few rarely used applications. The outlined approach makes both camps happy: +people using these applications can leave the new security feature off, while +everyone else can enable it without running into trouble. + +How to create a configuration similar to the one of an older kernel? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Start your machine with a known-good kernel and configure the newer Linux +version with ``make olddefconfig``. This makes the kernel's build scripts pick +up the configuration file (the ".config" file) from the running kernel as base +for the new one you are about to compile; afterwards they set all new +configuration options to their default value, which should disable new features +that might cause regressions. + +Can I report a regression I found with pre-compiled vanilla kernels? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You need to ensure the newer kernel was compiled with a similar configuration +file as the older one (see above), as those that built them might have enabled +some known-to-be incompatible feature for the newer kernel. If in doubt, report +the matter to the kernel's provider and ask for advice. + + +More about regression tracking with "regzbot" +--------------------------------------------- + +What is regression tracking and why should I care about it? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Rules like "no regressions" need someone to ensure they are followed, otherwise +they are broken either accidentally or on purpose. History has shown this to be +true for Linux kernel development as well. That's why Thorsten Leemhuis, the +Linux Kernel's regression tracker, and some people try to ensure all regression +are fixed by keeping an eye on them until they are resolved. Neither of them are +paid for this, that's why the work is done on a best effort basis. + +Why and how are Linux kernel regressions tracked using a bot? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Tracking regressions completely manually has proven to be quite hard due to the +distributed and loosely structured nature of Linux kernel development process. +That's why the Linux kernel's regression tracker developed regzbot to facilitate +the work, with the long term goal to automate regression tracking as much as +possible for everyone involved. + +Regzbot works by watching for replies to reports of tracked regressions. +Additionally, it's looking out for posted or committed patches referencing such +reports with "Link:" tags; replies to such patch postings are tracked as well. +Combined this data provides good insights into the current state of the fixing +process. + +How to see which regressions regzbot tracks currently? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Check out `regzbot's web-interface `_. + +What kind of issues are supposed to be tracked by regzbot? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The bot is meant to track regressions, hence please don't involve regzbot for +regular issues. But it's okay for the Linux kernel's regression tracker if you +involve regzbot to track severe issues, like reports about hangs, corrupted +data, or internal errors (Panic, Oops, BUG(), warning, ...). + +How to change aspects of a tracked regression? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By using a 'regzbot command' in a direct or indirect reply to the mail with the +report. The easiest way to do that: find the report in your "Sent" folder or the +mailing list archive and reply to it using your mailer's "Reply-all" function. +In that mail, use one of the following commands in a stand-alone paragraph (IOW: +use blank lines to separate one or multiple of these commands from the rest of +the mail's text). + + * Update when the regression started to happen, for example after performing a + bisection:: + + #regzbot introduced: 1f2e3d4c5d + + * Set or update the title:: + + #regzbot title: foo + + * Monitor a discussion or bugzilla.kernel.org ticket where additions aspects of + the issue or a fix are discussed::: + + #regzbot monitor: https://lore.kernel.org/r/30th.anniversary.repost@klaava.Helsinki.FI/ + #regzbot monitor: https://bugzilla.kernel.org/show_bug.cgi?id=123456789 + + * Point to a place with further details of interest, like a mailing list post + or a ticket in a bug tracker that are slightly related, but about a different + topic:: + + #regzbot link: https://bugzilla.kernel.org/show_bug.cgi?id=123456789 + + * Mark a regression as invalid:: + + #regzbot invalid: wasn't a regression, problem has always existed + +Regzbot supports a few other commands primarily used by developers or people +tracking regressions. They and more details about the aforementioned regzbot +commands can be found in the `getting started guide +`_ and +the `reference documentation `_ +for regzbot. + +.. + end-of-content +.. + 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-regressions.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/process/handling-regressions.rst b/Documentation/process/handling-regressions.rst new file mode 100644 index 000000000000..e1102a3207e3 --- /dev/null +++ b/Documentation/process/handling-regressions.rst @@ -0,0 +1,659 @@ +.. SPDX-License-Identifier: (GPL-2.0+ OR CC-BY-4.0) +.. See the bottom of this file for additional redistribution information. + +Handling regressions +++++++++++++++++++++ + +*We don't cause regressions* -- this document describes what this "first rule of +Linux kernel development" means in practice for developers. It complements +Documentation/admin-guide/reporting-regressions.rst, which covers the topic from a +user's point of view; if you never read that text, go and at least skim over it +before continuing here. + +The important bits (aka "The TL;DR") +==================================== + +#. Ensure subscribers of the `regression mailing list `_ + (regressions@lists.linux.dev) quickly become aware of any new regression + report: + + * When receiving a mailed report that did not CC the list, bring it into the + loop by immediately sending at least a brief "Reply-all" with the list + CCed. + + * Forward or bounce any reports submitted in bug trackers to the list. + +#. Make the Linux kernel regression tracking bot "regzbot" track the issue (this + is optional, but recommended): + + * For mailed reports, check if the reporter included a line like ``#regzbot + introduced v5.13..v5.14-rc1``. If not, send a reply (with the regressions + list in CC) containing a paragraph like the following, which tells regzbot + when the issue started to happen:: + + #regzbot ^introduced 1f2e3d4c5b6a + + * When forwarding reports from a bug tracker to the regressions list (see + above), include a paragraph like the following:: + + #regzbot introduced: v5.13..v5.14-rc1 + #regzbot from: Some N. Ice Human + #regzbot monitor: http://some.bugtracker.example.com/ticket?id=123456789 + +#. When submitting fixes for regressions, add "Link:" tags to the patch + description pointing to all places where the issue was reported, as + mandated by Documentation/process/submitting-patches.rst and + :ref:`Documentation/process/5.Posting.rst `. + + +All the details on Linux kernel regressions relevant for developers +=================================================================== + + +The important basics in more detail +----------------------------------- + + +What to do when receiving regression reports +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Ensure the Linux kernel's regression tracker and others subscribers of the +`regression mailing list `_ +(regressions@lists.linux.dev) become aware of any newly reported regression: + + * When you receive a report by mail that did not CC the list, immediately bring + it into the loop by sending at least a brief "Reply-all" with the list CCed; + try to ensure it gets CCed again in case you reply to a reply that omitted + the list. + + * If a report submitted in a bug tracker hits your Inbox, forward or bounce it + to the list. Consider checking the list archives beforehand, if the reporter + already forwarded the report as instructed by + Documentation/admin-guide/reporting-issues.rst. + +When doing either, consider making the Linux kernel regression tracking bot +"regzbot" immediately start tracking the issue: + + * For mailed reports, check if the reporter included a "regzbot command" like + ``#regzbot introduced 1f2e3d4c5b6a``. If not, send a reply (with the + regressions list in CC) with a paragraph like the following::: + + #regzbot ^introduced: v5.13..v5.14-rc1 + + This tells regzbot the version range in which the issue started to happen; + you can specify a range using commit-ids as well or state a single commit-id + in case the reporter bisected the culprit. + + Note the caret (^) before the "introduced": it tells regzbot to treat the + parent mail (the one you reply to) as the initial report for the regression + you want to see tracked; that's important, as regzbot will later look out + for patches with "Link:" tags pointing to the report in the archives on + lore.kernel.org. + + * When forwarding a regressions reported to a bug tracker, include a paragraph + with these regzbot commands:: + + #regzbot introduced: 1f2e3d4c5b6a + #regzbot from: Some N. Ice Human + #regzbot monitor: http://some.bugtracker.example.com/ticket?id=123456789 + + Regzbot will then automatically associate patches with the report that + contain "Link:" tags pointing to your mail or the mentioned ticket. + +What's important when fixing regressions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You don't need to do anything special when submitting fixes for regression, just +remember to do what Documentation/process/submitting-patches.rst, +:ref:`Documentation/process/5.Posting.rst `, and +Documentation/process/stable-kernel-rules.rst already explain in more detail: + + * Point to all places where the issue was reported using "Link:" tags:: + + Link: https://lore.kernel.org/r/30th.anniversary.repost@klaava.Helsinki.FI/ + Link: https://bugzilla.kernel.org/show_bug.cgi?id=1234567890 + + * Add a "Fixes:" tag to specify the commit causing the regression. + + * If the culprit was merged in an earlier development cycle, explicitly mark + the fix for backporting using the ``Cc: stable@vger.kernel.org`` tag. + +All this is expected from you and important when it comes to regression, as +these tags are of great value for everyone (you included) that might be looking +into the issue weeks, months, or years later. These tags are also crucial for +tools and scripts used by other kernel developers or Linux distributions; one of +these tools is regzbot, which heavily relies on the "Link:" tags to associate +reports for regression with changes resolving them. + + +More aspects regarding regressions developers should be aware of +---------------------------------------------------------------- + + +How to deal with changes where a risk of regression is known +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Evaluate how big the risk of regressions is, for example by performing a code +search in Linux distributions and Git forges. Also consider asking other +developers or projects likely to be affected to evaluate or even test the +proposed change; if problems surface, maybe some solution acceptable for all +can be found. + +If the risk of regressions in the end seems to be relatively small, go ahead +with the change, but let all involved parties know about the risk. Hence, make +sure your patch description makes this aspect obvious. Once the change is +merged, tell the Linux kernel's regression tracker and the regressions mailing +list about the risk, so everyone has the change on the radar in case reports +trickle in. Depending on the risk, you also might want to ask the subsystem +maintainer to mention the issue in his mainline pull request. + +What else is there to known about regressions? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Check out Documentation/admin-guide/reporting-regressions.rst, it covers a lot +of other aspects you want might want to be aware of: + + * the purpose of the "no regressions rule" + + * what issues actually qualify as regression + + * who's in charge for finding the root cause of a regression + + * how to handle tricky situations, e.g. when a regression is caused by a + security fix or when fixing a regression might cause another one + +Whom to ask for advice when it comes to regressions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Send a mail to the regressions mailing list (regressions@lists.linux.dev) while +CCing the Linux kernel's regression tracker (regressions@leemhuis.info); if the +issue might better be dealt with in private, feel free to omit the list. + + +More about regression tracking and regzbot +------------------------------------------ + + +Why the Linux kernel has a regression tracker, and why is regzbot used? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Rules like "no regressions" need someone to ensure they are followed, otherwise +they are broken either accidentally or on purpose. History has shown this to be +true for the Linux kernel as well. That's why Thorsten Leemhuis volunteered to +keep an eye on things as the Linux kernel's regression tracker, who's +occasionally helped by other people. Neither of them are paid to do this, +that's why regression tracking is done on a best effort basis. + +Earlier attempts to manually track regressions have shown it's an exhausting and +frustrating work, which is why they were abandoned after a while. To prevent +this from happening again, Thorsten developed regzbot to facilitate the work, +with the long term goal to automate regression tracking as much as possible for +everyone involved. + +How does regression tracking work with regzbot? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The bot watches for replies to reports of tracked regressions. Additionally, +it's looking out for posted or committed patches referencing such reports +with "Link:" tags; replies to such patch postings are tracked as well. +Combined this data provides good insights into the current state of the fixing +process. + +Regzbot tries to do its job with as little overhead as possible for both +reporters and developers. In fact, only reporters are burdened with an extra +duty: they need to tell regzbot about the regression report using the ``#regzbot +introduced`` command outlined above; if they don't do that, someone else can +take care of that using ``#regzbot ^introduced``. + +For developers there normally is no extra work involved, they just need to make +sure to do something that was expected long before regzbot came to light: add +"Link:" tags to the patch description pointing to all reports about the issue +fixed. + +Do I have to use regzbot? +~~~~~~~~~~~~~~~~~~~~~~~~~ + +It's in the interest of everyone if you do, as kernel maintainers like Linus +Torvalds partly rely on regzbot's tracking in their work -- for example when +deciding to release a new version or extend the development phase. For this they +need to be aware of all unfixed regression; to do that, Linus is known to look +into the weekly reports sent by regzbot. + +Do I have to tell regzbot about every regression I stumble upon? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Ideally yes: we are all humans and easily forget problems when something more +important unexpectedly comes up -- for example a bigger problem in the Linux +kernel or something in real life that's keeping us away from keyboards for a +while. Hence, it's best to tell regzbot about every regression, except when you +immediately write a fix and commit it to a tree regularly merged to the affected +kernel series. + +How to see which regressions regzbot tracks currently? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Check `regzbot's web-interface `_ +for the latest info; alternatively, `search for the latest regression report +`_, +which regzbot normally sends out once a week on Sunday evening (UTC), which is a +few hours before Linus usually publishes new (pre-)releases. + +What places is regzbot monitoring? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Regzbot is watching the most important Linux mailing lists as well as the git +repositories of linux-next, mainline, and stable/longterm. + +What kind of issues are supposed to be tracked by regzbot? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The bot is meant to track regressions, hence please don't involve regzbot for +regular issues. But it's okay for the Linux kernel's regression tracker if you +use regzbot to track severe issues, like reports about hangs, corrupted data, +or internal errors (Panic, Oops, BUG(), warning, ...). + +Can I add regressions found by CI systems to regzbot's tracking? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Feel free to do so, if the particular regression likely has impact on practical +use cases and thus might be noticed by users; hence, please don't involve +regzbot for theoretical regressions unlikely to show themselves in real world +usage. + +How to interact with regzbot? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +By using a 'regzbot command' in a direct or indirect reply to the mail with the +regression report. These commands need to be in their own paragraph (IOW: they +need to be separated from the rest of the mail using blank lines). + +One such command is ``#regzbot introduced ``, which makes +regzbot consider your mail as a regressions report added to the tracking, as +already described above; ``#regzbot ^introduced `` is another +such command, which makes regzbot consider the parent mail as a report for a +regression which it starts to track. + +Once one of those two commands has been utilized, other regzbot commands can be +used in direct or indirect replies to the report. You can write them below one +of the `introduced` commands or in replies to the mail that used one of them +or itself is a reply to that mail: + + * Set or update the title:: + + #regzbot title: foo + + * Monitor a discussion or bugzilla.kernel.org ticket where additions aspects of + the issue or a fix are discussed -- for example the posting of a patch fixing + the regression:: + + #regzbot monitor: https://lore.kernel.org/all/30th.anniversary.repost@klaava.Helsinki.FI/ + + Monitoring only works for lore.kernel.org and bugzilla.kernel.org; regzbot + will consider all messages in that thread or ticket as related to the fixing + process. + + * Point to a place with further details of interest, like a mailing list post + or a ticket in a bug tracker that are slightly related, but about a different + topic:: + + #regzbot link: https://bugzilla.kernel.org/show_bug.cgi?id=123456789 + + * Mark a regression as fixed by a commit that is heading upstream or already + landed:: + + #regzbot fixed-by: 1f2e3d4c5d + + * Mark a regression as a duplicate of another one already tracked by regzbot:: + + #regzbot dup-of: https://lore.kernel.org/all/30th.anniversary.repost@klaava.Helsinki.FI/ + + * Mark a regression as invalid:: + + #regzbot invalid: wasn't a regression, problem has always existed + +Is there more to tell about regzbot and its commands? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +More detailed and up-to-date information about the Linux +kernel's regression tracking bot can be found on its +`project page `_, which among others +contains a `getting started guide `_ +and `reference documentation `_ +which both cover more details than the above section. + +Quotes from Linus about regression +---------------------------------- + +Find below a few real life examples of how Linus Torvalds expects regressions to +be handled: + + * From `2017-10-26 (1/2) + `_:: + + If you break existing user space setups THAT IS A REGRESSION. + + It's not ok to say "but we'll fix the user space setup". + + Really. NOT OK. + + [...] + + The first rule is: + + - we don't cause regressions + + and the corollary is that when regressions *do* occur, we admit to + them and fix them, instead of blaming user space. + + The fact that you have apparently been denying the regression now for + three weeks means that I will revert, and I will stop pulling apparmor + requests until the people involved understand how kernel development + is done. + + * From `2017-10-26 (2/2) + `_:: + + People should basically always feel like they can update their kernel + and simply not have to worry about it. + + I refuse to introduce "you can only update the kernel if you also + update that other program" kind of limitations. If the kernel used to + work for you, the rule is that it continues to work for you. + + There have been exceptions, but they are few and far between, and they + generally have some major and fundamental reasons for having happened, + that were basically entirely unavoidable, and people _tried_hard_ to + avoid them. Maybe we can't practically support the hardware any more + after it is decades old and nobody uses it with modern kernels any + more. Maybe there's a serious security issue with how we did things, + and people actually depended on that fundamentally broken model. Maybe + there was some fundamental other breakage that just _had_ to have a + flag day for very core and fundamental reasons. + + And notice that this is very much about *breaking* peoples environments. + + Behavioral changes happen, and maybe we don't even support some + feature any more. There's a number of fields in /proc//stat that + are printed out as zeroes, simply because they don't even *exist* in + the kernel any more, or because showing them was a mistake (typically + an information leak). But the numbers got replaced by zeroes, so that + the code that used to parse the fields still works. The user might not + see everything they used to see, and so behavior is clearly different, + but things still _work_, even if they might no longer show sensitive + (or no longer relevant) information. + + But if something actually breaks, then the change must get fixed or + reverted. And it gets fixed in the *kernel*. Not by saying "well, fix + your user space then". It was a kernel change that exposed the + problem, it needs to be the kernel that corrects for it, because we + have a "upgrade in place" model. We don't have a "upgrade with new + user space". + + And I seriously will refuse to take code from people who do not + understand and honor this very simple rule. + + This rule is also not going to change. + + And yes, I realize that the kernel is "special" in this respect. I'm + proud of it. + + I have seen, and can point to, lots of projects that go "We need to + break that use case in order to make progress" or "you relied on + undocumented behavior, it sucks to be you" or "there's a better way to + do what you want to do, and you have to change to that new better + way", and I simply don't think that's acceptable outside of very early + alpha releases that have experimental users that know what they signed + up for. The kernel hasn't been in that situation for the last two + decades. + + We do API breakage _inside_ the kernel all the time. We will fix + internal problems by saying "you now need to do XYZ", but then it's + about internal kernel API's, and the people who do that then also + obviously have to fix up all the in-kernel users of that API. Nobody + can say "I now broke the API you used, and now _you_ need to fix it + up". Whoever broke something gets to fix it too. + + And we simply do not break user space. + + * From `2020-05-21 + `_:: + + The rules about regressions have never been about any kind of + documented behavior, or where the code lives. + + The rules about regressions are always about "breaks user workflow". + + Users are literally the _only_ thing that matters. + + No amount of "you shouldn't have used this" or "that behavior was + undefined, it's your own fault your app broke" or "that used to work + simply because of a kernel bug" is at all relevant. + + Now, reality is never entirely black-and-white. So we've had things + like "serious security issue" etc that just forces us to make changes + that may break user space. But even then the rule is that we don't + really have other options that would allow things to continue. + + And obviously, if users take years to even notice that something + broke, or if we have sane ways to work around the breakage that + doesn't make for too much trouble for users (ie "ok, there are a + handful of users, and they can use a kernel command line to work + around it" kind of things) we've also been a bit less strict. + + But no, "that was documented to be broken" (whether it's because the + code was in staging or because the man-page said something else) is + irrelevant. If staging code is so useful that people end up using it, + that means that it's basically regular kernel code with a flag saying + "please clean this up". + + The other side of the coin is that people who talk about "API + stability" are entirely wrong. API's don't matter either. You can make + any changes to an API you like - as long as nobody notices. + + Again, the regression rule is not about documentation, not about + API's, and not about the phase of the moon. + + It's entirely about "we caused problems for user space that used to work". + + * From `2017-11-05 + `_:: + + And our regression rule has never been "behavior doesn't change". + That would mean that we could never make any changes at all. + + For example, we do things like add new error handling etc all the + time, which we then sometimes even add tests for in our kselftest + directory. + + So clearly behavior changes all the time and we don't consider that a + regression per se. + + The rule for a regression for the kernel is that some real user + workflow breaks. Not some test. Not a "look, I used to be able to do + X, now I can't". + + * From `2018-08-03 + `_:: + + YOU ARE MISSING THE #1 KERNEL RULE. + + We do not regress, and we do not regress exactly because your are 100% wrong. + + And the reason you state for your opinion is in fact exactly *WHY* you + are wrong. + + Your "good reasons" are pure and utter garbage. + + The whole point of "we do not regress" is so that people can upgrade + the kernel and never have to worry about it. + + > Kernel had a bug which has been fixed + + That is *ENTIRELY* immaterial. + + Guys, whether something was buggy or not DOES NOT MATTER. + + Why? + + Bugs happen. That's a fact of life. Arguing that "we had to break + something because we were fixing a bug" is completely insane. We fix + tens of bugs every single day, thinking that "fixing a bug" means that + we can break something is simply NOT TRUE. + + So bugs simply aren't even relevant to the discussion. They happen, + they get found, they get fixed, and it has nothing to do with "we + break users". + + Because the only thing that matters IS THE USER. + + How hard is that to understand? + + Anybody who uses "but it was buggy" as an argument is entirely missing + the point. As far as the USER was concerned, it wasn't buggy - it + worked for him/her. + + Maybe it worked *because* the user had taken the bug into account, + maybe it worked because the user didn't notice - again, it doesn't + matter. It worked for the user. + + Breaking a user workflow for a "bug" is absolutely the WORST reason + for breakage you can imagine. + + It's basically saying "I took something that worked, and I broke it, + but now it's better". Do you not see how f*cking insane that statement + is? + + And without users, your program is not a program, it's a pointless + piece of code that you might as well throw away. + + Seriously. This is *why* the #1 rule for kernel development is "we + don't break users". Because "I fixed a bug" is absolutely NOT AN + ARGUMENT if that bug fix broke a user setup. You actually introduced a + MUCH BIGGER bug by "fixing" something that the user clearly didn't + even care about. + + And dammit, we upgrade the kernel ALL THE TIME without upgrading any + other programs at all. It is absolutely required, because flag-days + and dependencies are horribly bad. + + And it is also required simply because I as a kernel developer do not + upgrade random other tools that I don't even care about as I develop + the kernel, and I want any of my users to feel safe doing the same + time. + + So no. Your rule is COMPLETELY wrong. If you cannot upgrade a kernel + without upgrading some other random binary, then we have a problem. + + * From `2021-06-05 + `_:: + + THERE ARE NO VALID ARGUMENTS FOR REGRESSIONS. + + Honestly, security people need to understand that "not working" is not + a success case of security. It's a failure case. + + Yes, "not working" may be secure. But security in that case is *pointless*. + + * From `2011-05-06 (1/3) + `_:: + + Binary compatibility is more important. + + And if binaries don't use the interface to parse the format (or just + parse it wrongly - see the fairly recent example of adding uuid's to + /proc/self/mountinfo), then it's a regression. + + And regressions get reverted, unless there are security issues or + similar that makes us go "Oh Gods, we really have to break things". + + I don't understand why this simple logic is so hard for some kernel + developers to understand. Reality matters. Your personal wishes matter + NOT AT ALL. + + If you made an interface that can be used without parsing the + interface description, then we're stuck with the interface. Theory + simply doesn't matter. + + You could help fix the tools, and try to avoid the compatibility + issues that way. There aren't that many of them. + + From `2011-05-06 (2/3) + `_:: + + it's clearly NOT an internal tracepoint. By definition. It's being + used by powertop. + + From `2011-05-06 (3/3) + `_:: + + We have programs that use that ABI and thus it's a regression if they break. + + * From `2012-07-06 `_:: + + > Now this got me wondering if Debian _unstable_ actually qualifies as a + > standard distro userspace. + + Oh, if the kernel breaks some standard user space, that counts. Tons + of people run Debian unstable + + * From `2019-09-15 + `_:: + + One _particularly_ last-minute revert is the top-most commit (ignoring + the version change itself) done just before the release, and while + it's very annoying, it's perhaps also instructive. + + What's instructive about it is that I reverted a commit that wasn't + actually buggy. In fact, it was doing exactly what it set out to do, + and did it very well. In fact it did it _so_ well that the much + improved IO patterns it caused then ended up revealing a user-visible + regression due to a real bug in a completely unrelated area. + + The actual details of that regression are not the reason I point that + revert out as instructive, though. It's more that it's an instructive + example of what counts as a regression, and what the whole "no + regressions" kernel rule means. The reverted commit didn't change any + API's, and it didn't introduce any new bugs. But it ended up exposing + another problem, and as such caused a kernel upgrade to fail for a + user. So it got reverted. + + The point here being that we revert based on user-reported _behavior_, + not based on some "it changes the ABI" or "it caused a bug" concept. + The problem was really pre-existing, and it just didn't happen to + trigger before. The better IO patterns introduced by the change just + happened to expose an old bug, and people had grown to depend on the + previously benign behavior of that old issue. + + And never fear, we'll re-introduce the fix that improved on the IO + patterns once we've decided just how to handle the fact that we had a + bad interaction with an interface that people had then just happened + to rely on incidental behavior for before. It's just that we'll have + to hash through how to do that (there are no less than three different + patches by three different developers being discussed, and there might + be more coming...). In the meantime, I reverted the thing that exposed + the problem to users for this release, even if I hope it will be + re-introduced (perhaps even backported as a stable patch) once we have + consensus about the issue it exposed. + + Take-away from the whole thing: it's not about whether you change the + kernel-userspace ABI, or fix a bug, or about whether the old code + "should never have worked in the first place". It's about whether + something breaks existing users' workflow. + + Anyway, that was my little aside on the whole regression thing. Since + it's that "first rule of kernel programming", I felt it is perhaps + worth just bringing it up every once in a while + +.. + end-of-content +.. + 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/process/handling-regressions.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/process/index.rst b/Documentation/process/index.rst index 9f1b88492bb3..428e39074f61 100644 --- a/Documentation/process/index.rst +++ b/Documentation/process/index.rst @@ -25,6 +25,7 @@ Below are the essential guides that every developer should read. code-of-conduct-interpretation development-process submitting-patches + handling-regressions programming-language coding-style maintainer-handbooks diff --git a/MAINTAINERS b/MAINTAINERS index ea3e6c914384..6c62f7e0dc9d 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10438,6 +10438,8 @@ KERNEL REGRESSIONS M: Thorsten Leemhuis L: regressions@lists.linux.dev S: Supported +F: Documentation/admin-guide/reporting-regressions.rst +F: Documentation/process/handling-regressions.rst KERNEL SELFTEST FRAMEWORK M: Shuah Khan From d2b40ba2cce207ecea8a740f71e113f03cc75fd5 Mon Sep 17 00:00:00 2001 From: Thorsten Leemhuis Date: Wed, 16 Feb 2022 07:51:34 +0100 Subject: [PATCH 161/294] docs: *-regressions.rst: explain how quickly issues should be handled Add a section with a few rules of thumb about how quickly developers should address regressions to Documentation/process/handling-regressions.rst; additionally, add a short paragraph about this to the companion document Documentation/admin-guide/reporting-regressions.rst as well. The rules of thumb were written after studying the quotes from Linus found in handling-regressions.rst and especially influenced by statements like "Users are literally the _only_ thing that matters" and "without users, your program is not a program, it's a pointless piece of code that you might as well throw away". The author interpreted those in perspective to how the various Linux kernel series are maintained currently and what those practices might mean for users running into a regression on a small or big kernel update. That for example lead to the paragraph starting with "Aim to get fixes for regressions mainlined within one week after identifying the culprit, if the regression was introduced in a stable/longterm release or the devel cycle for the latest mainline release". Some might see this as pretty high bar, but on the other hand something like that is needed to not leave users out in the cold for too long -- which can quickly happen when updating to the latest stable series, as the previous one is normally stamped "End of Life" about three or four weeks after a new mainline release. This makes a lot of users switch during this timeframe. Any of them thus risk running into regressions not promptly fixed; even worse, once the previous stable series is EOLed for real, users that face a regression might be left with only three options: (1) continue running an outdated and thus potentially insecure kernel version from an abandoned stable series (2) run the kernel with the regression (3) downgrade to an earlier longterm series still supported This is better avoided, as (1) puts users and their data in danger, (2) will only be possible if it's a minor regression that doesn't interfere with booting or serious usage, and (3) might be regression itself or impossible on the particular machine, as the users might require drivers or features only introduced after the latest longterm series branched of. In the end this lead to the aforementioned "Aim to fix regression within one week" part. It's also the reason for the "Try to resolve any regressions introduced in the current development cycle before its end.". Signed-off-by: Thorsten Leemhuis CC: Linus Torvalds Acked-by: Greg Kroah-Hartman Reviewed-by: Lukas Bulwahn Link: https://lore.kernel.org/r/a7b717b52c0d54cdec9b6daf56ed6669feddee2c.1644994117.git.linux@leemhuis.info Signed-off-by: Jonathan Corbet --- .../admin-guide/reporting-regressions.rst | 12 +++ .../process/handling-regressions.rst | 87 +++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/Documentation/admin-guide/reporting-regressions.rst b/Documentation/admin-guide/reporting-regressions.rst index 6fbd24ceb3bf..d8adccdae23f 100644 --- a/Documentation/admin-guide/reporting-regressions.rst +++ b/Documentation/admin-guide/reporting-regressions.rst @@ -214,6 +214,18 @@ your report on the radar of these people by CCing or forwarding each report to the regressions mailing list, ideally with a "regzbot command" in your mail to get it tracked immediately. +How quickly are regressions normally fixed? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Developers should fix any reported regression as quickly as possible, to provide +affected users with a solution in a timely manner and prevent more users from +running into the issue; nevertheless developers need to take enough time and +care to ensure regression fixes do not cause additional damage. + +The answer thus depends on various factors like the impact of a regression, its +age, or the Linux series in which it occurs. In the end though, most regressions +should be fixed within two weeks. + Is it a regression, if the issue can be avoided by updating some software? ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/Documentation/process/handling-regressions.rst b/Documentation/process/handling-regressions.rst index e1102a3207e3..abb741b1aeee 100644 --- a/Documentation/process/handling-regressions.rst +++ b/Documentation/process/handling-regressions.rst @@ -45,6 +45,10 @@ The important bits (aka "The TL;DR") mandated by Documentation/process/submitting-patches.rst and :ref:`Documentation/process/5.Posting.rst `. +#. Try to fix regressions quickly once the culprit has been identified; fixes + for most regressions should be merged within two weeks, but some need to be + resolved within two or three days. + All the details on Linux kernel regressions relevant for developers =================================================================== @@ -125,6 +129,89 @@ tools and scripts used by other kernel developers or Linux distributions; one of these tools is regzbot, which heavily relies on the "Link:" tags to associate reports for regression with changes resolving them. +Prioritize work on fixing regressions +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +You should fix any reported regression as quickly as possible, to provide +affected users with a solution in a timely manner and prevent more users from +running into the issue; nevertheless developers need to take enough time and +care to ensure regression fixes do not cause additional damage. + +In the end though, developers should give their best to prevent users from +running into situations where a regression leaves them only three options: "run +a kernel with a regression that seriously impacts usage", "continue running an +outdated and thus potentially insecure kernel version for more than two weeks +after a regression's culprit was identified", and "downgrade to a still +supported kernel series that lack required features". + +How to realize this depends a lot on the situation. Here are a few rules of +thumb for you, in order or importance: + + * Prioritize work on handling regression reports and fixing regression over all + other Linux kernel work, unless the latter concerns acute security issues or + bugs causing data loss or damage. + + * Always consider reverting the culprit commits and reapplying them later + together with necessary fixes, as this might be the least dangerous and + quickest way to fix a regression. + + * Developers should handle regressions in all supported kernel series, but are + free to delegate the work to the stable team, if the issue probably at no + point in time occurred with mainline. + + * Try to resolve any regressions introduced in the current development before + its end. If you fear a fix might be too risky to apply only days before a new + mainline release, let Linus decide: submit the fix separately to him as soon + as possible with the explanation of the situation. He then can make a call + and postpone the release if necessary, for example if multiple such changes + show up in his inbox. + + * Address regressions in stable, longterm, or proper mainline releases with + more urgency than regressions in mainline pre-releases. That changes after + the release of the fifth pre-release, aka "-rc5": mainline then becomes as + important, to ensure all the improvements and fixes are ideally tested + together for at least one week before Linus releases a new mainline version. + + * Fix regressions within two or three days, if they are critical for some + reason -- for example, if the issue is likely to affect many users of the + kernel series in question on all or certain architectures. Note, this + includes mainline, as issues like compile errors otherwise might prevent many + testers or continuous integration systems from testing the series. + + * Aim to fix regressions within one week after the culprit was identified, if + the issue was introduced in either: + + * a recent stable/longterm release + + * the development cycle of the latest proper mainline release + + In the latter case (say Linux v5.14), try to address regressions even + quicker, if the stable series for the predecessor (v5.13) will be abandoned + soon or already was stamped "End-of-Life" (EOL) -- this usually happens about + three to four weeks after a new mainline release. + + * Try to fix all other regressions within two weeks after the culprit was + found. Two or three additional weeks are acceptable for performance + regressions and other issues which are annoying, but don't prevent anyone + from running Linux (unless it's an issue in the current development cycle, + as those should ideally be addressed before the release). A few weeks in + total are acceptable if a regression can only be fixed with a risky change + and at the same time is affecting only a few users; as much time is + also okay if the regression is already present in the second newest longterm + kernel series. + +Note: The aforementioned time frames for resolving regressions are meant to +include getting the fix tested, reviewed, and merged into mainline, ideally with +the fix being in linux-next at least briefly. This leads to delays you need to +account for. + +Subsystem maintainers are expected to assist in reaching those periods by doing +timely reviews and quick handling of accepted patches. They thus might have to +send git-pull requests earlier or more often than usual; depending on the fix, +it might even be acceptable to skip testing in linux-next. Especially fixes for +regressions in stable and longterm kernels need to be handled quickly, as fixes +need to be merged in mainline before they can be backported to older series. + More aspects regarding regressions developers should be aware of ---------------------------------------------------------------- From 247097e2bbff4201b85eee8de4f31b4065877f67 Mon Sep 17 00:00:00 2001 From: Thorsten Leemhuis Date: Wed, 16 Feb 2022 07:51:35 +0100 Subject: [PATCH 162/294] docs: reporting-issues.rst: link new document about regressions Make Documentation/admin-guide/reporting-issues.rst point to the newly created document about regressions (Documentation/admin-guide/regressions-regressions.rst). This allows to shorten a few explanations the new document describes better and in more detail. While at it move the copyright hint to the end of the file and remove quotes around links to other places in the documentation. Both issues came up during the review of the new documents about regressions. Signed-off-by: Thorsten Leemhuis Link: https://lore.kernel.org/r/f20114eaddc356a8c79dd62812a6c7f4ca5d87b9.1644994117.git.linux@leemhuis.info Signed-off-by: Jonathan Corbet --- .../admin-guide/reporting-issues.rst | 73 +++++++++---------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/Documentation/admin-guide/reporting-issues.rst b/Documentation/admin-guide/reporting-issues.rst index d7ac13f789cc..ec62151fe672 100644 --- a/Documentation/admin-guide/reporting-issues.rst +++ b/Documentation/admin-guide/reporting-issues.rst @@ -1,14 +1,5 @@ .. 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. Reporting issues ++++++++++++++++ @@ -395,22 +386,16 @@ fixed as soon as possible, hence there are 'issues of high priority' that get handled slightly differently in the reporting process. Three type of cases qualify: regressions, security issues, and really severe problems. -You deal with a 'regression' if something that worked with an older version of -the Linux kernel does not work with a newer one or somehow works worse with it. -It thus is a regression when a WiFi driver that did a fine job with Linux 5.7 -somehow misbehaves with 5.8 or doesn't work at all. It's also a regression if -an application shows erratic behavior with a newer kernel, which might happen -due to incompatible changes in the interface between the kernel and the -userland (like procfs and sysfs). Significantly reduced performance or -increased power consumption also qualify as regression. But keep in mind: the -new kernel needs to be built with a configuration that is similar to the one -from the old kernel (see below how to achieve that). That's because the kernel -developers sometimes can not avoid incompatibilities when implementing new -features; but to avoid regressions such features have to be enabled explicitly -during build time configuration. +You deal with a regression if some application or practical use case running +fine with one Linux kernel works worse or not at all with a newer version +compiled using a similar configuration. The document +Documentation/admin-guide/reporting-regressions.rst explains this in more +detail. It also provides a good deal of other information about regressions you +might want to be aware of; it for example explains how to add your issue to the +list of tracked regressions, to ensure it won't fall through the cracks. What qualifies as security issue is left to your judgment. Consider reading -'Documentation/admin-guide/security-bugs.rst' before proceeding, as it +Documentation/admin-guide/security-bugs.rst before proceeding, as it provides additional details how to best handle security issues. An issue is a 'really severe problem' when something totally unacceptably bad @@ -517,7 +502,7 @@ line starting with 'CPU:'. It should end with 'Not tainted' if the kernel was not tainted when it noticed the problem; it was tainted if you see 'Tainted:' followed by a few spaces and some letters. -If your kernel is tainted, study 'Documentation/admin-guide/tainted-kernels.rst' +If your kernel is tainted, study Documentation/admin-guide/tainted-kernels.rst to find out why. Try to eliminate the reason. Often it's caused by one these three things: @@ -1043,7 +1028,7 @@ down the culprit, as maintainers often won't have the time or setup at hand to reproduce it themselves. To find the change there is a process called 'bisection' which the document -'Documentation/admin-guide/bug-bisect.rst' describes in detail. That process +Documentation/admin-guide/bug-bisect.rst describes in detail. That process will often require you to build about ten to twenty kernel images, trying to reproduce the issue with each of them before building the next. Yes, that takes some time, but don't worry, it works a lot quicker than most people assume. @@ -1073,10 +1058,11 @@ When dealing with regressions make sure the issue you face is really caused by the kernel and not by something else, as outlined above already. In the whole process keep in mind: an issue only qualifies as regression if the -older and the newer kernel got built with a similar configuration. The best way -to archive this: copy the configuration file (``.config``) from the old working -kernel freshly to each newer kernel version you try. Afterwards run ``make -olddefconfig`` to adjust it for the needs of the new version. +older and the newer kernel got built with a similar configuration. This can be +achieved by using ``make olddefconfig``, as explained in more detail by +Documentation/admin-guide/reporting-regressions.rst; that document also +provides a good deal of other information about regressions you might want to be +aware of. Write and send the report @@ -1283,7 +1269,7 @@ them when sending the report by mail. If you filed it in a bug tracker, forward the report's text to these addresses; but on top of it put a small note where you mention that you filed it with a link to the ticket. -See 'Documentation/admin-guide/security-bugs.rst' for more information. +See Documentation/admin-guide/security-bugs.rst for more information. Duties after the report went out @@ -1571,7 +1557,7 @@ Once your report is out your might get asked to do a proper one, as it allows to pinpoint the exact change that causes the issue (which then can easily get reverted to fix the issue quickly). Hence consider to do a proper bisection right away if time permits. See the section 'Special care for regressions' and -the document 'Documentation/admin-guide/bug-bisect.rst' for details how to +the document Documentation/admin-guide/bug-bisect.rst for details how to perform one. In case of a successful bisection add the author of the culprit to the recipients; also CC everyone in the signed-off-by chain, which you find at the end of its commit message. @@ -1594,7 +1580,7 @@ Some fixes are too complex Even small and seemingly obvious code-changes sometimes introduce new and totally unexpected problems. The maintainers of the stable and longterm kernels are very aware of that and thus only apply changes to these kernels that are -within rules outlined in 'Documentation/process/stable-kernel-rules.rst'. +within rules outlined in Documentation/process/stable-kernel-rules.rst. Complex or risky changes for example do not qualify and thus only get applied to mainline. Other fixes are easy to get backported to the newest stable and @@ -1756,10 +1742,23 @@ art will lay some groundwork to improve the situation over time. .. - This text is maintained by Thorsten Leemhuis . If you - spot a typo or small mistake, feel free to let him know directly and he'll - fix it. 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 + end-of-content +.. + This document is maintained by Thorsten Leemhuis . If + you spot a typo or small mistake, feel free to let him know directly and + he'll fix it. 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. From 9ddb00a2a136cc6ebbf6ee32fcf527d0d66044a2 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 11 Feb 2022 08:09:27 -0800 Subject: [PATCH 163/294] ACPI: fan: Fix error reporting to user space When user get/set cur_state fails, it should be some negative error value instead of whatever returned by acpi_evaluate_object() or from acpi_execute_simple_method(). The return value from these apis is some positive values greater than 0. For example if AE_NOT_FOUND is returned it will be "5". In other ACPI drivers, -ENODEV is returned when ACPI_FAILURE(status) is true. Do the same thing here for thermal sysfs callbacks for get and set for failures. Signed-off-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki --- drivers/acpi/fan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan.c index 5cd0ceb50bc8..098d64568d6d 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan.c @@ -107,7 +107,7 @@ static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state) status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer); if (ACPI_FAILURE(status)) { dev_err(&device->dev, "Get fan state failed\n"); - return status; + return -ENODEV; } obj = buffer.pointer; @@ -195,7 +195,7 @@ static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state) fan->fps[state].control); if (ACPI_FAILURE(status)) { dev_dbg(&device->dev, "Failed to set state by _FSL\n"); - return status; + return -ENODEV; } return 0; From 00ae053a0533155d830da27070a931e6fa747327 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 11 Feb 2022 08:09:28 -0800 Subject: [PATCH 164/294] ACPI: fan: Separate file for attributes creation Move the functionality of creation of sysfs attributes under acpi device to a new file fan_attr.c. This cleans up the core fan code, which just use thermal sysfs interface. The original fan.c is renamed to fan_core.c. No functional changes are expected. Signed-off-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki --- drivers/acpi/Makefile | 3 + drivers/acpi/fan.h | 35 +++++++++++ drivers/acpi/fan_attr.c | 86 ++++++++++++++++++++++++++ drivers/acpi/{fan.c => fan_core.c} | 98 +++--------------------------- 4 files changed, 133 insertions(+), 89 deletions(-) create mode 100644 drivers/acpi/fan_attr.c rename drivers/acpi/{fan.c => fan_core.c} (80%) diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index bb757148e7ba..b5a8d3e00a52 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -81,6 +81,9 @@ obj-$(CONFIG_ACPI_AC) += ac.o obj-$(CONFIG_ACPI_BUTTON) += button.o obj-$(CONFIG_ACPI_TINY_POWER_BUTTON) += tiny-power-button.o obj-$(CONFIG_ACPI_FAN) += fan.o +fan-objs := fan_core.o +fan-objs += fan_attr.o + obj-$(CONFIG_ACPI_VIDEO) += video.o obj-$(CONFIG_ACPI_TAD) += acpi_tad.o obj-$(CONFIG_ACPI_PCI_SLOT) += pci_slot.o diff --git a/drivers/acpi/fan.h b/drivers/acpi/fan.h index dd9bb8ca2244..36c5e1a57094 100644 --- a/drivers/acpi/fan.h +++ b/drivers/acpi/fan.h @@ -6,9 +6,44 @@ * * Add new device IDs before the generic ACPI fan one. */ + +#ifndef _ACPI_FAN_H_ +#define _ACPI_FAN_H_ + #define ACPI_FAN_DEVICE_IDS \ {"INT3404", }, /* Fan */ \ {"INTC1044", }, /* Fan for Tiger Lake generation */ \ {"INTC1048", }, /* Fan for Alder Lake generation */ \ {"INTC10A2", }, /* Fan for Raptor Lake generation */ \ {"PNP0C0B", } /* Generic ACPI fan */ + +#define ACPI_FPS_NAME_LEN 20 + +struct acpi_fan_fps { + u64 control; + u64 trip_point; + u64 speed; + u64 noise_level; + u64 power; + char name[ACPI_FPS_NAME_LEN]; + struct device_attribute dev_attr; +}; + +struct acpi_fan_fif { + u64 revision; + u64 fine_grain_ctrl; + u64 step_size; + u64 low_speed_notification; +}; + +struct acpi_fan { + bool acpi4; + struct acpi_fan_fif fif; + struct acpi_fan_fps *fps; + int fps_count; + struct thermal_cooling_device *cdev; +}; + +int acpi_fan_create_attributes(struct acpi_device *device); +void acpi_fan_delete_attributes(struct acpi_device *device); +#endif diff --git a/drivers/acpi/fan_attr.c b/drivers/acpi/fan_attr.c new file mode 100644 index 000000000000..7b109022108b --- /dev/null +++ b/drivers/acpi/fan_attr.c @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * fan_attr.c - Create extra attributes for ACPI Fan driver + * + * Copyright (C) 2001, 2002 Andy Grover + * Copyright (C) 2001, 2002 Paul Diefenbaugh + * Copyright (C) 2022 Intel Corporation. All rights reserved. + */ + +#include +#include +#include +#include + +#include "fan.h" + +MODULE_LICENSE("GPL"); + +static ssize_t show_state(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct acpi_fan_fps *fps = container_of(attr, struct acpi_fan_fps, dev_attr); + int count; + + if (fps->control == 0xFFFFFFFF || fps->control > 100) + count = scnprintf(buf, PAGE_SIZE, "not-defined:"); + else + count = scnprintf(buf, PAGE_SIZE, "%lld:", fps->control); + + if (fps->trip_point == 0xFFFFFFFF || fps->trip_point > 9) + count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:"); + else + count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->trip_point); + + if (fps->speed == 0xFFFFFFFF) + count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:"); + else + count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->speed); + + if (fps->noise_level == 0xFFFFFFFF) + count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:"); + else + count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->noise_level * 100); + + if (fps->power == 0xFFFFFFFF) + count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined\n"); + else + count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld\n", fps->power); + + return count; +} + +int acpi_fan_create_attributes(struct acpi_device *device) +{ + struct acpi_fan *fan = acpi_driver_data(device); + int i, status = 0; + + for (i = 0; i < fan->fps_count; ++i) { + struct acpi_fan_fps *fps = &fan->fps[i]; + + snprintf(fps->name, ACPI_FPS_NAME_LEN, "state%d", i); + sysfs_attr_init(&fps->dev_attr.attr); + fps->dev_attr.show = show_state; + fps->dev_attr.store = NULL; + fps->dev_attr.attr.name = fps->name; + fps->dev_attr.attr.mode = 0444; + status = sysfs_create_file(&device->dev.kobj, &fps->dev_attr.attr); + if (status) { + int j; + + for (j = 0; j < i; ++j) + sysfs_remove_file(&device->dev.kobj, &fan->fps[j].dev_attr.attr); + break; + } + } + + return status; +} + +void acpi_fan_delete_attributes(struct acpi_device *device) +{ + struct acpi_fan *fan = acpi_driver_data(device); + int i; + + for (i = 0; i < fan->fps_count; ++i) + sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr); +} diff --git a/drivers/acpi/fan.c b/drivers/acpi/fan_core.c similarity index 80% rename from drivers/acpi/fan.c rename to drivers/acpi/fan_core.c index 098d64568d6d..9f8e68403fad 100644 --- a/drivers/acpi/fan.c +++ b/drivers/acpi/fan_core.c @@ -1,9 +1,10 @@ // SPDX-License-Identifier: GPL-2.0-or-later /* - * acpi_fan.c - ACPI Fan Driver ($Revision: 29 $) + * fan_core.c - ACPI Fan core Driver * * Copyright (C) 2001, 2002 Andy Grover * Copyright (C) 2001, 2002 Paul Diefenbaugh + * Copyright (C) 2022 Intel Corporation. All rights reserved. */ #include @@ -45,33 +46,6 @@ static const struct dev_pm_ops acpi_fan_pm = { #define FAN_PM_OPS_PTR NULL #endif -#define ACPI_FPS_NAME_LEN 20 - -struct acpi_fan_fps { - u64 control; - u64 trip_point; - u64 speed; - u64 noise_level; - u64 power; - char name[ACPI_FPS_NAME_LEN]; - struct device_attribute dev_attr; -}; - -struct acpi_fan_fif { - u64 revision; - u64 fine_grain_ctrl; - u64 step_size; - u64 low_speed_notification; -}; - -struct acpi_fan { - bool acpi4; - struct acpi_fan_fif fif; - struct acpi_fan_fps *fps; - int fps_count; - struct thermal_cooling_device *cdev; -}; - static struct platform_driver acpi_fan_driver = { .probe = acpi_fan_probe, .remove = acpi_fan_remove, @@ -270,39 +244,6 @@ static int acpi_fan_speed_cmp(const void *a, const void *b) return fps1->speed - fps2->speed; } -static ssize_t show_state(struct device *dev, struct device_attribute *attr, char *buf) -{ - struct acpi_fan_fps *fps = container_of(attr, struct acpi_fan_fps, dev_attr); - int count; - - if (fps->control == 0xFFFFFFFF || fps->control > 100) - count = scnprintf(buf, PAGE_SIZE, "not-defined:"); - else - count = scnprintf(buf, PAGE_SIZE, "%lld:", fps->control); - - if (fps->trip_point == 0xFFFFFFFF || fps->trip_point > 9) - count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:"); - else - count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->trip_point); - - if (fps->speed == 0xFFFFFFFF) - count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:"); - else - count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->speed); - - if (fps->noise_level == 0xFFFFFFFF) - count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined:"); - else - count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld:", fps->noise_level * 100); - - if (fps->power == 0xFFFFFFFF) - count += scnprintf(&buf[count], PAGE_SIZE - count, "not-defined\n"); - else - count += scnprintf(&buf[count], PAGE_SIZE - count, "%lld\n", fps->power); - - return count; -} - static int acpi_fan_get_fps(struct acpi_device *device) { struct acpi_fan *fan = acpi_driver_data(device); @@ -347,25 +288,6 @@ static int acpi_fan_get_fps(struct acpi_device *device) sort(fan->fps, fan->fps_count, sizeof(*fan->fps), acpi_fan_speed_cmp, NULL); - for (i = 0; i < fan->fps_count; ++i) { - struct acpi_fan_fps *fps = &fan->fps[i]; - - snprintf(fps->name, ACPI_FPS_NAME_LEN, "state%d", i); - sysfs_attr_init(&fps->dev_attr.attr); - fps->dev_attr.show = show_state; - fps->dev_attr.store = NULL; - fps->dev_attr.attr.name = fps->name; - fps->dev_attr.attr.mode = 0444; - status = sysfs_create_file(&device->dev.kobj, &fps->dev_attr.attr); - if (status) { - int j; - - for (j = 0; j < i; ++j) - sysfs_remove_file(&device->dev.kobj, &fan->fps[j].dev_attr.attr); - break; - } - } - err: kfree(obj); return status; @@ -396,6 +318,10 @@ static int acpi_fan_probe(struct platform_device *pdev) if (result) return result; + result = acpi_fan_create_attributes(device); + if (result) + return result; + fan->acpi4 = true; } else { result = acpi_device_update_power(device, NULL); @@ -437,12 +363,8 @@ static int acpi_fan_probe(struct platform_device *pdev) return 0; err_end: - if (fan->acpi4) { - int i; - - for (i = 0; i < fan->fps_count; ++i) - sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr); - } + if (fan->acpi4) + acpi_fan_delete_attributes(device); return result; } @@ -453,10 +375,8 @@ static int acpi_fan_remove(struct platform_device *pdev) if (fan->acpi4) { struct acpi_device *device = ACPI_COMPANION(&pdev->dev); - int i; - for (i = 0; i < fan->fps_count; ++i) - sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr); + acpi_fan_delete_attributes(device); } sysfs_remove_link(&pdev->dev.kobj, "thermal_cooling"); sysfs_remove_link(&fan->cdev->device.kobj, "device"); From d445571fa369cf08148dcd9bce563d5fae14fcd7 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 11 Feb 2022 08:09:29 -0800 Subject: [PATCH 165/294] ACPI: fan: Optimize struct acpi_fan_fif We don't need u64 to store the information about _FIF. There are two booleans (fine_grain_ctrl and low_speed_notification) and one field step_size which can take value from 1-9. There are no internal users of revision field. So convert all fields to u8, by not directly extracting the _FIF info the struct. Use an intermediate buffer to extract and assign. This will help to do u32 math using these fields. No functional changes are expected. Signed-off-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki --- drivers/acpi/fan.h | 8 ++++---- drivers/acpi/fan_core.c | 8 +++++++- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/fan.h b/drivers/acpi/fan.h index 36c5e1a57094..6cbb4b028da0 100644 --- a/drivers/acpi/fan.h +++ b/drivers/acpi/fan.h @@ -30,10 +30,10 @@ struct acpi_fan_fps { }; struct acpi_fan_fif { - u64 revision; - u64 fine_grain_ctrl; - u64 step_size; - u64 low_speed_notification; + u8 revision; + u8 fine_grain_ctrl; + u8 step_size; + u8 low_speed_notification; }; struct acpi_fan { diff --git a/drivers/acpi/fan_core.c b/drivers/acpi/fan_core.c index 9f8e68403fad..484cee0fb13e 100644 --- a/drivers/acpi/fan_core.c +++ b/drivers/acpi/fan_core.c @@ -211,7 +211,8 @@ static int acpi_fan_get_fif(struct acpi_device *device) struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_fan *fan = acpi_driver_data(device); struct acpi_buffer format = { sizeof("NNNN"), "NNNN" }; - struct acpi_buffer fif = { sizeof(fan->fif), &fan->fif }; + u64 fields[4]; + struct acpi_buffer fif = { sizeof(fields), fields }; union acpi_object *obj; acpi_status status; @@ -232,6 +233,11 @@ static int acpi_fan_get_fif(struct acpi_device *device) status = -EINVAL; } + fan->fif.revision = fields[0]; + fan->fif.fine_grain_ctrl = fields[1]; + fan->fif.step_size = fields[2]; + fan->fif.low_speed_notification = fields[3]; + err: kfree(obj); return status; From bea2d9868ef553e376480de3cd84a7a06fb03e41 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 11 Feb 2022 15:27:42 -0800 Subject: [PATCH 166/294] ACPI: fan: Properly handle fine grain control When _FIF object specifies support for fine grain control, then fan speed can be set from 0 to 100% with the recommended minimum "step size" via _FSL object. Here the control value doesn't need to match any value from _FPS object. Currently we have a simple solution implemented which just pick maximum control value from _FPS to display the actual state, but this is not optimal when there is a big window between two control values in _FPS. Also there is no way to set to any speed which doesn't match control values in _FPS. The system firmware can start the fan at speed which doesn't match any control value. To support fine grain control (when supported) via thermal sysfs: - cooling device max state is not _FPS state count but it will be 100 / _FIF.step_size Step size can be from 1 to 9. - cooling device current state is _FST.control / _FIF.step_size - cooling device set state will set the control value cdev.curr_state * _FIF.step_size plus any adjustment for 100%. By the spec, when control value do not sum to 100% because of _FIF.step_size, OSPM may select an appropriate ending Level increment to reach 100%. There is no rounding during calculation. For example if step size is 6: thermal sysfs cooling device max_state = 100/6 = 16 So user can set any value from 0-16. If the system boots with a _FST.control which is not multiples of step_size, the thermal sysfs cur_state will be based on the range. For example for step size = 6: _FST.control thermal sysfs cur_state ------------------------------------------------ 0-5 0 6-11 1 .. .. 90-95 15 96-100 16 While setting the _FST.control, the compensation will be at the last step for cur_state = 16, which will set the _FST.control to 100. Signed-off-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki --- drivers/acpi/fan.h | 6 +++ drivers/acpi/fan_core.c | 96 +++++++++++++++++++++++++++++------------ 2 files changed, 75 insertions(+), 27 deletions(-) diff --git a/drivers/acpi/fan.h b/drivers/acpi/fan.h index 6cbb4b028da0..4c01be2e3b77 100644 --- a/drivers/acpi/fan.h +++ b/drivers/acpi/fan.h @@ -36,6 +36,12 @@ struct acpi_fan_fif { u8 low_speed_notification; }; +struct acpi_fan_fst { + u64 revision; + u64 control; + u64 speed; +}; + struct acpi_fan { bool acpi4; struct acpi_fan_fif fif; diff --git a/drivers/acpi/fan_core.c b/drivers/acpi/fan_core.c index 484cee0fb13e..01616ec2e9ac 100644 --- a/drivers/acpi/fan_core.c +++ b/drivers/acpi/fan_core.c @@ -63,20 +63,24 @@ static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long struct acpi_device *device = cdev->devdata; struct acpi_fan *fan = acpi_driver_data(device); - if (fan->acpi4) - *state = fan->fps_count - 1; - else + if (fan->acpi4) { + if (fan->fif.fine_grain_ctrl) + *state = 100 / fan->fif.step_size; + else + *state = fan->fps_count - 1; + } else { *state = 1; + } + return 0; } -static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state) +static int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; - struct acpi_fan *fan = acpi_driver_data(device); union acpi_object *obj; acpi_status status; - int control, i; + int ret = 0; status = acpi_evaluate_object(device->handle, "_FST", NULL, &buffer); if (ACPI_FAILURE(status)) { @@ -89,35 +93,52 @@ static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state) obj->package.count != 3 || obj->package.elements[1].type != ACPI_TYPE_INTEGER) { dev_err(&device->dev, "Invalid _FST data\n"); - status = -EINVAL; + ret = -EINVAL; goto err; } - control = obj->package.elements[1].integer.value; - for (i = 0; i < fan->fps_count; i++) { - /* - * When Fine Grain Control is set, return the state - * corresponding to maximum fan->fps[i].control - * value compared to the current speed. Here the - * fan->fps[] is sorted array with increasing speed. - */ - if (fan->fif.fine_grain_ctrl && control < fan->fps[i].control) { - i = (i > 0) ? i - 1 : 0; - break; - } else if (control == fan->fps[i].control) { - break; + fst->revision = obj->package.elements[0].integer.value; + fst->control = obj->package.elements[1].integer.value; + fst->speed = obj->package.elements[2].integer.value; + +err: + kfree(obj); + return ret; +} + +static int fan_get_state_acpi4(struct acpi_device *device, unsigned long *state) +{ + struct acpi_fan *fan = acpi_driver_data(device); + struct acpi_fan_fst fst; + int status, i; + + status = acpi_fan_get_fst(device, &fst); + if (status) + return status; + + if (fan->fif.fine_grain_ctrl) { + /* This control should be same what we set using _FSL by spec */ + if (fst.control > 100) { + dev_dbg(&device->dev, "Invalid control value returned\n"); + goto match_fps; } + + *state = (int) fst.control / fan->fif.step_size; + return 0; + } + +match_fps: + for (i = 0; i < fan->fps_count; i++) { + if (fst.control == fan->fps[i].control) + break; } if (i == fan->fps_count) { dev_dbg(&device->dev, "Invalid control value returned\n"); - status = -EINVAL; - goto err; + return -EINVAL; } *state = i; -err: - kfree(obj); return status; } @@ -161,12 +182,27 @@ static int fan_set_state_acpi4(struct acpi_device *device, unsigned long state) { struct acpi_fan *fan = acpi_driver_data(device); acpi_status status; + u64 value = state; + int max_state; - if (state >= fan->fps_count) + if (fan->fif.fine_grain_ctrl) + max_state = 100 / fan->fif.step_size; + else + max_state = fan->fps_count - 1; + + if (state > max_state) return -EINVAL; - status = acpi_execute_simple_method(device->handle, "_FSL", - fan->fps[state].control); + if (fan->fif.fine_grain_ctrl) { + value *= fan->fif.step_size; + /* Spec allows compensate the last step only */ + if (value + fan->fif.step_size > 100) + value = 100; + } else { + value = fan->fps[state].control; + } + + status = acpi_execute_simple_method(device->handle, "_FSL", value); if (ACPI_FAILURE(status)) { dev_dbg(&device->dev, "Failed to set state by _FSL\n"); return -ENODEV; @@ -238,6 +274,12 @@ static int acpi_fan_get_fif(struct acpi_device *device) fan->fif.step_size = fields[2]; fan->fif.low_speed_notification = fields[3]; + /* If there is a bug in step size and set as 0, change to 1 */ + if (!fan->fif.step_size) + fan->fif.step_size = 1; + /* If step size > 9, change to 9 (by spec valid values 1-9) */ + else if (fan->fif.step_size > 9) + fan->fif.step_size = 9; err: kfree(obj); return status; From f1197343f07749035d74c08cf8b546c4f95614ab Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 11 Feb 2022 08:09:31 -0800 Subject: [PATCH 167/294] ACPI: fan: Add additional attributes for fine grain control Add additional attributes, which helps in implementing algorithm in the user space to optimize fan control. These attributes are presented in the same directory as the existing performance state attributes. Additional attributes: 1. Support of fine grain control Publish support of presence of fine grain control so that fan speed can be tuned correctly. This attribute is called "fine_grain_control". 2. fan speed Publish the actual fan rpm in sysfs. Knowing fan rpm is helpful to reduce noise level and use passive control instead. Also fan performance may not be same over time, so the same control value may not be enough to run the fan at a speed. So a feedback value of speed is helpful. This sysfs attribute is called "fan_speed_rpm". Signed-off-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki --- drivers/acpi/fan.h | 3 +++ drivers/acpi/fan_attr.c | 55 +++++++++++++++++++++++++++++++++++++++-- drivers/acpi/fan_core.c | 2 +- 3 files changed, 57 insertions(+), 3 deletions(-) diff --git a/drivers/acpi/fan.h b/drivers/acpi/fan.h index 4c01be2e3b77..44728529a5b6 100644 --- a/drivers/acpi/fan.h +++ b/drivers/acpi/fan.h @@ -48,8 +48,11 @@ struct acpi_fan { struct acpi_fan_fps *fps; int fps_count; struct thermal_cooling_device *cdev; + struct device_attribute fst_speed; + struct device_attribute fine_grain_control; }; +int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst); int acpi_fan_create_attributes(struct acpi_device *device); void acpi_fan_delete_attributes(struct acpi_device *device); #endif diff --git a/drivers/acpi/fan_attr.c b/drivers/acpi/fan_attr.c index 7b109022108b..f15157d40713 100644 --- a/drivers/acpi/fan_attr.c +++ b/drivers/acpi/fan_attr.c @@ -49,10 +49,50 @@ static ssize_t show_state(struct device *dev, struct device_attribute *attr, cha return count; } +static ssize_t show_fan_speed(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct acpi_device *acpi_dev = container_of(dev, struct acpi_device, dev); + struct acpi_fan_fst fst; + int status; + + status = acpi_fan_get_fst(acpi_dev, &fst); + if (status) + return status; + + return sprintf(buf, "%lld\n", fst.speed); +} + +static ssize_t show_fine_grain_control(struct device *dev, struct device_attribute *attr, char *buf) +{ + struct acpi_device *acpi_dev = container_of(dev, struct acpi_device, dev); + struct acpi_fan *fan = acpi_driver_data(acpi_dev); + + return sprintf(buf, "%d\n", fan->fif.fine_grain_ctrl); +} + int acpi_fan_create_attributes(struct acpi_device *device) { struct acpi_fan *fan = acpi_driver_data(device); - int i, status = 0; + int i, status; + + sysfs_attr_init(&fan->fine_grain_control.attr); + fan->fine_grain_control.show = show_fine_grain_control; + fan->fine_grain_control.store = NULL; + fan->fine_grain_control.attr.name = "fine_grain_control"; + fan->fine_grain_control.attr.mode = 0444; + status = sysfs_create_file(&device->dev.kobj, &fan->fine_grain_control.attr); + if (status) + return status; + + /* _FST is present if we are here */ + sysfs_attr_init(&fan->fst_speed.attr); + fan->fst_speed.show = show_fan_speed; + fan->fst_speed.store = NULL; + fan->fst_speed.attr.name = "fan_speed_rpm"; + fan->fst_speed.attr.mode = 0444; + status = sysfs_create_file(&device->dev.kobj, &fan->fst_speed.attr); + if (status) + goto rem_fine_grain_attr; for (i = 0; i < fan->fps_count; ++i) { struct acpi_fan_fps *fps = &fan->fps[i]; @@ -69,10 +109,18 @@ int acpi_fan_create_attributes(struct acpi_device *device) for (j = 0; j < i; ++j) sysfs_remove_file(&device->dev.kobj, &fan->fps[j].dev_attr.attr); - break; + goto rem_fst_attr; } } + return 0; + +rem_fst_attr: + sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr); + +rem_fine_grain_attr: + sysfs_remove_file(&device->dev.kobj, &fan->fine_grain_control.attr); + return status; } @@ -83,4 +131,7 @@ void acpi_fan_delete_attributes(struct acpi_device *device) for (i = 0; i < fan->fps_count; ++i) sysfs_remove_file(&device->dev.kobj, &fan->fps[i].dev_attr.attr); + + sysfs_remove_file(&device->dev.kobj, &fan->fst_speed.attr); + sysfs_remove_file(&device->dev.kobj, &fan->fine_grain_control.attr); } diff --git a/drivers/acpi/fan_core.c b/drivers/acpi/fan_core.c index 01616ec2e9ac..b9a9a59ddcc1 100644 --- a/drivers/acpi/fan_core.c +++ b/drivers/acpi/fan_core.c @@ -75,7 +75,7 @@ static int fan_get_max_state(struct thermal_cooling_device *cdev, unsigned long return 0; } -static int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst) +int acpi_fan_get_fst(struct acpi_device *device, struct acpi_fan_fst *fst) { struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; union acpi_object *obj; From 0750b8fcf313845b21c71344b4bea8ad7d3cee84 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Fri, 11 Feb 2022 08:09:32 -0800 Subject: [PATCH 168/294] Documentation/admin-guide/acpi: Add documentation for fine grain control Add documentation for the newly added attributes: fine_grain_control fan_speed_rpm Signed-off-by: Srinivas Pandruvada Signed-off-by: Rafael J. Wysocki --- .../acpi/fan_performance_states.rst | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Documentation/admin-guide/acpi/fan_performance_states.rst b/Documentation/admin-guide/acpi/fan_performance_states.rst index 98fe5c333121..b9e4b4d146c1 100644 --- a/Documentation/admin-guide/acpi/fan_performance_states.rst +++ b/Documentation/admin-guide/acpi/fan_performance_states.rst @@ -60,3 +60,31 @@ For example:: When a given field is not populated or its value provided by the platform firmware is invalid, the "not-defined" string is shown instead of the value. + +ACPI Fan Fine Grain Control +============================= + +When _FIF object specifies support for fine grain control, then fan speed +can be set from 0 to 100% with the recommended minimum "step size" via +_FSL object. User can adjust fan speed using thermal sysfs cooling device. + +Here use can look at fan performance states for a reference speed (speed_rpm) +and set it by changing cooling device cur_state. If the fine grain control +is supported then user can also adjust to some other speeds which are +not defined in the performance states. + +The support of fine grain control is presented via sysfs attribute +"fine_grain_control". If fine grain control is present, this attribute +will show "1" otherwise "0". + +This sysfs attribute is presented in the same directory as performance states. + +ACPI Fan Performance Feedback +============================= + +The optional _FST object provides status information for the fan device. +This includes field to provide current fan speed in revolutions per minute +at which the fan is rotating. + +This speed is presented in the sysfs using the attribute "fan_speed_rpm", +in the same directory as performance states. From 7dacee0b9efc8bd061f097b1a8d4daa6591af0c6 Mon Sep 17 00:00:00 2001 From: Maximilian Luz Date: Sun, 13 Feb 2022 16:49:20 +0100 Subject: [PATCH 169/294] ACPI: battery: Add device HID and quirk for Microsoft Surface Go 3 For some reason, the Microsoft Surface Go 3 uses the standard ACPI interface for battery information, but does not use the standard PNP0C0A HID. Instead it uses MSHW0146 as identifier. Add that ID to the driver as this seems to work well. Additionally, the power state is not updated immediately after the AC has been (un-)plugged, so add the respective quirk for that. Signed-off-by: Maximilian Luz Cc: All applicable Signed-off-by: Rafael J. Wysocki --- drivers/acpi/battery.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/acpi/battery.c b/drivers/acpi/battery.c index ea31ae01458b..dc208f5f5a1f 100644 --- a/drivers/acpi/battery.c +++ b/drivers/acpi/battery.c @@ -59,6 +59,10 @@ MODULE_PARM_DESC(cache_time, "cache time in milliseconds"); static const struct acpi_device_id battery_device_ids[] = { {"PNP0C0A", 0}, + + /* Microsoft Surface Go 3 */ + {"MSHW0146", 0}, + {"", 0}, }; @@ -1148,6 +1152,14 @@ static const struct dmi_system_id bat_dmi_table[] __initconst = { DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad"), }, }, + { + /* Microsoft Surface Go 3 */ + .callback = battery_notification_delay_quirk, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "Microsoft Corporation"), + DMI_MATCH(DMI_PRODUCT_NAME, "Surface Go 3"), + }, + }, {}, }; From e7d90cfac5510f8c94baa18f9f3f7808859c8332 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 17 Feb 2022 13:49:50 +0100 Subject: [PATCH 170/294] PM: domains: Prevent power off for parent unless child is in deepest state A PM domain managed by genpd may support multiple idlestates (power-off states). During genpd_power_off() a genpd governor may be asked to select one of the idlestates based upon the dev PM QoS constraints, for example. However, there is a problem with the behaviour around this in genpd. More precisely, a parent-domain is allowed to be powered off, no matter of what idlestate that has been selected for the child-domain. For the stm32mp1 platform from STMicro, this behaviour doesn't play well. Instead, the parent-domain must not be powered off, unless the deepest idlestate has been selected for the child-domain. As the current behaviour in genpd is quite questionable anyway, let's simply change it into what is needed by the stm32mp1 platform. If it surprisingly turns out that other platforms may need a different behaviour from genpd, then we will have to revisit this to find a way to make it configurable. Signed-off-by: Ulf Hansson Reviewed-by: Dmitry Osipenko Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index 5db704f02e71..c87588c21700 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -636,6 +636,18 @@ static int genpd_power_off(struct generic_pm_domain *genpd, bool one_dev_on, atomic_read(&genpd->sd_count) > 0) return -EBUSY; + /* + * The children must be in their deepest (powered-off) states to allow + * the parent to be powered off. Note that, there's no need for + * additional locking, as powering on a child, requires the parent's + * lock to be acquired first. + */ + list_for_each_entry(link, &genpd->parent_links, parent_node) { + struct generic_pm_domain *child = link->child; + if (child->state_idx < child->state_count - 1) + return -EBUSY; + } + list_for_each_entry(pdd, &genpd->dev_list, list_node) { enum pm_qos_flags_status stat; @@ -1073,6 +1085,13 @@ static void genpd_sync_power_off(struct generic_pm_domain *genpd, bool use_lock, || atomic_read(&genpd->sd_count) > 0) return; + /* Check that the children are in their deepest (powered-off) state. */ + list_for_each_entry(link, &genpd->parent_links, parent_node) { + struct generic_pm_domain *child = link->child; + if (child->state_idx < child->state_count - 1) + return; + } + /* Choose the deepest state when suspending */ genpd->state_idx = genpd->state_count - 1; if (_genpd_power_off(genpd, false)) From 9a6582b839281ee0e874621f1a2139d2aeb9489e Mon Sep 17 00:00:00 2001 From: Ahmad Fatoum Date: Wed, 23 Feb 2022 09:03:23 +0100 Subject: [PATCH 171/294] PM: domains: use dev_err_probe() to simplify error handling dev_err_probe() can reduce code size, makes the code easier to read and has the added benefit of recording the defer reason for later read out. Use it where appropriate. This also fixes an issue, where an error message in __genpd_dev_pm_attach was not terminated by a line break. Signed-off-by: Ahmad Fatoum Signed-off-by: Sascha Hauer Acked-by: Ulf Hansson Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index c87588c21700..c0d9ad01b32c 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2267,12 +2267,8 @@ int of_genpd_add_provider_simple(struct device_node *np, /* Parse genpd OPP table */ if (genpd->set_performance_state) { ret = dev_pm_opp_of_add_table(&genpd->dev); - if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&genpd->dev, "Failed to add OPP table: %d\n", - ret); - return ret; - } + if (ret) + return dev_err_probe(&genpd->dev, ret, "Failed to add OPP table\n"); /* * Save table for faster processing while setting performance @@ -2331,9 +2327,8 @@ int of_genpd_add_provider_onecell(struct device_node *np, if (genpd->set_performance_state) { ret = dev_pm_opp_of_add_table_indexed(&genpd->dev, i); if (ret) { - if (ret != -EPROBE_DEFER) - dev_err(&genpd->dev, "Failed to add OPP table for index %d: %d\n", - i, ret); + dev_err_probe(&genpd->dev, ret, + "Failed to add OPP table for index %d\n", i); goto error; } @@ -2691,12 +2686,8 @@ static int __genpd_dev_pm_attach(struct device *dev, struct device *base_dev, ret = genpd_add_device(pd, dev, base_dev); mutex_unlock(&gpd_list_lock); - if (ret < 0) { - if (ret != -EPROBE_DEFER) - dev_err(dev, "failed to add to PM domain %s: %d", - pd->name, ret); - return ret; - } + if (ret < 0) + return dev_err_probe(dev, ret, "failed to add to PM domain %s\n", pd->name); dev->pm_domain->detach = genpd_dev_pm_detach; dev->pm_domain->sync = genpd_dev_pm_sync; From f6bfe8b5b2c2a5ac8bd2fc7bca3706e6c3fc26d8 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Fri, 25 Feb 2022 14:48:15 +0800 Subject: [PATCH 172/294] PM: domains: Fix sleep-in-atomic bug caused by genpd_debug_remove() When a genpd with GENPD_FLAG_IRQ_SAFE gets removed, the following sleep-in-atomic bug will be seen, as genpd_debug_remove() will be called with a spinlock being held. [ 0.029183] BUG: sleeping function called from invalid context at kernel/locking/rwsem.c:1460 [ 0.029204] in_atomic(): 1, irqs_disabled(): 128, non_block: 0, pid: 1, name: swapper/0 [ 0.029219] preempt_count: 1, expected: 0 [ 0.029230] CPU: 1 PID: 1 Comm: swapper/0 Not tainted 5.17.0-rc4+ #489 [ 0.029245] Hardware name: Thundercomm TurboX CM2290 (DT) [ 0.029256] Call trace: [ 0.029265] dump_backtrace.part.0+0xbc/0xd0 [ 0.029285] show_stack+0x3c/0xa0 [ 0.029298] dump_stack_lvl+0x7c/0xa0 [ 0.029311] dump_stack+0x18/0x34 [ 0.029323] __might_resched+0x10c/0x13c [ 0.029338] __might_sleep+0x4c/0x80 [ 0.029351] down_read+0x24/0xd0 [ 0.029363] lookup_one_len_unlocked+0x9c/0xcc [ 0.029379] lookup_positive_unlocked+0x10/0x50 [ 0.029392] debugfs_lookup+0x68/0xac [ 0.029406] genpd_remove.part.0+0x12c/0x1b4 [ 0.029419] of_genpd_remove_last+0xa8/0xd4 [ 0.029434] psci_cpuidle_domain_probe+0x174/0x53c [ 0.029449] platform_probe+0x68/0xe0 [ 0.029462] really_probe+0x190/0x430 [ 0.029473] __driver_probe_device+0x90/0x18c [ 0.029485] driver_probe_device+0x40/0xe0 [ 0.029497] __driver_attach+0xf4/0x1d0 [ 0.029508] bus_for_each_dev+0x70/0xd0 [ 0.029523] driver_attach+0x24/0x30 [ 0.029534] bus_add_driver+0x164/0x22c [ 0.029545] driver_register+0x78/0x130 [ 0.029556] __platform_driver_register+0x28/0x34 [ 0.029569] psci_idle_init_domains+0x1c/0x28 [ 0.029583] do_one_initcall+0x50/0x1b0 [ 0.029595] kernel_init_freeable+0x214/0x280 [ 0.029609] kernel_init+0x2c/0x13c [ 0.029622] ret_from_fork+0x10/0x20 It doesn't seem necessary to call genpd_debug_remove() with the lock, so move it out from locking to fix the problem. Fixes: 718072ceb211 ("PM: domains: create debugfs nodes when adding power domains") Signed-off-by: Shawn Guo Reviewed-by: Ulf Hansson Cc: 5.11+ # 5.11+ Signed-off-by: Rafael J. Wysocki --- drivers/base/power/domain.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/base/power/domain.c b/drivers/base/power/domain.c index c0d9ad01b32c..1ee878d126fd 100644 --- a/drivers/base/power/domain.c +++ b/drivers/base/power/domain.c @@ -2077,9 +2077,9 @@ static int genpd_remove(struct generic_pm_domain *genpd) kfree(link); } - genpd_debug_remove(genpd); list_del(&genpd->gpd_list_node); genpd_unlock(genpd); + genpd_debug_remove(genpd); cancel_work_sync(&genpd->power_off_work); if (genpd_is_cpu_domain(genpd)) free_cpumask_var(genpd->cpus); From 7dfe105dfc724c82ed3d79a4c47439c516a2410b Mon Sep 17 00:00:00 2001 From: Tom Rix Date: Fri, 11 Feb 2022 08:10:27 -0800 Subject: [PATCH 173/294] PM: sleep: wakeup: Fix typos in comments Remove the second 'the'. Replace the second 'of' with 'the'. Replace 'couter' with 'counter'. Signed-off-by: Tom Rix Acked-by: Randy Dunlap Acked-by: Pavel Machek Signed-off-by: Rafael J. Wysocki --- drivers/base/power/wakeirq.c | 2 +- drivers/base/power/wakeup.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/base/power/wakeirq.c b/drivers/base/power/wakeirq.c index 0004db4a9d3b..d487a6bac630 100644 --- a/drivers/base/power/wakeirq.c +++ b/drivers/base/power/wakeirq.c @@ -289,7 +289,7 @@ EXPORT_SYMBOL_GPL(dev_pm_disable_wake_irq); * * Enables wakeirq conditionally. We need to enable wake-up interrupt * lazily on the first rpm_suspend(). This is needed as the consumer device - * starts in RPM_SUSPENDED state, and the the first pm_runtime_get() would + * starts in RPM_SUSPENDED state, and the first pm_runtime_get() would * otherwise try to disable already disabled wakeirq. The wake-up interrupt * starts disabled with IRQ_NOAUTOEN set. * diff --git a/drivers/base/power/wakeup.c b/drivers/base/power/wakeup.c index 8666590201c9..a57d469676ca 100644 --- a/drivers/base/power/wakeup.c +++ b/drivers/base/power/wakeup.c @@ -587,7 +587,7 @@ static bool wakeup_source_not_registered(struct wakeup_source *ws) * @ws: Wakeup source to handle. * * Update the @ws' statistics and, if @ws has just been activated, notify the PM - * core of the event by incrementing the counter of of wakeup events being + * core of the event by incrementing the counter of the wakeup events being * processed. */ static void wakeup_source_activate(struct wakeup_source *ws) @@ -733,7 +733,7 @@ static void wakeup_source_deactivate(struct wakeup_source *ws) /* * Increment the counter of registered wakeup events and decrement the - * couter of wakeup events in progress simultaneously. + * counter of wakeup events in progress simultaneously. */ cec = atomic_add_return(MAX_IN_PROGRESS, &combined_event_count); trace_wakeup_source_deactivate(ws->name, cec); From 444e1154b2bf0b881b65ba1bba5bc8e691fac04a Mon Sep 17 00:00:00 2001 From: Jiapeng Chong Date: Tue, 15 Feb 2022 11:37:50 +0800 Subject: [PATCH 174/294] PM: hibernate: Clean up non-kernel-doc comments Address the following W=1 kernel build warning: kernel/power/swap.c:120: warning: This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst. Reported-by: Abaci Robot Signed-off-by: Jiapeng Chong Signed-off-by: Rafael J. Wysocki --- kernel/power/swap.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/kernel/power/swap.c b/kernel/power/swap.c index ad10359030a4..c51f5507b34f 100644 --- a/kernel/power/swap.c +++ b/kernel/power/swap.c @@ -89,7 +89,7 @@ struct swap_map_page_list { struct swap_map_page_list *next; }; -/** +/* * The swap_map_handle structure is used for handling swap in * a file-alike way */ @@ -117,7 +117,7 @@ struct swsusp_header { static struct swsusp_header *swsusp_header; -/** +/* * The following functions are used for tracing the allocated * swap pages, so that they can be freed in case of an error. */ @@ -171,7 +171,7 @@ static int swsusp_extents_insert(unsigned long swap_offset) return 0; } -/** +/* * alloc_swapdev_block - allocate a swap page and register that it has * been allocated, so that it can be freed in case of an error. */ @@ -190,7 +190,7 @@ sector_t alloc_swapdev_block(int swap) return 0; } -/** +/* * free_all_swap_pages - free swap pages allocated for saving image data. * It also frees the extents used to register which swap entries had been * allocated. From a644161ba11df38e5582e718c99668e282ddbf36 Mon Sep 17 00:00:00 2001 From: Srinivas Pandruvada Date: Mon, 28 Feb 2022 11:57:59 -0800 Subject: [PATCH 175/294] Documentation: admin-guide: pm: Document uncore frequency scaling Added documentation to configure uncore frequency limits in Intel Xeon processors. Signed-off-by: Srinivas Pandruvada [ rjw: Clean up the document wording ] Signed-off-by: Rafael J. Wysocki --- .../pm/intel_uncore_frequency_scaling.rst | 60 +++++++++++++++++++ .../admin-guide/pm/working-state.rst | 1 + 2 files changed, 61 insertions(+) create mode 100644 Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst diff --git a/Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst b/Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst new file mode 100644 index 000000000000..09169d935835 --- /dev/null +++ b/Documentation/admin-guide/pm/intel_uncore_frequency_scaling.rst @@ -0,0 +1,60 @@ +.. SPDX-License-Identifier: GPL-2.0 +.. include:: + +============================== +Intel Uncore Frequency Scaling +============================== + +:Copyright: |copy| 2022 Intel Corporation + +:Author: Srinivas Pandruvada + +Introduction +------------ + +The uncore can consume significant amount of power in Intel's Xeon servers based +on the workload characteristics. To optimize the total power and improve overall +performance, SoCs have internal algorithms for scaling uncore frequency. These +algorithms monitor workload usage of uncore and set a desirable frequency. + +It is possible that users have different expectations of uncore performance and +want to have control over it. The objective is similar to allowing users to set +the scaling min/max frequencies via cpufreq sysfs to improve CPU performance. +Users may have some latency sensitive workloads where they do not want any +change to uncore frequency. Also, users may have workloads which require +different core and uncore performance at distinct phases and they may want to +use both cpufreq and the uncore scaling interface to distribute power and +improve overall performance. + +Sysfs Interface +--------------- + +To control uncore frequency, a sysfs interface is provided in the directory: +`/sys/devices/system/cpu/intel_uncore_frequency/`. + +There is one directory for each package and die combination as the scope of +uncore scaling control is per die in multiple die/package SoCs or per +package for single die per package SoCs. The name represents the +scope of control. For example: 'package_00_die_00' is for package id 0 and +die 0. + +Each package_*_die_* contains the following attributes: + +``initial_max_freq_khz`` + Out of reset, this attribute represent the maximum possible frequency. + This is a read-only attribute. If users adjust max_freq_khz, + they can always go back to maximum using the value from this attribute. + +``initial_min_freq_khz`` + Out of reset, this attribute represent the minimum possible frequency. + This is a read-only attribute. If users adjust min_freq_khz, + they can always go back to minimum using the value from this attribute. + +``max_freq_khz`` + This attribute is used to set the maximum uncore frequency. + +``min_freq_khz`` + This attribute is used to set the minimum uncore frequency. + +``current_freq_khz`` + This attribute is used to get the current uncore frequency. diff --git a/Documentation/admin-guide/pm/working-state.rst b/Documentation/admin-guide/pm/working-state.rst index 5d2757e2de65..ee45887811ff 100644 --- a/Documentation/admin-guide/pm/working-state.rst +++ b/Documentation/admin-guide/pm/working-state.rst @@ -15,3 +15,4 @@ Working-State Power Management cpufreq_drivers intel_epb intel-speed-select + intel_uncore_frequency_scaling From ba7ffcd4c4da374b0f64666354eeeda7d3827131 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 28 Feb 2022 14:05:32 -0800 Subject: [PATCH 176/294] PM: hibernate: fix __setup handler error handling If an invalid value is used in "resumedelay=", it is silently ignored. Add a warning message and then let the __setup handler return 1 to indicate that the kernel command line option has been handled. Fixes: 317cf7e5e85e3 ("PM / hibernate: convert simple_strtoul to kstrtoul") Signed-off-by: Randy Dunlap Reported-by: Igor Zhbanov Link: lore.kernel.org/r/64644a2f-4a20-bab3-1e15-3b2cdd0defe3@omprussia.ru Signed-off-by: Rafael J. Wysocki --- kernel/power/hibernate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 49d1df0218cb..0ac805b753e5 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -1330,7 +1330,7 @@ static int __init resumedelay_setup(char *str) int rc = kstrtouint(str, 0, &resume_delay); if (rc) - return rc; + pr_warn("resumedelay: bad option string '%s'\n", str); return 1; } From 7a64ca17e4dd50d5f910769167f3553902777844 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 28 Feb 2022 14:05:44 -0800 Subject: [PATCH 177/294] PM: suspend: fix return value of __setup handler If an invalid option is given for "test_suspend=