mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/
synced 2025-04-19 20:58:31 +09:00
- The 7 patch series "powerpc/crash: use generic crashkernel
reservation" from Sourabh Jain changes powerpc's kexec code to use more of the generic layers. - The 2 patch series "get_maintainer: report subsystem status separately" from Vlastimil Babka makes some long-requested improvements to the get_maintainer output. - The 4 patch series "ucount: Simplify refcounting with rcuref_t" from Sebastian Siewior cleans up and optimizing the refcounting in the ucount code. - The 12 patch series "reboot: support runtime configuration of emergency hw_protection action" from Ahmad Fatoum improves the ability for a driver to perform an emergency system shutdown or reboot. - The 16 patch series "Converge on using secs_to_jiffies() part two" from Easwar Hariharan performs further migrations from msecs_to_jiffies() to secs_to_jiffies(). - The 7 patch series "lib/interval_tree: add some test cases and cleanup" from Wei Yang permits more userspace testing of kernel library code, adds some more tests and performs some cleanups. - The 2 patch series "hung_task: Dump the blocking task stacktrace" from Masami Hiramatsu arranges for the hung_task detector to dump the stack of the blocking task and not just that of the blocked task. - The 4 patch series "resource: Split and use DEFINE_RES*() macros" from Andy Shevchenko provides some cleanups to the resource definition macros. - Plus the usual shower of singleton patches - please see the individual changelogs for details. -----BEGIN PGP SIGNATURE----- iHUEABYKAB0WIQTTMBEPP41GrTpTJgfdBJ7gKXxAjgUCZ+nuqwAKCRDdBJ7gKXxA jtNqAQDxqJpjWkzn4yN9CNSs1ivVx3fr6SqazlYCrt3u89WQvwEA1oRrGpETzUGq r6khQUIcQImPPcjFqEFpuiSOU0MBZA0= =Kii8 -----END PGP SIGNATURE----- Merge tag 'mm-nonmm-stable-2025-03-30-18-23' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm Pull non-MM updates from Andrew Morton: - The series "powerpc/crash: use generic crashkernel reservation" from Sourabh Jain changes powerpc's kexec code to use more of the generic layers. - The series "get_maintainer: report subsystem status separately" from Vlastimil Babka makes some long-requested improvements to the get_maintainer output. - The series "ucount: Simplify refcounting with rcuref_t" from Sebastian Siewior cleans up and optimizing the refcounting in the ucount code. - The series "reboot: support runtime configuration of emergency hw_protection action" from Ahmad Fatoum improves the ability for a driver to perform an emergency system shutdown or reboot. - The series "Converge on using secs_to_jiffies() part two" from Easwar Hariharan performs further migrations from msecs_to_jiffies() to secs_to_jiffies(). - The series "lib/interval_tree: add some test cases and cleanup" from Wei Yang permits more userspace testing of kernel library code, adds some more tests and performs some cleanups. - The series "hung_task: Dump the blocking task stacktrace" from Masami Hiramatsu arranges for the hung_task detector to dump the stack of the blocking task and not just that of the blocked task. - The series "resource: Split and use DEFINE_RES*() macros" from Andy Shevchenko provides some cleanups to the resource definition macros. - Plus the usual shower of singleton patches - please see the individual changelogs for details. * tag 'mm-nonmm-stable-2025-03-30-18-23' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: (77 commits) mailmap: consolidate email addresses of Alexander Sverdlin fs/procfs: fix the comment above proc_pid_wchan() relay: use kasprintf() instead of fixed buffer formatting resource: replace open coded variant of DEFINE_RES() resource: replace open coded variants of DEFINE_RES_*_NAMED() resource: replace open coded variant of DEFINE_RES_NAMED_DESC() resource: split DEFINE_RES_NAMED_DESC() out of DEFINE_RES_NAMED() samples: add hung_task detector mutex blocking sample hung_task: show the blocker task if the task is hung on mutex kexec_core: accept unaccepted kexec segments' destination addresses watchdog/perf: optimize bytes copied and remove manual NUL-termination lib/interval_tree: fix the comment of interval_tree_span_iter_next_gap() lib/interval_tree: skip the check before go to the right subtree lib/interval_tree: add test case for span iteration lib/interval_tree: add test case for interval_tree_iter_xxx() helpers lib/rbtree: add random seed lib/rbtree: split tests lib/rbtree: enable userland test suite for rbtree related data structure checkpatch: describe --min-conf-desc-length scripts/gdb/symbols: determine KASLR offset on s390 ...
This commit is contained in:
commit
d6b02199cd
11
.mailmap
11
.mailmap
@ -31,6 +31,13 @@ Alexander Lobakin <alobakin@pm.me> <alobakin@marvell.com>
|
||||
Alexander Lobakin <alobakin@pm.me> <bloodyreaper@yandex.ru>
|
||||
Alexander Mikhalitsyn <alexander@mihalicyn.com> <alexander.mikhalitsyn@virtuozzo.com>
|
||||
Alexander Mikhalitsyn <alexander@mihalicyn.com> <aleksandr.mikhalitsyn@canonical.com>
|
||||
Alexander Sverdlin <alexander.sverdlin@gmail.com> <alexander.sverdlin.ext@nsn.com>
|
||||
Alexander Sverdlin <alexander.sverdlin@gmail.com> <alexander.sverdlin@gmx.de>
|
||||
Alexander Sverdlin <alexander.sverdlin@gmail.com> <alexander.sverdlin@nokia.com>
|
||||
Alexander Sverdlin <alexander.sverdlin@gmail.com> <alexander.sverdlin@nsn.com>
|
||||
Alexander Sverdlin <alexander.sverdlin@gmail.com> <alexander.sverdlin@siemens.com>
|
||||
Alexander Sverdlin <alexander.sverdlin@gmail.com> <alexander.sverdlin@sysgo.com>
|
||||
Alexander Sverdlin <alexander.sverdlin@gmail.com> <subaparts@yandex.ru>
|
||||
Alexandre Belloni <alexandre.belloni@bootlin.com> <alexandre.belloni@free-electrons.com>
|
||||
Alexandre Ghiti <alex@ghiti.fr> <alexandre.ghiti@canonical.com>
|
||||
Alexei Avshalom Lazar <quic_ailizaro@quicinc.com> <ailizaro@codeaurora.org>
|
||||
@ -153,7 +160,6 @@ Carlos Bilbao <carlos.bilbao@kernel.org> <carlos.bilbao@amd.com>
|
||||
Carlos Bilbao <carlos.bilbao@kernel.org> <carlos.bilbao.osdev@gmail.com>
|
||||
Carlos Bilbao <carlos.bilbao@kernel.org> <bilbao@vt.edu>
|
||||
Changbin Du <changbin.du@intel.com> <changbin.du@gmail.com>
|
||||
Changbin Du <changbin.du@intel.com> <changbin.du@intel.com>
|
||||
Chao Yu <chao@kernel.org> <chao2.yu@samsung.com>
|
||||
Chao Yu <chao@kernel.org> <yuchao0@huawei.com>
|
||||
Chester Lin <chester62515@gmail.com> <clin@suse.com>
|
||||
@ -271,6 +277,7 @@ Hamza Mahfooz <hamzamahfooz@linux.microsoft.com> <hamza.mahfooz@amd.com>
|
||||
Hanjun Guo <guohanjun@huawei.com> <hanjun.guo@linaro.org>
|
||||
Hans Verkuil <hverkuil@xs4all.nl> <hansverk@cisco.com>
|
||||
Hans Verkuil <hverkuil@xs4all.nl> <hverkuil-cisco@xs4all.nl>
|
||||
Harry Yoo <harry.yoo@oracle.com> <42.hyeyoo@gmail.com>
|
||||
Heiko Carstens <hca@linux.ibm.com> <h.carstens@de.ibm.com>
|
||||
Heiko Carstens <hca@linux.ibm.com> <heiko.carstens@de.ibm.com>
|
||||
Heiko Stuebner <heiko@sntech.de> <heiko.stuebner@bqreaders.com>
|
||||
@ -305,7 +312,6 @@ Jan Glauber <jan.glauber@gmail.com> <jglauber@cavium.com>
|
||||
Jan Kuliga <jtkuliga.kdev@gmail.com> <jankul@alatek.krakow.pl>
|
||||
Jarkko Sakkinen <jarkko@kernel.org> <jarkko.sakkinen@linux.intel.com>
|
||||
Jarkko Sakkinen <jarkko@kernel.org> <jarkko@profian.com>
|
||||
Jarkko Sakkinen <jarkko@kernel.org> <jarkko.sakkinen@parity.io>
|
||||
Jason Gunthorpe <jgg@ziepe.ca> <jgg@mellanox.com>
|
||||
Jason Gunthorpe <jgg@ziepe.ca> <jgg@nvidia.com>
|
||||
Jason Gunthorpe <jgg@ziepe.ca> <jgunthorpe@obsidianresearch.com>
|
||||
@ -762,7 +768,6 @@ Vinod Koul <vkoul@kernel.org> <vkoul@infradead.org>
|
||||
Viresh Kumar <vireshk@kernel.org> <viresh.kumar2@arm.com>
|
||||
Viresh Kumar <vireshk@kernel.org> <viresh.kumar@st.com>
|
||||
Viresh Kumar <vireshk@kernel.org> <viresh.linux@gmail.com>
|
||||
Viresh Kumar <viresh.kumar@linaro.org> <viresh.kumar@linaro.org>
|
||||
Viresh Kumar <viresh.kumar@linaro.org> <viresh.kumar@linaro.com>
|
||||
Vishnu Dasa <vishnu.dasa@broadcom.com> <vdasa@vmware.com>
|
||||
Vivek Aknurwar <quic_viveka@quicinc.com> <viveka@codeaurora.org>
|
||||
|
@ -30,3 +30,11 @@ KernelVersion: 5.11
|
||||
Contact: Matteo Croce <mcroce@microsoft.com>
|
||||
Description: Don't wait for any other CPUs on reboot and
|
||||
avoid anything that could hang.
|
||||
|
||||
What: /sys/kernel/reboot/hw_protection
|
||||
Date: April 2025
|
||||
KernelVersion: 6.15
|
||||
Contact: Ahmad Fatoum <a.fatoum@pengutronix.de>
|
||||
Description: Hardware protection action taken on critical events like
|
||||
overtemperature or imminent voltage loss.
|
||||
Valid values are: reboot shutdown
|
||||
|
@ -1954,6 +1954,12 @@
|
||||
which allow the hypervisor to 'idle' the guest
|
||||
on lock contention.
|
||||
|
||||
hw_protection= [HW]
|
||||
Format: reboot | shutdown
|
||||
|
||||
Hardware protection action taken on critical events like
|
||||
overtemperature or imminent voltage loss.
|
||||
|
||||
i2c_bus= [HW] Override the default board specific I2C bus speed
|
||||
or register an additional I2C bus that is not
|
||||
registered from board initialization code.
|
||||
|
@ -82,9 +82,8 @@ patternProperties:
|
||||
$ref: /schemas/types.yaml#/definitions/string
|
||||
description: |
|
||||
The action the OS should perform after the critical temperature is reached.
|
||||
By default the system will shutdown as a safe action to prevent damage
|
||||
to the hardware, if the property is not set.
|
||||
The shutdown action should be always the default and preferred one.
|
||||
If the property is not set, it is up to the system to select the correct
|
||||
action. The recommended and preferred default is shutdown.
|
||||
Choose 'reboot' with care, as the hardware may be in thermal stress,
|
||||
thus leading to infinite reboots that may cause damage to the hardware.
|
||||
Make sure the firmware/bootloader will act as the last resort and take
|
||||
|
@ -413,18 +413,21 @@ This function serves as an arbitrator to set the state of a cooling
|
||||
device. It sets the cooling device to the deepest cooling state if
|
||||
possible.
|
||||
|
||||
5. thermal_emergency_poweroff
|
||||
=============================
|
||||
5. Critical Events
|
||||
==================
|
||||
|
||||
On an event of critical trip temperature crossing the thermal framework
|
||||
shuts down the system by calling hw_protection_shutdown(). The
|
||||
hw_protection_shutdown() first attempts to perform an orderly shutdown
|
||||
but accepts a delay after which it proceeds doing a forced power-off
|
||||
or as last resort an emergency_restart.
|
||||
On an event of critical trip temperature crossing, the thermal framework
|
||||
will trigger a hardware protection power-off (shutdown) or reboot,
|
||||
depending on configuration.
|
||||
|
||||
At first, the kernel will attempt an orderly power-off or reboot, but
|
||||
accepts a delay after which it proceeds to do a forced power-off or
|
||||
reboot, respectively. If this fails, ``emergency_restart()`` is invoked
|
||||
as last resort.
|
||||
|
||||
The delay should be carefully profiled so as to give adequate time for
|
||||
orderly poweroff.
|
||||
orderly power-off or reboot.
|
||||
|
||||
If the delay is set to 0 emergency poweroff will not be supported. So a
|
||||
carefully profiled non-zero positive value is a must for emergency
|
||||
poweroff to be triggered.
|
||||
If the delay is set to 0, the emergency action will not be supported. So a
|
||||
carefully profiled non-zero positive value is a must for the emergency
|
||||
action to be triggered.
|
||||
|
@ -128,6 +128,16 @@ process running on the system, which is named after the process ID (PID).
|
||||
The link 'self' points to the process reading the file system. Each process
|
||||
subdirectory has the entries listed in Table 1-1.
|
||||
|
||||
A process can read its own information from /proc/PID/* with no extra
|
||||
permissions. When reading /proc/PID/* information for other processes, reading
|
||||
process is required to have either CAP_SYS_PTRACE capability with
|
||||
PTRACE_MODE_READ access permissions, or, alternatively, CAP_PERFMON
|
||||
capability. This applies to all read-only information like `maps`, `environ`,
|
||||
`pagemap`, etc. The only exception is `mem` file due to its read-write nature,
|
||||
which requires CAP_SYS_PTRACE capabilities with more elevated
|
||||
PTRACE_MODE_ATTACH permissions; CAP_PERFMON capability does not grant access
|
||||
to /proc/PID/mem for other processes.
|
||||
|
||||
Note that an open file descriptor to /proc/<pid> or to any of its
|
||||
contained files or subdirectories does not prevent <pid> being reused
|
||||
for some other process in the event that <pid> exits. Operations on
|
||||
|
@ -18802,6 +18802,7 @@ F: mm/percpu*.c
|
||||
|
||||
PER-TASK DELAY ACCOUNTING
|
||||
M: Balbir Singh <bsingharora@gmail.com>
|
||||
M: Yang Yang <yang.yang29@zte.com.cn>
|
||||
S: Maintained
|
||||
F: include/linux/delayacct.h
|
||||
F: kernel/delayacct.c
|
||||
@ -22153,7 +22154,7 @@ M: Joonsoo Kim <iamjoonsoo.kim@lge.com>
|
||||
M: Andrew Morton <akpm@linux-foundation.org>
|
||||
M: Vlastimil Babka <vbabka@suse.cz>
|
||||
R: Roman Gushchin <roman.gushchin@linux.dev>
|
||||
R: Hyeonggon Yoo <42.hyeyoo@gmail.com>
|
||||
R: Harry Yoo <harry.yoo@oracle.com>
|
||||
L: linux-mm@kvack.org
|
||||
S: Maintained
|
||||
T: git git://git.kernel.org/pub/scm/linux/kernel/git/vbabka/slab.git
|
||||
|
@ -98,21 +98,19 @@ static void __init arch_reserve_crashkernel(void)
|
||||
{
|
||||
unsigned long long low_size = 0;
|
||||
unsigned long long crash_base, crash_size;
|
||||
char *cmdline = boot_command_line;
|
||||
bool high = false;
|
||||
int ret;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_CRASH_RESERVE))
|
||||
return;
|
||||
|
||||
ret = parse_crashkernel(cmdline, memblock_phys_mem_size(),
|
||||
ret = parse_crashkernel(boot_command_line, memblock_phys_mem_size(),
|
||||
&crash_size, &crash_base,
|
||||
&low_size, &high);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
reserve_crashkernel_generic(cmdline, crash_size, crash_base,
|
||||
low_size, high);
|
||||
reserve_crashkernel_generic(crash_size, crash_base, low_size, high);
|
||||
}
|
||||
|
||||
static phys_addr_t __init max_zone_phys(phys_addr_t zone_limit)
|
||||
|
@ -259,18 +259,17 @@ static void __init arch_reserve_crashkernel(void)
|
||||
int ret;
|
||||
unsigned long long low_size = 0;
|
||||
unsigned long long crash_base, crash_size;
|
||||
char *cmdline = boot_command_line;
|
||||
bool high = false;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_CRASH_RESERVE))
|
||||
return;
|
||||
|
||||
ret = parse_crashkernel(cmdline, memblock_phys_mem_size(),
|
||||
ret = parse_crashkernel(boot_command_line, memblock_phys_mem_size(),
|
||||
&crash_size, &crash_base, &low_size, &high);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
reserve_crashkernel_generic(cmdline, crash_size, crash_base, low_size, high);
|
||||
reserve_crashkernel_generic(crash_size, crash_base, low_size, high);
|
||||
}
|
||||
|
||||
static void __init fdt_setup(void)
|
||||
|
@ -716,6 +716,9 @@ config ARCH_SUPPORTS_CRASH_HOTPLUG
|
||||
def_bool y
|
||||
depends on PPC64
|
||||
|
||||
config ARCH_HAS_GENERIC_CRASHKERNEL_RESERVATION
|
||||
def_bool CRASH_RESERVE
|
||||
|
||||
config FA_DUMP
|
||||
bool "Firmware-assisted dump"
|
||||
depends on CRASH_DUMP && PPC64 && (PPC_RTAS || PPC_POWERNV)
|
||||
|
8
arch/powerpc/include/asm/crash_reserve.h
Normal file
8
arch/powerpc/include/asm/crash_reserve.h
Normal file
@ -0,0 +1,8 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _ASM_POWERPC_CRASH_RESERVE_H
|
||||
#define _ASM_POWERPC_CRASH_RESERVE_H
|
||||
|
||||
/* crash kernel regions are Page size agliged */
|
||||
#define CRASH_ALIGN PAGE_SIZE
|
||||
|
||||
#endif /* _ASM_POWERPC_CRASH_RESERVE_H */
|
@ -94,8 +94,10 @@ int arch_kexec_kernel_image_probe(struct kimage *image, void *buf, unsigned long
|
||||
int arch_kimage_file_post_load_cleanup(struct kimage *image);
|
||||
#define arch_kimage_file_post_load_cleanup arch_kimage_file_post_load_cleanup
|
||||
|
||||
int arch_kexec_locate_mem_hole(struct kexec_buf *kbuf);
|
||||
#define arch_kexec_locate_mem_hole arch_kexec_locate_mem_hole
|
||||
int arch_check_excluded_range(struct kimage *image, unsigned long start,
|
||||
unsigned long end);
|
||||
#define arch_check_excluded_range arch_check_excluded_range
|
||||
|
||||
|
||||
int load_crashdump_segments_ppc64(struct kimage *image,
|
||||
struct kexec_buf *kbuf);
|
||||
@ -112,9 +114,9 @@ int setup_new_fdt_ppc64(const struct kimage *image, void *fdt, struct crash_mem
|
||||
|
||||
#ifdef CONFIG_CRASH_RESERVE
|
||||
int __init overlaps_crashkernel(unsigned long start, unsigned long size);
|
||||
extern void reserve_crashkernel(void);
|
||||
extern void arch_reserve_crashkernel(void);
|
||||
#else
|
||||
static inline void reserve_crashkernel(void) {}
|
||||
static inline void arch_reserve_crashkernel(void) {}
|
||||
static inline int overlaps_crashkernel(unsigned long start, unsigned long size) { return 0; }
|
||||
#endif
|
||||
|
||||
|
@ -860,7 +860,7 @@ void __init early_init_devtree(void *params)
|
||||
*/
|
||||
if (fadump_reserve_mem() == 0)
|
||||
#endif
|
||||
reserve_crashkernel();
|
||||
arch_reserve_crashkernel();
|
||||
early_reserve_mem();
|
||||
|
||||
if (memory_limit > memblock_phys_mem_size())
|
||||
|
@ -58,38 +58,20 @@ void machine_kexec(struct kimage *image)
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CRASH_RESERVE
|
||||
void __init reserve_crashkernel(void)
|
||||
|
||||
static unsigned long long __init get_crash_base(unsigned long long crash_base)
|
||||
{
|
||||
unsigned long long crash_size, crash_base, total_mem_sz;
|
||||
int ret;
|
||||
|
||||
total_mem_sz = memory_limit ? memory_limit : memblock_phys_mem_size();
|
||||
/* use common parsing */
|
||||
ret = parse_crashkernel(boot_command_line, total_mem_sz,
|
||||
&crash_size, &crash_base, NULL, NULL);
|
||||
if (ret == 0 && crash_size > 0) {
|
||||
crashk_res.start = crash_base;
|
||||
crashk_res.end = crash_base + crash_size - 1;
|
||||
}
|
||||
|
||||
if (crashk_res.end == crashk_res.start) {
|
||||
crashk_res.start = crashk_res.end = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* We might have got these values via the command line or the
|
||||
* device tree, either way sanitise them now. */
|
||||
|
||||
crash_size = resource_size(&crashk_res);
|
||||
|
||||
#ifndef CONFIG_NONSTATIC_KERNEL
|
||||
if (crashk_res.start != KDUMP_KERNELBASE)
|
||||
if (crash_base != KDUMP_KERNELBASE)
|
||||
printk("Crash kernel location must be 0x%x\n",
|
||||
KDUMP_KERNELBASE);
|
||||
|
||||
crashk_res.start = KDUMP_KERNELBASE;
|
||||
return KDUMP_KERNELBASE;
|
||||
#else
|
||||
if (!crashk_res.start) {
|
||||
unsigned long long crash_base_align;
|
||||
|
||||
if (!crash_base) {
|
||||
#ifdef CONFIG_PPC64
|
||||
/*
|
||||
* On the LPAR platform place the crash kernel to mid of
|
||||
@ -101,53 +83,51 @@ void __init reserve_crashkernel(void)
|
||||
* kernel starts at 128MB offset on other platforms.
|
||||
*/
|
||||
if (firmware_has_feature(FW_FEATURE_LPAR))
|
||||
crashk_res.start = min_t(u64, ppc64_rma_size / 2, SZ_512M);
|
||||
crash_base = min_t(u64, ppc64_rma_size / 2, SZ_512M);
|
||||
else
|
||||
crashk_res.start = min_t(u64, ppc64_rma_size / 2, SZ_128M);
|
||||
crash_base = min_t(u64, ppc64_rma_size / 2, SZ_128M);
|
||||
#else
|
||||
crashk_res.start = KDUMP_KERNELBASE;
|
||||
crash_base = KDUMP_KERNELBASE;
|
||||
#endif
|
||||
}
|
||||
|
||||
crash_base = PAGE_ALIGN(crashk_res.start);
|
||||
if (crash_base != crashk_res.start) {
|
||||
printk("Crash kernel base must be aligned to 0x%lx\n",
|
||||
PAGE_SIZE);
|
||||
crashk_res.start = crash_base;
|
||||
}
|
||||
crash_base_align = PAGE_ALIGN(crash_base);
|
||||
if (crash_base != crash_base_align)
|
||||
pr_warn("Crash kernel base must be aligned to 0x%lx\n", PAGE_SIZE);
|
||||
|
||||
return crash_base_align;
|
||||
#endif
|
||||
crash_size = PAGE_ALIGN(crash_size);
|
||||
crashk_res.end = crashk_res.start + crash_size - 1;
|
||||
}
|
||||
|
||||
void __init arch_reserve_crashkernel(void)
|
||||
{
|
||||
unsigned long long crash_size, crash_base, crash_end;
|
||||
unsigned long long kernel_start, kernel_size;
|
||||
unsigned long long total_mem_sz;
|
||||
int ret;
|
||||
|
||||
total_mem_sz = memory_limit ? memory_limit : memblock_phys_mem_size();
|
||||
|
||||
/* use common parsing */
|
||||
ret = parse_crashkernel(boot_command_line, total_mem_sz, &crash_size,
|
||||
&crash_base, NULL, NULL);
|
||||
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
crash_base = get_crash_base(crash_base);
|
||||
crash_end = crash_base + crash_size - 1;
|
||||
|
||||
kernel_start = __pa(_stext);
|
||||
kernel_size = _end - _stext;
|
||||
|
||||
/* The crash region must not overlap the current kernel */
|
||||
if (overlaps_crashkernel(__pa(_stext), _end - _stext)) {
|
||||
printk(KERN_WARNING
|
||||
"Crash kernel can not overlap current kernel\n");
|
||||
crashk_res.start = crashk_res.end = 0;
|
||||
if ((kernel_start + kernel_size > crash_base) && (kernel_start <= crash_end)) {
|
||||
pr_warn("Crash kernel can not overlap current kernel\n");
|
||||
return;
|
||||
}
|
||||
|
||||
/* Crash kernel trumps memory limit */
|
||||
if (memory_limit && memory_limit <= crashk_res.end) {
|
||||
memory_limit = crashk_res.end + 1;
|
||||
total_mem_sz = memory_limit;
|
||||
printk("Adjusted memory limit for crashkernel, now 0x%llx\n",
|
||||
memory_limit);
|
||||
}
|
||||
|
||||
printk(KERN_INFO "Reserving %ldMB of memory at %ldMB "
|
||||
"for crashkernel (System RAM: %ldMB)\n",
|
||||
(unsigned long)(crash_size >> 20),
|
||||
(unsigned long)(crashk_res.start >> 20),
|
||||
(unsigned long)(total_mem_sz >> 20));
|
||||
|
||||
if (!memblock_is_region_memory(crashk_res.start, crash_size) ||
|
||||
memblock_reserve(crashk_res.start, crash_size)) {
|
||||
pr_err("Failed to reserve memory for crashkernel!\n");
|
||||
crashk_res.start = crashk_res.end = 0;
|
||||
return;
|
||||
}
|
||||
reserve_crashkernel_generic(crash_size, crash_base, 0, false);
|
||||
}
|
||||
|
||||
int __init overlaps_crashkernel(unsigned long start, unsigned long size)
|
||||
|
@ -49,201 +49,18 @@ const struct kexec_file_ops * const kexec_file_loaders[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
/**
|
||||
* __locate_mem_hole_top_down - Looks top down for a large enough memory hole
|
||||
* in the memory regions between buf_min & buf_max
|
||||
* for the buffer. If found, sets kbuf->mem.
|
||||
* @kbuf: Buffer contents and memory parameters.
|
||||
* @buf_min: Minimum address for the buffer.
|
||||
* @buf_max: Maximum address for the buffer.
|
||||
*
|
||||
* Returns 0 on success, negative errno on error.
|
||||
*/
|
||||
static int __locate_mem_hole_top_down(struct kexec_buf *kbuf,
|
||||
u64 buf_min, u64 buf_max)
|
||||
int arch_check_excluded_range(struct kimage *image, unsigned long start,
|
||||
unsigned long end)
|
||||
{
|
||||
int ret = -EADDRNOTAVAIL;
|
||||
phys_addr_t start, end;
|
||||
u64 i;
|
||||
struct crash_mem *emem;
|
||||
int i;
|
||||
|
||||
for_each_mem_range_rev(i, &start, &end) {
|
||||
/*
|
||||
* memblock uses [start, end) convention while it is
|
||||
* [start, end] here. Fix the off-by-one to have the
|
||||
* same convention.
|
||||
*/
|
||||
end -= 1;
|
||||
emem = image->arch.exclude_ranges;
|
||||
for (i = 0; i < emem->nr_ranges; i++)
|
||||
if (start < emem->ranges[i].end && end > emem->ranges[i].start)
|
||||
return 1;
|
||||
|
||||
if (start > buf_max)
|
||||
continue;
|
||||
|
||||
/* Memory hole not found */
|
||||
if (end < buf_min)
|
||||
break;
|
||||
|
||||
/* Adjust memory region based on the given range */
|
||||
if (start < buf_min)
|
||||
start = buf_min;
|
||||
if (end > buf_max)
|
||||
end = buf_max;
|
||||
|
||||
start = ALIGN(start, kbuf->buf_align);
|
||||
if (start < end && (end - start + 1) >= kbuf->memsz) {
|
||||
/* Suitable memory range found. Set kbuf->mem */
|
||||
kbuf->mem = ALIGN_DOWN(end - kbuf->memsz + 1,
|
||||
kbuf->buf_align);
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* locate_mem_hole_top_down_ppc64 - Skip special memory regions to find a
|
||||
* suitable buffer with top down approach.
|
||||
* @kbuf: Buffer contents and memory parameters.
|
||||
* @buf_min: Minimum address for the buffer.
|
||||
* @buf_max: Maximum address for the buffer.
|
||||
* @emem: Exclude memory ranges.
|
||||
*
|
||||
* Returns 0 on success, negative errno on error.
|
||||
*/
|
||||
static int locate_mem_hole_top_down_ppc64(struct kexec_buf *kbuf,
|
||||
u64 buf_min, u64 buf_max,
|
||||
const struct crash_mem *emem)
|
||||
{
|
||||
int i, ret = 0, err = -EADDRNOTAVAIL;
|
||||
u64 start, end, tmin, tmax;
|
||||
|
||||
tmax = buf_max;
|
||||
for (i = (emem->nr_ranges - 1); i >= 0; i--) {
|
||||
start = emem->ranges[i].start;
|
||||
end = emem->ranges[i].end;
|
||||
|
||||
if (start > tmax)
|
||||
continue;
|
||||
|
||||
if (end < tmax) {
|
||||
tmin = (end < buf_min ? buf_min : end + 1);
|
||||
ret = __locate_mem_hole_top_down(kbuf, tmin, tmax);
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
tmax = start - 1;
|
||||
|
||||
if (tmax < buf_min) {
|
||||
ret = err;
|
||||
break;
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
tmin = buf_min;
|
||||
ret = __locate_mem_hole_top_down(kbuf, tmin, tmax);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* __locate_mem_hole_bottom_up - Looks bottom up for a large enough memory hole
|
||||
* in the memory regions between buf_min & buf_max
|
||||
* for the buffer. If found, sets kbuf->mem.
|
||||
* @kbuf: Buffer contents and memory parameters.
|
||||
* @buf_min: Minimum address for the buffer.
|
||||
* @buf_max: Maximum address for the buffer.
|
||||
*
|
||||
* Returns 0 on success, negative errno on error.
|
||||
*/
|
||||
static int __locate_mem_hole_bottom_up(struct kexec_buf *kbuf,
|
||||
u64 buf_min, u64 buf_max)
|
||||
{
|
||||
int ret = -EADDRNOTAVAIL;
|
||||
phys_addr_t start, end;
|
||||
u64 i;
|
||||
|
||||
for_each_mem_range(i, &start, &end) {
|
||||
/*
|
||||
* memblock uses [start, end) convention while it is
|
||||
* [start, end] here. Fix the off-by-one to have the
|
||||
* same convention.
|
||||
*/
|
||||
end -= 1;
|
||||
|
||||
if (end < buf_min)
|
||||
continue;
|
||||
|
||||
/* Memory hole not found */
|
||||
if (start > buf_max)
|
||||
break;
|
||||
|
||||
/* Adjust memory region based on the given range */
|
||||
if (start < buf_min)
|
||||
start = buf_min;
|
||||
if (end > buf_max)
|
||||
end = buf_max;
|
||||
|
||||
start = ALIGN(start, kbuf->buf_align);
|
||||
if (start < end && (end - start + 1) >= kbuf->memsz) {
|
||||
/* Suitable memory range found. Set kbuf->mem */
|
||||
kbuf->mem = start;
|
||||
ret = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* locate_mem_hole_bottom_up_ppc64 - Skip special memory regions to find a
|
||||
* suitable buffer with bottom up approach.
|
||||
* @kbuf: Buffer contents and memory parameters.
|
||||
* @buf_min: Minimum address for the buffer.
|
||||
* @buf_max: Maximum address for the buffer.
|
||||
* @emem: Exclude memory ranges.
|
||||
*
|
||||
* Returns 0 on success, negative errno on error.
|
||||
*/
|
||||
static int locate_mem_hole_bottom_up_ppc64(struct kexec_buf *kbuf,
|
||||
u64 buf_min, u64 buf_max,
|
||||
const struct crash_mem *emem)
|
||||
{
|
||||
int i, ret = 0, err = -EADDRNOTAVAIL;
|
||||
u64 start, end, tmin, tmax;
|
||||
|
||||
tmin = buf_min;
|
||||
for (i = 0; i < emem->nr_ranges; i++) {
|
||||
start = emem->ranges[i].start;
|
||||
end = emem->ranges[i].end;
|
||||
|
||||
if (end < tmin)
|
||||
continue;
|
||||
|
||||
if (start > tmin) {
|
||||
tmax = (start > buf_max ? buf_max : start - 1);
|
||||
ret = __locate_mem_hole_bottom_up(kbuf, tmin, tmax);
|
||||
if (!ret)
|
||||
return 0;
|
||||
}
|
||||
|
||||
tmin = end + 1;
|
||||
|
||||
if (tmin > buf_max) {
|
||||
ret = err;
|
||||
break;
|
||||
}
|
||||
ret = 0;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
tmax = buf_max;
|
||||
ret = __locate_mem_hole_bottom_up(kbuf, tmin, tmax);
|
||||
}
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CRASH_DUMP
|
||||
@ -1004,64 +821,6 @@ out:
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* arch_kexec_locate_mem_hole - Skip special memory regions like rtas, opal,
|
||||
* tce-table, reserved-ranges & such (exclude
|
||||
* memory ranges) as they can't be used for kexec
|
||||
* segment buffer. Sets kbuf->mem when a suitable
|
||||
* memory hole is found.
|
||||
* @kbuf: Buffer contents and memory parameters.
|
||||
*
|
||||
* Assumes minimum of PAGE_SIZE alignment for kbuf->memsz & kbuf->buf_align.
|
||||
*
|
||||
* Returns 0 on success, negative errno on error.
|
||||
*/
|
||||
int arch_kexec_locate_mem_hole(struct kexec_buf *kbuf)
|
||||
{
|
||||
struct crash_mem **emem;
|
||||
u64 buf_min, buf_max;
|
||||
int ret;
|
||||
|
||||
/* Look up the exclude ranges list while locating the memory hole */
|
||||
emem = &(kbuf->image->arch.exclude_ranges);
|
||||
if (!(*emem) || ((*emem)->nr_ranges == 0)) {
|
||||
pr_warn("No exclude range list. Using the default locate mem hole method\n");
|
||||
return kexec_locate_mem_hole(kbuf);
|
||||
}
|
||||
|
||||
buf_min = kbuf->buf_min;
|
||||
buf_max = kbuf->buf_max;
|
||||
/* Segments for kdump kernel should be within crashkernel region */
|
||||
if (IS_ENABLED(CONFIG_CRASH_DUMP) && kbuf->image->type == KEXEC_TYPE_CRASH) {
|
||||
buf_min = (buf_min < crashk_res.start ?
|
||||
crashk_res.start : buf_min);
|
||||
buf_max = (buf_max > crashk_res.end ?
|
||||
crashk_res.end : buf_max);
|
||||
}
|
||||
|
||||
if (buf_min > buf_max) {
|
||||
pr_err("Invalid buffer min and/or max values\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (kbuf->top_down)
|
||||
ret = locate_mem_hole_top_down_ppc64(kbuf, buf_min, buf_max,
|
||||
*emem);
|
||||
else
|
||||
ret = locate_mem_hole_bottom_up_ppc64(kbuf, buf_min, buf_max,
|
||||
*emem);
|
||||
|
||||
/* Add the buffer allocated to the exclude list for the next lookup */
|
||||
if (!ret) {
|
||||
add_mem_range(emem, kbuf->mem, kbuf->memsz);
|
||||
sort_memory_ranges(*emem, true);
|
||||
} else {
|
||||
pr_err("Failed to locate memory buffer of size %lu\n",
|
||||
kbuf->memsz);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* arch_kexec_kernel_image_probe - Does additional handling needed to setup
|
||||
* kexec segments.
|
||||
|
@ -338,7 +338,7 @@ static int __init add_system_ram_resources(void)
|
||||
*/
|
||||
res->end = end - 1;
|
||||
res->flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;
|
||||
WARN_ON(request_resource(&iomem_resource, res) < 0);
|
||||
WARN_ON(insert_resource(&iomem_resource, res) < 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1393,21 +1393,19 @@ static void __init arch_reserve_crashkernel(void)
|
||||
{
|
||||
unsigned long long low_size = 0;
|
||||
unsigned long long crash_base, crash_size;
|
||||
char *cmdline = boot_command_line;
|
||||
bool high = false;
|
||||
int ret;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_CRASH_RESERVE))
|
||||
return;
|
||||
|
||||
ret = parse_crashkernel(cmdline, memblock_phys_mem_size(),
|
||||
ret = parse_crashkernel(boot_command_line, memblock_phys_mem_size(),
|
||||
&crash_size, &crash_base,
|
||||
&low_size, &high);
|
||||
if (ret)
|
||||
return;
|
||||
|
||||
reserve_crashkernel_generic(cmdline, crash_size, crash_base,
|
||||
low_size, high);
|
||||
reserve_crashkernel_generic(crash_size, crash_base, low_size, high);
|
||||
}
|
||||
|
||||
void __init paging_init(void)
|
||||
|
@ -578,14 +578,13 @@ static void __init memblock_x86_reserve_range_setup_data(void)
|
||||
static void __init arch_reserve_crashkernel(void)
|
||||
{
|
||||
unsigned long long crash_base, crash_size, low_size = 0;
|
||||
char *cmdline = boot_command_line;
|
||||
bool high = false;
|
||||
int ret;
|
||||
|
||||
if (!IS_ENABLED(CONFIG_CRASH_RESERVE))
|
||||
return;
|
||||
|
||||
ret = parse_crashkernel(cmdline, memblock_phys_mem_size(),
|
||||
ret = parse_crashkernel(boot_command_line, memblock_phys_mem_size(),
|
||||
&crash_size, &crash_base,
|
||||
&low_size, &high);
|
||||
if (ret)
|
||||
@ -596,8 +595,7 @@ static void __init arch_reserve_crashkernel(void)
|
||||
return;
|
||||
}
|
||||
|
||||
reserve_crashkernel_generic(cmdline, crash_size, crash_base,
|
||||
low_size, high);
|
||||
reserve_crashkernel_generic(crash_size, crash_base, low_size, high);
|
||||
}
|
||||
|
||||
static struct resource standard_io_resources[] = {
|
||||
|
@ -2586,7 +2586,7 @@ int hl_cs_ioctl(struct drm_device *ddev, void *data, struct drm_file *file_priv)
|
||||
cs_seq = args->in.seq;
|
||||
|
||||
timeout = flags & HL_CS_FLAGS_CUSTOM_TIMEOUT
|
||||
? msecs_to_jiffies(args->in.timeout * 1000)
|
||||
? secs_to_jiffies(args->in.timeout)
|
||||
: hpriv->hdev->timeout_jiffies;
|
||||
|
||||
switch (cs_type) {
|
||||
|
@ -1403,7 +1403,7 @@ static ssize_t hl_timeout_locked_write(struct file *f, const char __user *buf,
|
||||
return rc;
|
||||
|
||||
if (value)
|
||||
hdev->timeout_jiffies = msecs_to_jiffies(value * 1000);
|
||||
hdev->timeout_jiffies = secs_to_jiffies(value);
|
||||
else
|
||||
hdev->timeout_jiffies = MAX_SCHEDULE_TIMEOUT;
|
||||
|
||||
|
@ -2091,7 +2091,7 @@ int hl_device_cond_reset(struct hl_device *hdev, u32 flags, u64 event_mask)
|
||||
dev_dbg(hdev->dev, "Device is going to be hard-reset in %u sec unless being released\n",
|
||||
hdev->device_release_watchdog_timeout_sec);
|
||||
schedule_delayed_work(&hdev->device_release_watchdog_work.reset_work,
|
||||
msecs_to_jiffies(hdev->device_release_watchdog_timeout_sec * 1000));
|
||||
secs_to_jiffies(hdev->device_release_watchdog_timeout_sec));
|
||||
hdev->reset_info.watchdog_active = 1;
|
||||
out:
|
||||
spin_unlock(&hdev->reset_info.lock);
|
||||
|
@ -386,7 +386,7 @@ static int fixup_device_params(struct hl_device *hdev)
|
||||
hdev->fw_comms_poll_interval_usec = HL_FW_STATUS_POLL_INTERVAL_USEC;
|
||||
|
||||
if (tmp_timeout)
|
||||
hdev->timeout_jiffies = msecs_to_jiffies(tmp_timeout * MSEC_PER_SEC);
|
||||
hdev->timeout_jiffies = secs_to_jiffies(tmp_timeout);
|
||||
else
|
||||
hdev->timeout_jiffies = MAX_SCHEDULE_TIMEOUT;
|
||||
|
||||
|
@ -160,8 +160,7 @@ void zpodd_on_suspend(struct ata_device *dev)
|
||||
return;
|
||||
}
|
||||
|
||||
expires = zpodd->last_ready +
|
||||
msecs_to_jiffies(zpodd_poweroff_delay * 1000);
|
||||
expires = zpodd->last_ready + secs_to_jiffies(zpodd_poweroff_delay);
|
||||
if (time_before(jiffies, expires))
|
||||
return;
|
||||
|
||||
|
@ -160,7 +160,7 @@ static int __wait_for_resp(struct bnxt_qplib_rcfw *rcfw, u16 cookie)
|
||||
wait_event_timeout(cmdq->waitq,
|
||||
!crsqe->is_in_used ||
|
||||
test_bit(ERR_DEVICE_DETACHED, &cmdq->flags),
|
||||
msecs_to_jiffies(rcfw->max_timeout * 1000));
|
||||
secs_to_jiffies(rcfw->max_timeout));
|
||||
|
||||
if (!crsqe->is_in_used)
|
||||
return 0;
|
||||
|
@ -4469,11 +4469,9 @@ static void nvme_fw_act_work(struct work_struct *work)
|
||||
nvme_auth_stop(ctrl);
|
||||
|
||||
if (ctrl->mtfa)
|
||||
fw_act_timeout = jiffies +
|
||||
msecs_to_jiffies(ctrl->mtfa * 100);
|
||||
fw_act_timeout = jiffies + msecs_to_jiffies(ctrl->mtfa * 100);
|
||||
else
|
||||
fw_act_timeout = jiffies +
|
||||
msecs_to_jiffies(admin_timeout * 1000);
|
||||
fw_act_timeout = jiffies + secs_to_jiffies(admin_timeout);
|
||||
|
||||
nvme_quiesce_io_queues(ctrl);
|
||||
while (nvme_ctrl_pp_status(ctrl)) {
|
||||
|
@ -455,7 +455,7 @@ static void cros_ec_lpc_acpi_notify(acpi_handle device, u32 value, void *data)
|
||||
blocking_notifier_call_chain(&ec_dev->panic_notifier, 0, ec_dev);
|
||||
kobject_uevent_env(&ec_dev->dev->kobj, KOBJ_CHANGE, (char **)env);
|
||||
/* Begin orderly shutdown. EC will force reset after a short period. */
|
||||
hw_protection_shutdown("CrOS EC Panic", -1);
|
||||
__hw_protection_trigger("CrOS EC Panic", -1, HWPROT_ACT_SHUTDOWN);
|
||||
/* Do not query for other events after a panic is reported */
|
||||
return;
|
||||
}
|
||||
|
@ -502,8 +502,7 @@ static int da9030_battery_probe(struct platform_device *pdev)
|
||||
|
||||
/* 10 seconds between monitor runs unless platform defines other
|
||||
interval */
|
||||
charger->interval = msecs_to_jiffies(
|
||||
(pdata->batmon_interval ? : 10) * 1000);
|
||||
charger->interval = secs_to_jiffies(pdata->batmon_interval ? : 10);
|
||||
|
||||
charger->charge_milliamp = pdata->charge_milliamp;
|
||||
charger->charge_millivolt = pdata->charge_millivolt;
|
||||
|
@ -5282,8 +5282,8 @@ static void regulator_handle_critical(struct regulator_dev *rdev,
|
||||
if (!reason)
|
||||
return;
|
||||
|
||||
hw_protection_shutdown(reason,
|
||||
rdev->constraints->uv_less_critical_window_ms);
|
||||
hw_protection_trigger(reason,
|
||||
rdev->constraints->uv_less_critical_window_ms);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -64,16 +64,16 @@ static void regulator_notifier_isr_work(struct work_struct *work)
|
||||
reread:
|
||||
if (d->fatal_cnt && h->retry_cnt > d->fatal_cnt) {
|
||||
if (!d->die)
|
||||
return hw_protection_shutdown("Regulator HW failure? - no IC recovery",
|
||||
REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS);
|
||||
return hw_protection_trigger("Regulator HW failure? - no IC recovery",
|
||||
REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS);
|
||||
ret = d->die(rid);
|
||||
/*
|
||||
* If the 'last resort' IC recovery failed we will have
|
||||
* nothing else left to do...
|
||||
*/
|
||||
if (ret)
|
||||
return hw_protection_shutdown("Regulator HW failure. IC recovery failed",
|
||||
REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS);
|
||||
return hw_protection_trigger("Regulator HW failure. IC recovery failed",
|
||||
REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS);
|
||||
|
||||
/*
|
||||
* If h->die() was implemented we assume recovery has been
|
||||
@ -263,14 +263,14 @@ fail_out:
|
||||
if (d->fatal_cnt && h->retry_cnt > d->fatal_cnt) {
|
||||
/* If we have no recovery, just try shut down straight away */
|
||||
if (!d->die) {
|
||||
hw_protection_shutdown("Regulator failure. Retry count exceeded",
|
||||
REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS);
|
||||
hw_protection_trigger("Regulator failure. Retry count exceeded",
|
||||
REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS);
|
||||
} else {
|
||||
ret = d->die(rid);
|
||||
/* If die() failed shut down as a last attempt to save the HW */
|
||||
if (ret)
|
||||
hw_protection_shutdown("Regulator failure. Recovery failed",
|
||||
REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS);
|
||||
hw_protection_trigger("Regulator failure. Recovery failed",
|
||||
REGULATOR_FORCED_SAFETY_SHUTDOWN_WAIT_MS);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -369,7 +369,8 @@ void thermal_governor_update_tz(struct thermal_zone_device *tz,
|
||||
tz->governor->update_tz(tz, reason);
|
||||
}
|
||||
|
||||
static void thermal_zone_device_halt(struct thermal_zone_device *tz, bool shutdown)
|
||||
static void thermal_zone_device_halt(struct thermal_zone_device *tz,
|
||||
enum hw_protection_action action)
|
||||
{
|
||||
/*
|
||||
* poweroff_delay_ms must be a carefully profiled positive value.
|
||||
@ -380,21 +381,23 @@ static void thermal_zone_device_halt(struct thermal_zone_device *tz, bool shutdo
|
||||
|
||||
dev_emerg(&tz->device, "%s: critical temperature reached\n", tz->type);
|
||||
|
||||
if (shutdown)
|
||||
hw_protection_shutdown(msg, poweroff_delay_ms);
|
||||
else
|
||||
hw_protection_reboot(msg, poweroff_delay_ms);
|
||||
__hw_protection_trigger(msg, poweroff_delay_ms, action);
|
||||
}
|
||||
|
||||
void thermal_zone_device_critical(struct thermal_zone_device *tz)
|
||||
{
|
||||
thermal_zone_device_halt(tz, true);
|
||||
thermal_zone_device_halt(tz, HWPROT_ACT_DEFAULT);
|
||||
}
|
||||
EXPORT_SYMBOL(thermal_zone_device_critical);
|
||||
|
||||
void thermal_zone_device_critical_shutdown(struct thermal_zone_device *tz)
|
||||
{
|
||||
thermal_zone_device_halt(tz, HWPROT_ACT_SHUTDOWN);
|
||||
}
|
||||
|
||||
void thermal_zone_device_critical_reboot(struct thermal_zone_device *tz)
|
||||
{
|
||||
thermal_zone_device_halt(tz, false);
|
||||
thermal_zone_device_halt(tz, HWPROT_ACT_REBOOT);
|
||||
}
|
||||
|
||||
static void handle_critical_trips(struct thermal_zone_device *tz,
|
||||
|
@ -262,6 +262,7 @@ int thermal_build_list_of_policies(char *buf);
|
||||
void __thermal_zone_device_update(struct thermal_zone_device *tz,
|
||||
enum thermal_notify_event event);
|
||||
void thermal_zone_device_critical_reboot(struct thermal_zone_device *tz);
|
||||
void thermal_zone_device_critical_shutdown(struct thermal_zone_device *tz);
|
||||
void thermal_governor_update_tz(struct thermal_zone_device *tz,
|
||||
enum thermal_notify_event reason);
|
||||
|
||||
|
@ -405,9 +405,12 @@ static struct thermal_zone_device *thermal_of_zone_register(struct device_node *
|
||||
of_ops.should_bind = thermal_of_should_bind;
|
||||
|
||||
ret = of_property_read_string(np, "critical-action", &action);
|
||||
if (!ret)
|
||||
if (!of_ops.critical && !strcasecmp(action, "reboot"))
|
||||
if (!ret && !of_ops.critical) {
|
||||
if (!strcasecmp(action, "reboot"))
|
||||
of_ops.critical = thermal_zone_device_critical_reboot;
|
||||
else if (!strcasecmp(action, "shutdown"))
|
||||
of_ops.critical = thermal_zone_device_critical_shutdown;
|
||||
}
|
||||
|
||||
tz = thermal_zone_device_register_with_trips(np->name, trips, ntrips,
|
||||
data, &of_ops, &tzp,
|
||||
|
@ -1564,7 +1564,7 @@ static int transaction_kthread(void *arg)
|
||||
|
||||
do {
|
||||
cannot_commit = false;
|
||||
delay = msecs_to_jiffies(fs_info->commit_interval * 1000);
|
||||
delay = secs_to_jiffies(fs_info->commit_interval);
|
||||
mutex_lock(&fs_info->transaction_kthread_mutex);
|
||||
|
||||
spin_lock(&fs_info->trans_lock);
|
||||
@ -1579,9 +1579,9 @@ static int transaction_kthread(void *arg)
|
||||
cur->state < TRANS_STATE_COMMIT_PREP &&
|
||||
delta < fs_info->commit_interval) {
|
||||
spin_unlock(&fs_info->trans_lock);
|
||||
delay -= msecs_to_jiffies((delta - 1) * 1000);
|
||||
delay -= secs_to_jiffies(delta - 1);
|
||||
delay = min(delay,
|
||||
msecs_to_jiffies(fs_info->commit_interval * 1000));
|
||||
secs_to_jiffies(fs_info->commit_interval));
|
||||
goto sleep;
|
||||
}
|
||||
transid = cur->transid;
|
||||
|
@ -1803,6 +1803,14 @@ static int __ocfs2_find_path(struct ocfs2_caching_info *ci,
|
||||
|
||||
el = root_el;
|
||||
while (el->l_tree_depth) {
|
||||
if (unlikely(le16_to_cpu(el->l_tree_depth) >= OCFS2_MAX_PATH_DEPTH)) {
|
||||
ocfs2_error(ocfs2_metadata_cache_get_super(ci),
|
||||
"Owner %llu has invalid tree depth %u in extent list\n",
|
||||
(unsigned long long)ocfs2_metadata_cache_owner(ci),
|
||||
le16_to_cpu(el->l_tree_depth));
|
||||
ret = -EROFS;
|
||||
goto out;
|
||||
}
|
||||
if (le16_to_cpu(el->l_next_free_rec) == 0) {
|
||||
ocfs2_error(ocfs2_metadata_cache_get_super(ci),
|
||||
"Owner %llu has empty extent list at depth %u\n",
|
||||
|
@ -46,7 +46,6 @@ static int ocfs2_symlink_get_block(struct inode *inode, sector_t iblock,
|
||||
struct buffer_head *bh = NULL;
|
||||
struct buffer_head *buffer_cache_bh = NULL;
|
||||
struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
|
||||
void *kaddr;
|
||||
|
||||
trace_ocfs2_symlink_get_block(
|
||||
(unsigned long long)OCFS2_I(inode)->ip_blkno,
|
||||
@ -91,17 +90,11 @@ static int ocfs2_symlink_get_block(struct inode *inode, sector_t iblock,
|
||||
* could've happened. Since we've got a reference on
|
||||
* the bh, even if it commits while we're doing the
|
||||
* copy, the data is still good. */
|
||||
if (buffer_jbd(buffer_cache_bh)
|
||||
&& ocfs2_inode_is_new(inode)) {
|
||||
kaddr = kmap_atomic(bh_result->b_page);
|
||||
if (!kaddr) {
|
||||
mlog(ML_ERROR, "couldn't kmap!\n");
|
||||
goto bail;
|
||||
}
|
||||
memcpy(kaddr + (bh_result->b_size * iblock),
|
||||
buffer_cache_bh->b_data,
|
||||
bh_result->b_size);
|
||||
kunmap_atomic(kaddr);
|
||||
if (buffer_jbd(buffer_cache_bh) && ocfs2_inode_is_new(inode)) {
|
||||
memcpy_to_folio(bh_result->b_folio,
|
||||
bh_result->b_size * iblock,
|
||||
buffer_cache_bh->b_data,
|
||||
bh_result->b_size);
|
||||
set_buffer_uptodate(bh_result);
|
||||
}
|
||||
brelse(buffer_cache_bh);
|
||||
|
@ -273,7 +273,7 @@ ssize_t ocfs2_quota_write(struct super_block *sb, int type,
|
||||
if (new)
|
||||
memset(bh->b_data, 0, sb->s_blocksize);
|
||||
memcpy(bh->b_data + offset, data, len);
|
||||
flush_dcache_page(bh->b_page);
|
||||
flush_dcache_folio(bh->b_folio);
|
||||
set_buffer_uptodate(bh);
|
||||
unlock_buffer(bh);
|
||||
ocfs2_set_buffer_uptodate(INODE_CACHE(gqinode), bh);
|
||||
|
@ -416,7 +416,7 @@ static const struct file_operations proc_pid_cmdline_ops = {
|
||||
#ifdef CONFIG_KALLSYMS
|
||||
/*
|
||||
* Provides a wchan file via kallsyms in a proper one-value-per-file format.
|
||||
* Returns the resolved symbol. If that fails, simply return the address.
|
||||
* Returns the resolved symbol to user space.
|
||||
*/
|
||||
static int proc_pid_wchan(struct seq_file *m, struct pid_namespace *ns,
|
||||
struct pid *pid, struct task_struct *task)
|
||||
|
@ -230,7 +230,7 @@ xfs_blockgc_queue(
|
||||
rcu_read_lock();
|
||||
if (radix_tree_tagged(&pag->pag_ici_root, XFS_ICI_BLOCKGC_TAG))
|
||||
queue_delayed_work(mp->m_blockgc_wq, &pag->pag_blockgc_work,
|
||||
msecs_to_jiffies(xfs_blockgc_secs * 1000));
|
||||
secs_to_jiffies(xfs_blockgc_secs));
|
||||
rcu_read_unlock();
|
||||
}
|
||||
|
||||
|
@ -569,8 +569,8 @@ retry_timeout_seconds_store(
|
||||
if (val == -1)
|
||||
cfg->retry_timeout = XFS_ERR_RETRY_FOREVER;
|
||||
else {
|
||||
cfg->retry_timeout = msecs_to_jiffies(val * MSEC_PER_SEC);
|
||||
ASSERT(msecs_to_jiffies(val * MSEC_PER_SEC) < LONG_MAX);
|
||||
cfg->retry_timeout = secs_to_jiffies(val);
|
||||
ASSERT(secs_to_jiffies(val) < LONG_MAX);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
@ -687,8 +687,8 @@ xfs_error_sysfs_init_class(
|
||||
if (init[i].retry_timeout == XFS_ERR_RETRY_FOREVER)
|
||||
cfg->retry_timeout = XFS_ERR_RETRY_FOREVER;
|
||||
else
|
||||
cfg->retry_timeout = msecs_to_jiffies(
|
||||
init[i].retry_timeout * MSEC_PER_SEC);
|
||||
cfg->retry_timeout =
|
||||
secs_to_jiffies(init[i].retry_timeout);
|
||||
}
|
||||
return 0;
|
||||
|
||||
|
@ -148,7 +148,7 @@ static inline int suspend_disable_secondary_cpus(void)
|
||||
}
|
||||
static inline void suspend_enable_secondary_cpus(void)
|
||||
{
|
||||
return thaw_secondary_cpus();
|
||||
thaw_secondary_cpus();
|
||||
}
|
||||
|
||||
#else /* !CONFIG_PM_SLEEP_SMP */
|
||||
|
@ -32,13 +32,12 @@ int __init parse_crashkernel(char *cmdline, unsigned long long system_ram,
|
||||
#define CRASH_ADDR_HIGH_MAX memblock_end_of_DRAM()
|
||||
#endif
|
||||
|
||||
void __init reserve_crashkernel_generic(char *cmdline,
|
||||
unsigned long long crash_size,
|
||||
unsigned long long crash_base,
|
||||
unsigned long long crash_low_size,
|
||||
bool high);
|
||||
void __init reserve_crashkernel_generic(unsigned long long crash_size,
|
||||
unsigned long long crash_base,
|
||||
unsigned long long crash_low_size,
|
||||
bool high);
|
||||
#else
|
||||
static inline void __init reserve_crashkernel_generic(char *cmdline,
|
||||
static inline void __init reserve_crashkernel_generic(
|
||||
unsigned long long crash_size,
|
||||
unsigned long long crash_base,
|
||||
unsigned long long crash_low_size,
|
||||
|
@ -104,12 +104,8 @@ ITPREFIX ## _subtree_search(ITSTRUCT *node, ITTYPE start, ITTYPE last) \
|
||||
if (ITSTART(node) <= last) { /* Cond1 */ \
|
||||
if (start <= ITLAST(node)) /* Cond2 */ \
|
||||
return node; /* node is leftmost match */ \
|
||||
if (node->ITRB.rb_right) { \
|
||||
node = rb_entry(node->ITRB.rb_right, \
|
||||
ITSTRUCT, ITRB); \
|
||||
if (start <= node->ITSUBTREE) \
|
||||
continue; \
|
||||
} \
|
||||
node = rb_entry(node->ITRB.rb_right, ITSTRUCT, ITRB); \
|
||||
continue; \
|
||||
} \
|
||||
return NULL; /* No match */ \
|
||||
} \
|
||||
|
@ -154,15 +154,20 @@ enum {
|
||||
};
|
||||
|
||||
/* helpers to define resources */
|
||||
#define DEFINE_RES_NAMED(_start, _size, _name, _flags) \
|
||||
#define DEFINE_RES_NAMED_DESC(_start, _size, _name, _flags, _desc) \
|
||||
(struct resource) { \
|
||||
.start = (_start), \
|
||||
.end = (_start) + (_size) - 1, \
|
||||
.name = (_name), \
|
||||
.flags = (_flags), \
|
||||
.desc = IORES_DESC_NONE, \
|
||||
.desc = (_desc), \
|
||||
}
|
||||
|
||||
#define DEFINE_RES_NAMED(_start, _size, _name, _flags) \
|
||||
DEFINE_RES_NAMED_DESC(_start, _size, _name, _flags, IORES_DESC_NONE)
|
||||
#define DEFINE_RES(_start, _size, _flags) \
|
||||
DEFINE_RES_NAMED(_start, _size, NULL, _flags)
|
||||
|
||||
#define DEFINE_RES_IO_NAMED(_start, _size, _name) \
|
||||
DEFINE_RES_NAMED((_start), (_size), (_name), IORESOURCE_IO)
|
||||
#define DEFINE_RES_IO(_start, _size) \
|
||||
|
@ -203,6 +203,15 @@ static inline int arch_kimage_file_post_load_cleanup(struct kimage *image)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef arch_check_excluded_range
|
||||
static inline int arch_check_excluded_range(struct kimage *image,
|
||||
unsigned long start,
|
||||
unsigned long end)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_KEXEC_SIG
|
||||
#ifdef CONFIG_SIGNED_PE_FILE_VERIFICATION
|
||||
int kexec_kernel_verify_pe_sig(const char *kernel, unsigned long kernel_len);
|
||||
|
@ -28,6 +28,7 @@ struct hlist_nulls_node {
|
||||
#define NULLS_MARKER(value) (1UL | (((long)value) << 1))
|
||||
#define INIT_HLIST_NULLS_HEAD(ptr, nulls) \
|
||||
((ptr)->first = (struct hlist_nulls_node *) NULLS_MARKER(nulls))
|
||||
#define HLIST_NULLS_HEAD_INIT(nulls) {.first = (struct hlist_nulls_node *)NULLS_MARKER(nulls)}
|
||||
|
||||
#define hlist_nulls_entry(ptr, type, member) container_of(ptr,type,member)
|
||||
|
||||
|
@ -218,7 +218,7 @@ static size_t parent(size_t i, unsigned int lsbit, size_t size)
|
||||
|
||||
/* Initialize a min-heap. */
|
||||
static __always_inline
|
||||
void __min_heap_init_inline(min_heap_char *heap, void *data, int size)
|
||||
void __min_heap_init_inline(min_heap_char *heap, void *data, size_t size)
|
||||
{
|
||||
heap->nr = 0;
|
||||
heap->size = size;
|
||||
@ -254,7 +254,7 @@ bool __min_heap_full_inline(min_heap_char *heap)
|
||||
|
||||
/* Sift the element at pos down the heap. */
|
||||
static __always_inline
|
||||
void __min_heap_sift_down_inline(min_heap_char *heap, int pos, size_t elem_size,
|
||||
void __min_heap_sift_down_inline(min_heap_char *heap, size_t pos, size_t elem_size,
|
||||
const struct min_heap_callbacks *func, void *args)
|
||||
{
|
||||
const unsigned long lsbit = elem_size & -elem_size;
|
||||
@ -324,7 +324,7 @@ static __always_inline
|
||||
void __min_heapify_all_inline(min_heap_char *heap, size_t elem_size,
|
||||
const struct min_heap_callbacks *func, void *args)
|
||||
{
|
||||
int i;
|
||||
ssize_t i;
|
||||
|
||||
for (i = heap->nr / 2 - 1; i >= 0; i--)
|
||||
__min_heap_sift_down_inline(heap, i, elem_size, func, args);
|
||||
@ -379,7 +379,7 @@ bool __min_heap_push_inline(min_heap_char *heap, const void *element, size_t ele
|
||||
const struct min_heap_callbacks *func, void *args)
|
||||
{
|
||||
void *data = heap->data;
|
||||
int pos;
|
||||
size_t pos;
|
||||
|
||||
if (WARN_ONCE(heap->nr >= heap->size, "Pushing on a full heap"))
|
||||
return false;
|
||||
@ -428,10 +428,10 @@ bool __min_heap_del_inline(min_heap_char *heap, size_t elem_size, size_t idx,
|
||||
__min_heap_del_inline(container_of(&(_heap)->nr, min_heap_char, nr), \
|
||||
__minheap_obj_size(_heap), _idx, _func, _args)
|
||||
|
||||
void __min_heap_init(min_heap_char *heap, void *data, int size);
|
||||
void __min_heap_init(min_heap_char *heap, void *data, size_t size);
|
||||
void *__min_heap_peek(struct min_heap_char *heap);
|
||||
bool __min_heap_full(min_heap_char *heap);
|
||||
void __min_heap_sift_down(min_heap_char *heap, int pos, size_t elem_size,
|
||||
void __min_heap_sift_down(min_heap_char *heap, size_t pos, size_t elem_size,
|
||||
const struct min_heap_callbacks *func, void *args);
|
||||
void __min_heap_sift_up(min_heap_char *heap, size_t elem_size, size_t idx,
|
||||
const struct min_heap_callbacks *func, void *args);
|
||||
|
@ -202,4 +202,6 @@ DEFINE_GUARD(mutex, struct mutex *, mutex_lock(_T), mutex_unlock(_T))
|
||||
DEFINE_GUARD_COND(mutex, _try, mutex_trylock(_T))
|
||||
DEFINE_GUARD_COND(mutex, _intr, mutex_lock_interruptible(_T) == 0)
|
||||
|
||||
extern unsigned long mutex_get_owner(struct mutex *lock);
|
||||
|
||||
#endif /* __LINUX_MUTEX_H */
|
||||
|
@ -177,16 +177,38 @@ void ctrl_alt_del(void);
|
||||
|
||||
extern void orderly_poweroff(bool force);
|
||||
extern void orderly_reboot(void);
|
||||
void __hw_protection_shutdown(const char *reason, int ms_until_forced, bool shutdown);
|
||||
|
||||
static inline void hw_protection_reboot(const char *reason, int ms_until_forced)
|
||||
{
|
||||
__hw_protection_shutdown(reason, ms_until_forced, false);
|
||||
}
|
||||
/**
|
||||
* enum hw_protection_action - Hardware protection action
|
||||
*
|
||||
* @HWPROT_ACT_DEFAULT:
|
||||
* The default action should be taken. This is HWPROT_ACT_SHUTDOWN
|
||||
* by default, but can be overridden.
|
||||
* @HWPROT_ACT_SHUTDOWN:
|
||||
* The system should be shut down (powered off) for HW protection.
|
||||
* @HWPROT_ACT_REBOOT:
|
||||
* The system should be rebooted for HW protection.
|
||||
*/
|
||||
enum hw_protection_action { HWPROT_ACT_DEFAULT, HWPROT_ACT_SHUTDOWN, HWPROT_ACT_REBOOT };
|
||||
|
||||
static inline void hw_protection_shutdown(const char *reason, int ms_until_forced)
|
||||
void __hw_protection_trigger(const char *reason, int ms_until_forced,
|
||||
enum hw_protection_action action);
|
||||
|
||||
/**
|
||||
* hw_protection_trigger - Trigger default emergency system hardware protection action
|
||||
*
|
||||
* @reason: Reason of emergency shutdown or reboot to be printed.
|
||||
* @ms_until_forced: Time to wait for orderly shutdown or reboot before
|
||||
* triggering it. Negative value disables the forced
|
||||
* shutdown or reboot.
|
||||
*
|
||||
* Initiate an emergency system shutdown or reboot in order to protect
|
||||
* hardware from further damage. The exact action taken is controllable at
|
||||
* runtime and defaults to shutdown.
|
||||
*/
|
||||
static inline void hw_protection_trigger(const char *reason, int ms_until_forced)
|
||||
{
|
||||
__hw_protection_shutdown(reason, ms_until_forced, true);
|
||||
__hw_protection_trigger(reason, ms_until_forced, HWPROT_ACT_DEFAULT);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1259,7 +1259,7 @@ static inline int rhashtable_replace_fast(
|
||||
static inline void rhltable_walk_enter(struct rhltable *hlt,
|
||||
struct rhashtable_iter *iter)
|
||||
{
|
||||
return rhashtable_walk_enter(&hlt->ht, iter);
|
||||
rhashtable_walk_enter(&hlt->ht, iter);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -1275,12 +1275,12 @@ static inline void rhltable_free_and_destroy(struct rhltable *hlt,
|
||||
void *arg),
|
||||
void *arg)
|
||||
{
|
||||
return rhashtable_free_and_destroy(&hlt->ht, free_fn, arg);
|
||||
rhashtable_free_and_destroy(&hlt->ht, free_fn, arg);
|
||||
}
|
||||
|
||||
static inline void rhltable_destroy(struct rhltable *hlt)
|
||||
{
|
||||
return rhltable_free_and_destroy(hlt, NULL, NULL);
|
||||
rhltable_free_and_destroy(hlt, NULL, NULL);
|
||||
}
|
||||
|
||||
#endif /* _LINUX_RHASHTABLE_H */
|
||||
|
@ -1239,6 +1239,10 @@ struct task_struct {
|
||||
struct mutex_waiter *blocked_on;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DETECT_HUNG_TASK_BLOCKER
|
||||
struct mutex *blocker_mutex;
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_DEBUG_ATOMIC_SLEEP
|
||||
int non_block_count;
|
||||
#endif
|
||||
|
@ -92,6 +92,7 @@ typedef unsigned char unchar;
|
||||
typedef unsigned short ushort;
|
||||
typedef unsigned int uint;
|
||||
typedef unsigned long ulong;
|
||||
typedef unsigned long long ullong;
|
||||
|
||||
#ifndef __BIT_TYPES_DEFINED__
|
||||
#define __BIT_TYPES_DEFINED__
|
||||
|
@ -5,8 +5,10 @@
|
||||
#include <linux/kref.h>
|
||||
#include <linux/nsproxy.h>
|
||||
#include <linux/ns_common.h>
|
||||
#include <linux/rculist_nulls.h>
|
||||
#include <linux/sched.h>
|
||||
#include <linux/workqueue.h>
|
||||
#include <linux/rcuref.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/sysctl.h>
|
||||
#include <linux/err.h>
|
||||
@ -115,10 +117,11 @@ struct user_namespace {
|
||||
} __randomize_layout;
|
||||
|
||||
struct ucounts {
|
||||
struct hlist_node node;
|
||||
struct hlist_nulls_node node;
|
||||
struct user_namespace *ns;
|
||||
kuid_t uid;
|
||||
atomic_t count;
|
||||
struct rcu_head rcu;
|
||||
rcuref_t count;
|
||||
atomic_long_t ucount[UCOUNT_COUNTS];
|
||||
atomic_long_t rlimit[UCOUNT_RLIMIT_COUNTS];
|
||||
};
|
||||
@ -131,9 +134,15 @@ void retire_userns_sysctls(struct user_namespace *ns);
|
||||
struct ucounts *inc_ucount(struct user_namespace *ns, kuid_t uid, enum ucount_type type);
|
||||
void dec_ucount(struct ucounts *ucounts, enum ucount_type type);
|
||||
struct ucounts *alloc_ucounts(struct user_namespace *ns, kuid_t uid);
|
||||
struct ucounts * __must_check get_ucounts(struct ucounts *ucounts);
|
||||
void put_ucounts(struct ucounts *ucounts);
|
||||
|
||||
static inline struct ucounts * __must_check get_ucounts(struct ucounts *ucounts)
|
||||
{
|
||||
if (rcuref_get(&ucounts->count))
|
||||
return ucounts;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline long get_rlimit_value(struct ucounts *ucounts, enum rlimit_type type)
|
||||
{
|
||||
return atomic_long_read(&ucounts->rlimit[type]);
|
||||
|
@ -275,6 +275,7 @@ struct vfs_ns_cap_data {
|
||||
/* Allow setting encryption key on loopback filesystem */
|
||||
/* Allow setting zone reclaim policy */
|
||||
/* Allow everything under CAP_BPF and CAP_PERFMON for backward compatibility */
|
||||
/* Allow setting hardware protection emergency action */
|
||||
|
||||
#define CAP_SYS_ADMIN 21
|
||||
|
||||
|
@ -375,11 +375,10 @@ static int __init reserve_crashkernel_low(unsigned long long low_size)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void __init reserve_crashkernel_generic(char *cmdline,
|
||||
unsigned long long crash_size,
|
||||
unsigned long long crash_base,
|
||||
unsigned long long crash_low_size,
|
||||
bool high)
|
||||
void __init reserve_crashkernel_generic(unsigned long long crash_size,
|
||||
unsigned long long crash_base,
|
||||
unsigned long long crash_low_size,
|
||||
bool high)
|
||||
{
|
||||
unsigned long long search_end = CRASH_ADDR_LOW_MAX, search_base = 0;
|
||||
bool fixed_base = false;
|
||||
|
@ -1582,6 +1582,17 @@ struct mm_struct *get_task_mm(struct task_struct *task)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(get_task_mm);
|
||||
|
||||
static bool may_access_mm(struct mm_struct *mm, struct task_struct *task, unsigned int mode)
|
||||
{
|
||||
if (mm == current->mm)
|
||||
return true;
|
||||
if (ptrace_may_access(task, mode))
|
||||
return true;
|
||||
if ((mode & PTRACE_MODE_READ) && perfmon_capable())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
struct mm_struct *mm_access(struct task_struct *task, unsigned int mode)
|
||||
{
|
||||
struct mm_struct *mm;
|
||||
@ -1594,7 +1605,7 @@ struct mm_struct *mm_access(struct task_struct *task, unsigned int mode)
|
||||
mm = get_task_mm(task);
|
||||
if (!mm) {
|
||||
mm = ERR_PTR(-ESRCH);
|
||||
} else if (mm != current->mm && !ptrace_may_access(task, mode)) {
|
||||
} else if (!may_access_mm(mm, task, mode)) {
|
||||
mmput(mm);
|
||||
mm = ERR_PTR(-EACCES);
|
||||
}
|
||||
|
@ -93,6 +93,43 @@ static struct notifier_block panic_block = {
|
||||
.notifier_call = hung_task_panic,
|
||||
};
|
||||
|
||||
|
||||
#ifdef CONFIG_DETECT_HUNG_TASK_BLOCKER
|
||||
static void debug_show_blocker(struct task_struct *task)
|
||||
{
|
||||
struct task_struct *g, *t;
|
||||
unsigned long owner;
|
||||
struct mutex *lock;
|
||||
|
||||
RCU_LOCKDEP_WARN(!rcu_read_lock_held(), "No rcu lock held");
|
||||
|
||||
lock = READ_ONCE(task->blocker_mutex);
|
||||
if (!lock)
|
||||
return;
|
||||
|
||||
owner = mutex_get_owner(lock);
|
||||
if (unlikely(!owner)) {
|
||||
pr_err("INFO: task %s:%d is blocked on a mutex, but the owner is not found.\n",
|
||||
task->comm, task->pid);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ensure the owner information is correct. */
|
||||
for_each_process_thread(g, t) {
|
||||
if ((unsigned long)t == owner) {
|
||||
pr_err("INFO: task %s:%d is blocked on a mutex likely owned by task %s:%d.\n",
|
||||
task->comm, task->pid, t->comm, t->pid);
|
||||
sched_show_task(t);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void debug_show_blocker(struct task_struct *task)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static void check_hung_task(struct task_struct *t, unsigned long timeout)
|
||||
{
|
||||
unsigned long switch_count = t->nvcsw + t->nivcsw;
|
||||
@ -152,6 +189,7 @@ static void check_hung_task(struct task_struct *t, unsigned long timeout)
|
||||
pr_err("\"echo 0 > /proc/sys/kernel/hung_task_timeout_secs\""
|
||||
" disables this message.\n");
|
||||
sched_show_task(t);
|
||||
debug_show_blocker(t);
|
||||
hung_task_show_lock = true;
|
||||
|
||||
if (sysctl_hung_task_all_cpu_backtrace)
|
||||
|
@ -210,6 +210,16 @@ int sanity_check_segment_list(struct kimage *image)
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* The destination addresses are searched from system RAM rather than
|
||||
* being allocated from the buddy allocator, so they are not guaranteed
|
||||
* to be accepted by the current kernel. Accept the destination
|
||||
* addresses before kexec swaps their content with the segments' source
|
||||
* pages to avoid accessing memory before it is accepted.
|
||||
*/
|
||||
for (i = 0; i < nr_segments; i++)
|
||||
accept_memory(image->segment[i].mem, image->segment[i].memsz);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -390,7 +390,7 @@ int kexec_elf_load(struct kimage *image, struct elfhdr *ehdr,
|
||||
struct kexec_buf *kbuf,
|
||||
unsigned long *lowest_load_addr)
|
||||
{
|
||||
unsigned long lowest_addr = UINT_MAX;
|
||||
unsigned long lowest_addr = ULONG_MAX;
|
||||
int ret;
|
||||
size_t i;
|
||||
|
||||
|
@ -464,6 +464,12 @@ static int locate_mem_hole_top_down(unsigned long start, unsigned long end,
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Make sure this does not conflict with exclude range */
|
||||
if (arch_check_excluded_range(image, temp_start, temp_end)) {
|
||||
temp_start = temp_start - PAGE_SIZE;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We found a suitable memory range */
|
||||
break;
|
||||
} while (1);
|
||||
@ -498,6 +504,12 @@ static int locate_mem_hole_bottom_up(unsigned long start, unsigned long end,
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Make sure this does not conflict with exclude range */
|
||||
if (arch_check_excluded_range(image, temp_start, temp_end)) {
|
||||
temp_start = temp_start + PAGE_SIZE;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* We found a suitable memory range */
|
||||
break;
|
||||
} while (1);
|
||||
|
@ -72,6 +72,14 @@ static inline unsigned long __owner_flags(unsigned long owner)
|
||||
return owner & MUTEX_FLAGS;
|
||||
}
|
||||
|
||||
/* Do not use the return value as a pointer directly. */
|
||||
unsigned long mutex_get_owner(struct mutex *lock)
|
||||
{
|
||||
unsigned long owner = atomic_long_read(&lock->owner);
|
||||
|
||||
return (unsigned long)__owner_task(owner);
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns: __mutex_owner(lock) on failure or NULL on success.
|
||||
*/
|
||||
@ -182,6 +190,9 @@ static void
|
||||
__mutex_add_waiter(struct mutex *lock, struct mutex_waiter *waiter,
|
||||
struct list_head *list)
|
||||
{
|
||||
#ifdef CONFIG_DETECT_HUNG_TASK_BLOCKER
|
||||
WRITE_ONCE(current->blocker_mutex, lock);
|
||||
#endif
|
||||
debug_mutex_add_waiter(lock, waiter, current);
|
||||
|
||||
list_add_tail(&waiter->list, list);
|
||||
@ -197,6 +208,9 @@ __mutex_remove_waiter(struct mutex *lock, struct mutex_waiter *waiter)
|
||||
__mutex_clear_flag(lock, MUTEX_FLAGS);
|
||||
|
||||
debug_mutex_remove_waiter(lock, waiter, current);
|
||||
#ifdef CONFIG_DETECT_HUNG_TASK_BLOCKER
|
||||
WRITE_ONCE(current->blocker_mutex, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
|
142
kernel/reboot.c
142
kernel/reboot.c
@ -36,6 +36,8 @@ enum reboot_mode reboot_mode DEFAULT_REBOOT_MODE;
|
||||
EXPORT_SYMBOL_GPL(reboot_mode);
|
||||
enum reboot_mode panic_reboot_mode = REBOOT_UNDEFINED;
|
||||
|
||||
static enum hw_protection_action hw_protection_action = HWPROT_ACT_SHUTDOWN;
|
||||
|
||||
/*
|
||||
* This variable is used privately to keep track of whether or not
|
||||
* reboot_type is still set to its default value (i.e., reboot= hasn't
|
||||
@ -229,6 +231,9 @@ EXPORT_SYMBOL(unregister_restart_handler);
|
||||
/**
|
||||
* do_kernel_restart - Execute kernel restart handler call chain
|
||||
*
|
||||
* @cmd: pointer to buffer containing command to execute for restart
|
||||
* or %NULL
|
||||
*
|
||||
* Calls functions registered with register_restart_handler.
|
||||
*
|
||||
* Expected to be called from machine_restart as last step of the restart
|
||||
@ -933,61 +938,86 @@ void orderly_reboot(void)
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(orderly_reboot);
|
||||
|
||||
static const char *hw_protection_action_str(enum hw_protection_action action)
|
||||
{
|
||||
switch (action) {
|
||||
case HWPROT_ACT_SHUTDOWN:
|
||||
return "shutdown";
|
||||
case HWPROT_ACT_REBOOT:
|
||||
return "reboot";
|
||||
default:
|
||||
return "undefined";
|
||||
}
|
||||
}
|
||||
|
||||
static enum hw_protection_action hw_failure_emergency_action;
|
||||
|
||||
/**
|
||||
* hw_failure_emergency_poweroff_func - emergency poweroff work after a known delay
|
||||
* @work: work_struct associated with the emergency poweroff function
|
||||
* hw_failure_emergency_action_func - emergency action work after a known delay
|
||||
* @work: work_struct associated with the emergency action function
|
||||
*
|
||||
* This function is called in very critical situations to force
|
||||
* a kernel poweroff after a configurable timeout value.
|
||||
* a kernel poweroff or reboot after a configurable timeout value.
|
||||
*/
|
||||
static void hw_failure_emergency_poweroff_func(struct work_struct *work)
|
||||
static void hw_failure_emergency_action_func(struct work_struct *work)
|
||||
{
|
||||
const char *action_str = hw_protection_action_str(hw_failure_emergency_action);
|
||||
|
||||
pr_emerg("Hardware protection timed-out. Trying forced %s\n",
|
||||
action_str);
|
||||
|
||||
/*
|
||||
* We have reached here after the emergency shutdown waiting period has
|
||||
* expired. This means orderly_poweroff has not been able to shut off
|
||||
* the system for some reason.
|
||||
* We have reached here after the emergency action waiting period has
|
||||
* expired. This means orderly_poweroff/reboot has not been able to
|
||||
* shut off the system for some reason.
|
||||
*
|
||||
* Try to shut down the system immediately using kernel_power_off
|
||||
* if populated
|
||||
* Try to shut off the system immediately if possible
|
||||
*/
|
||||
pr_emerg("Hardware protection timed-out. Trying forced poweroff\n");
|
||||
kernel_power_off();
|
||||
|
||||
if (hw_failure_emergency_action == HWPROT_ACT_REBOOT)
|
||||
kernel_restart(NULL);
|
||||
else
|
||||
kernel_power_off();
|
||||
|
||||
/*
|
||||
* Worst of the worst case trigger emergency restart
|
||||
*/
|
||||
pr_emerg("Hardware protection shutdown failed. Trying emergency restart\n");
|
||||
pr_emerg("Hardware protection %s failed. Trying emergency restart\n",
|
||||
action_str);
|
||||
emergency_restart();
|
||||
}
|
||||
|
||||
static DECLARE_DELAYED_WORK(hw_failure_emergency_poweroff_work,
|
||||
hw_failure_emergency_poweroff_func);
|
||||
static DECLARE_DELAYED_WORK(hw_failure_emergency_action_work,
|
||||
hw_failure_emergency_action_func);
|
||||
|
||||
/**
|
||||
* hw_failure_emergency_poweroff - Trigger an emergency system poweroff
|
||||
* hw_failure_emergency_schedule - Schedule an emergency system shutdown or reboot
|
||||
*
|
||||
* @action: The hardware protection action to be taken
|
||||
* @action_delay_ms: Time in milliseconds to elapse before triggering action
|
||||
*
|
||||
* This may be called from any critical situation to trigger a system shutdown
|
||||
* after a given period of time. If time is negative this is not scheduled.
|
||||
* or reboot after a given period of time.
|
||||
* If time is negative this is not scheduled.
|
||||
*/
|
||||
static void hw_failure_emergency_poweroff(int poweroff_delay_ms)
|
||||
static void hw_failure_emergency_schedule(enum hw_protection_action action,
|
||||
int action_delay_ms)
|
||||
{
|
||||
if (poweroff_delay_ms <= 0)
|
||||
if (action_delay_ms <= 0)
|
||||
return;
|
||||
schedule_delayed_work(&hw_failure_emergency_poweroff_work,
|
||||
msecs_to_jiffies(poweroff_delay_ms));
|
||||
hw_failure_emergency_action = action;
|
||||
schedule_delayed_work(&hw_failure_emergency_action_work,
|
||||
msecs_to_jiffies(action_delay_ms));
|
||||
}
|
||||
|
||||
/**
|
||||
* __hw_protection_shutdown - Trigger an emergency system shutdown or reboot
|
||||
* __hw_protection_trigger - Trigger an emergency system shutdown or reboot
|
||||
*
|
||||
* @reason: Reason of emergency shutdown or reboot to be printed.
|
||||
* @ms_until_forced: Time to wait for orderly shutdown or reboot before
|
||||
* triggering it. Negative value disables the forced
|
||||
* shutdown or reboot.
|
||||
* @shutdown: If true, indicates that a shutdown will happen
|
||||
* after the critical tempeature is reached.
|
||||
* If false, indicates that a reboot will happen
|
||||
* after the critical tempeature is reached.
|
||||
* @action: The hardware protection action to be taken.
|
||||
*
|
||||
* Initiate an emergency system shutdown or reboot in order to protect
|
||||
* hardware from further damage. Usage examples include a thermal protection.
|
||||
@ -995,11 +1025,16 @@ static void hw_failure_emergency_poweroff(int poweroff_delay_ms)
|
||||
* pending even if the previous request has given a large timeout for forced
|
||||
* shutdown/reboot.
|
||||
*/
|
||||
void __hw_protection_shutdown(const char *reason, int ms_until_forced, bool shutdown)
|
||||
void __hw_protection_trigger(const char *reason, int ms_until_forced,
|
||||
enum hw_protection_action action)
|
||||
{
|
||||
static atomic_t allow_proceed = ATOMIC_INIT(1);
|
||||
|
||||
pr_emerg("HARDWARE PROTECTION shutdown (%s)\n", reason);
|
||||
if (action == HWPROT_ACT_DEFAULT)
|
||||
action = hw_protection_action;
|
||||
|
||||
pr_emerg("HARDWARE PROTECTION %s (%s)\n",
|
||||
hw_protection_action_str(action), reason);
|
||||
|
||||
/* Shutdown should be initiated only once. */
|
||||
if (!atomic_dec_and_test(&allow_proceed))
|
||||
@ -1009,13 +1044,55 @@ void __hw_protection_shutdown(const char *reason, int ms_until_forced, bool shut
|
||||
* Queue a backup emergency shutdown in the event of
|
||||
* orderly_poweroff failure
|
||||
*/
|
||||
hw_failure_emergency_poweroff(ms_until_forced);
|
||||
if (shutdown)
|
||||
orderly_poweroff(true);
|
||||
else
|
||||
hw_failure_emergency_schedule(action, ms_until_forced);
|
||||
if (action == HWPROT_ACT_REBOOT)
|
||||
orderly_reboot();
|
||||
else
|
||||
orderly_poweroff(true);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__hw_protection_shutdown);
|
||||
EXPORT_SYMBOL_GPL(__hw_protection_trigger);
|
||||
|
||||
static bool hw_protection_action_parse(const char *str,
|
||||
enum hw_protection_action *action)
|
||||
{
|
||||
if (sysfs_streq(str, "shutdown"))
|
||||
*action = HWPROT_ACT_SHUTDOWN;
|
||||
else if (sysfs_streq(str, "reboot"))
|
||||
*action = HWPROT_ACT_REBOOT;
|
||||
else
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static int __init hw_protection_setup(char *str)
|
||||
{
|
||||
hw_protection_action_parse(str, &hw_protection_action);
|
||||
return 1;
|
||||
}
|
||||
__setup("hw_protection=", hw_protection_setup);
|
||||
|
||||
#ifdef CONFIG_SYSFS
|
||||
static ssize_t hw_protection_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, char *buf)
|
||||
{
|
||||
return sysfs_emit(buf, "%s\n",
|
||||
hw_protection_action_str(hw_protection_action));
|
||||
}
|
||||
static ssize_t hw_protection_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr, const char *buf,
|
||||
size_t count)
|
||||
{
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (!hw_protection_action_parse(buf, &hw_protection_action))
|
||||
return -EINVAL;
|
||||
|
||||
return count;
|
||||
}
|
||||
static struct kobj_attribute hw_protection_attr = __ATTR_RW(hw_protection);
|
||||
#endif
|
||||
|
||||
static int __init reboot_setup(char *str)
|
||||
{
|
||||
@ -1276,6 +1353,7 @@ static struct kobj_attribute reboot_cpu_attr = __ATTR_RW(cpu);
|
||||
#endif
|
||||
|
||||
static struct attribute *reboot_attrs[] = {
|
||||
&hw_protection_attr.attr,
|
||||
&reboot_mode_attr.attr,
|
||||
#ifdef CONFIG_X86
|
||||
&reboot_force_attr.attr,
|
||||
|
@ -351,10 +351,9 @@ static struct dentry *relay_create_buf_file(struct rchan *chan,
|
||||
struct dentry *dentry;
|
||||
char *tmpname;
|
||||
|
||||
tmpname = kzalloc(NAME_MAX + 1, GFP_KERNEL);
|
||||
tmpname = kasprintf(GFP_KERNEL, "%s%d", chan->base_filename, cpu);
|
||||
if (!tmpname)
|
||||
return NULL;
|
||||
snprintf(tmpname, NAME_MAX, "%s%d", chan->base_filename, cpu);
|
||||
|
||||
/* Create file in fs */
|
||||
dentry = chan->cb->create_buf_file(tmpname, chan->parent,
|
||||
|
@ -561,8 +561,7 @@ static int __region_intersects(struct resource *parent, resource_size_t start,
|
||||
struct resource res, o;
|
||||
bool covered;
|
||||
|
||||
res.start = start;
|
||||
res.end = start + size - 1;
|
||||
res = DEFINE_RES(start, size, 0);
|
||||
|
||||
for (p = parent->child; p ; p = p->sibling) {
|
||||
if (!resource_intersection(p, &res, &o))
|
||||
@ -1714,18 +1713,13 @@ static int __init reserve_setup(char *str)
|
||||
* I/O port space; otherwise assume it's memory.
|
||||
*/
|
||||
if (io_start < 0x10000) {
|
||||
res->flags = IORESOURCE_IO;
|
||||
*res = DEFINE_RES_IO_NAMED(io_start, io_num, "reserved");
|
||||
parent = &ioport_resource;
|
||||
} else {
|
||||
res->flags = IORESOURCE_MEM;
|
||||
*res = DEFINE_RES_MEM_NAMED(io_start, io_num, "reserved");
|
||||
parent = &iomem_resource;
|
||||
}
|
||||
res->name = "reserved";
|
||||
res->start = io_start;
|
||||
res->end = io_start + io_num - 1;
|
||||
res->flags |= IORESOURCE_BUSY;
|
||||
res->desc = IORES_DESC_NONE;
|
||||
res->child = NULL;
|
||||
if (request_resource(parent, res) == 0)
|
||||
reserved = x+1;
|
||||
}
|
||||
@ -1975,11 +1969,7 @@ get_free_mem_region(struct device *dev, struct resource *base,
|
||||
*/
|
||||
revoke_iomem(res);
|
||||
} else {
|
||||
res->start = addr;
|
||||
res->end = addr + size - 1;
|
||||
res->name = name;
|
||||
res->desc = desc;
|
||||
res->flags = IORESOURCE_MEM;
|
||||
*res = DEFINE_RES_NAMED_DESC(addr, size, name, IORESOURCE_MEM, desc);
|
||||
|
||||
/*
|
||||
* Only succeed if the resource hosts an exclusive
|
||||
|
@ -176,9 +176,10 @@ static bool recalc_sigpending_tsk(struct task_struct *t)
|
||||
|
||||
void recalc_sigpending(void)
|
||||
{
|
||||
if (!recalc_sigpending_tsk(current) && !freezing(current))
|
||||
clear_thread_flag(TIF_SIGPENDING);
|
||||
|
||||
if (!recalc_sigpending_tsk(current) && !freezing(current)) {
|
||||
if (unlikely(test_thread_flag(TIF_SIGPENDING)))
|
||||
clear_thread_flag(TIF_SIGPENDING);
|
||||
}
|
||||
}
|
||||
EXPORT_SYMBOL(recalc_sigpending);
|
||||
|
||||
|
@ -11,11 +11,14 @@
|
||||
struct ucounts init_ucounts = {
|
||||
.ns = &init_user_ns,
|
||||
.uid = GLOBAL_ROOT_UID,
|
||||
.count = ATOMIC_INIT(1),
|
||||
.count = RCUREF_INIT(1),
|
||||
};
|
||||
|
||||
#define UCOUNTS_HASHTABLE_BITS 10
|
||||
static struct hlist_head ucounts_hashtable[(1 << UCOUNTS_HASHTABLE_BITS)];
|
||||
#define UCOUNTS_HASHTABLE_ENTRIES (1 << UCOUNTS_HASHTABLE_BITS)
|
||||
static struct hlist_nulls_head ucounts_hashtable[UCOUNTS_HASHTABLE_ENTRIES] = {
|
||||
[0 ... UCOUNTS_HASHTABLE_ENTRIES - 1] = HLIST_NULLS_HEAD_INIT(0)
|
||||
};
|
||||
static DEFINE_SPINLOCK(ucounts_lock);
|
||||
|
||||
#define ucounts_hashfn(ns, uid) \
|
||||
@ -24,7 +27,6 @@ static DEFINE_SPINLOCK(ucounts_lock);
|
||||
#define ucounts_hashentry(ns, uid) \
|
||||
(ucounts_hashtable + ucounts_hashfn(ns, uid))
|
||||
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
static struct ctl_table_set *
|
||||
set_lookup(struct ctl_table_root *root)
|
||||
@ -127,88 +129,73 @@ void retire_userns_sysctls(struct user_namespace *ns)
|
||||
#endif
|
||||
}
|
||||
|
||||
static struct ucounts *find_ucounts(struct user_namespace *ns, kuid_t uid, struct hlist_head *hashent)
|
||||
static struct ucounts *find_ucounts(struct user_namespace *ns, kuid_t uid,
|
||||
struct hlist_nulls_head *hashent)
|
||||
{
|
||||
struct ucounts *ucounts;
|
||||
struct hlist_nulls_node *pos;
|
||||
|
||||
hlist_for_each_entry(ucounts, hashent, node) {
|
||||
if (uid_eq(ucounts->uid, uid) && (ucounts->ns == ns))
|
||||
return ucounts;
|
||||
guard(rcu)();
|
||||
hlist_nulls_for_each_entry_rcu(ucounts, pos, hashent, node) {
|
||||
if (uid_eq(ucounts->uid, uid) && (ucounts->ns == ns)) {
|
||||
if (rcuref_get(&ucounts->count))
|
||||
return ucounts;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void hlist_add_ucounts(struct ucounts *ucounts)
|
||||
{
|
||||
struct hlist_head *hashent = ucounts_hashentry(ucounts->ns, ucounts->uid);
|
||||
struct hlist_nulls_head *hashent = ucounts_hashentry(ucounts->ns, ucounts->uid);
|
||||
|
||||
spin_lock_irq(&ucounts_lock);
|
||||
hlist_add_head(&ucounts->node, hashent);
|
||||
hlist_nulls_add_head_rcu(&ucounts->node, hashent);
|
||||
spin_unlock_irq(&ucounts_lock);
|
||||
}
|
||||
|
||||
static inline bool get_ucounts_or_wrap(struct ucounts *ucounts)
|
||||
{
|
||||
/* Returns true on a successful get, false if the count wraps. */
|
||||
return !atomic_add_negative(1, &ucounts->count);
|
||||
}
|
||||
|
||||
struct ucounts *get_ucounts(struct ucounts *ucounts)
|
||||
{
|
||||
if (!get_ucounts_or_wrap(ucounts)) {
|
||||
put_ucounts(ucounts);
|
||||
ucounts = NULL;
|
||||
}
|
||||
return ucounts;
|
||||
}
|
||||
|
||||
struct ucounts *alloc_ucounts(struct user_namespace *ns, kuid_t uid)
|
||||
{
|
||||
struct hlist_head *hashent = ucounts_hashentry(ns, uid);
|
||||
bool wrapped;
|
||||
struct ucounts *ucounts, *new = NULL;
|
||||
struct hlist_nulls_head *hashent = ucounts_hashentry(ns, uid);
|
||||
struct ucounts *ucounts, *new;
|
||||
|
||||
ucounts = find_ucounts(ns, uid, hashent);
|
||||
if (ucounts)
|
||||
return ucounts;
|
||||
|
||||
new = kzalloc(sizeof(*new), GFP_KERNEL);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
new->ns = ns;
|
||||
new->uid = uid;
|
||||
rcuref_init(&new->count, 1);
|
||||
|
||||
spin_lock_irq(&ucounts_lock);
|
||||
ucounts = find_ucounts(ns, uid, hashent);
|
||||
if (!ucounts) {
|
||||
if (ucounts) {
|
||||
spin_unlock_irq(&ucounts_lock);
|
||||
|
||||
new = kzalloc(sizeof(*new), GFP_KERNEL);
|
||||
if (!new)
|
||||
return NULL;
|
||||
|
||||
new->ns = ns;
|
||||
new->uid = uid;
|
||||
atomic_set(&new->count, 1);
|
||||
|
||||
spin_lock_irq(&ucounts_lock);
|
||||
ucounts = find_ucounts(ns, uid, hashent);
|
||||
if (!ucounts) {
|
||||
hlist_add_head(&new->node, hashent);
|
||||
get_user_ns(new->ns);
|
||||
spin_unlock_irq(&ucounts_lock);
|
||||
return new;
|
||||
}
|
||||
kfree(new);
|
||||
return ucounts;
|
||||
}
|
||||
|
||||
wrapped = !get_ucounts_or_wrap(ucounts);
|
||||
hlist_nulls_add_head_rcu(&new->node, hashent);
|
||||
get_user_ns(new->ns);
|
||||
spin_unlock_irq(&ucounts_lock);
|
||||
kfree(new);
|
||||
if (wrapped) {
|
||||
put_ucounts(ucounts);
|
||||
return NULL;
|
||||
}
|
||||
return ucounts;
|
||||
return new;
|
||||
}
|
||||
|
||||
void put_ucounts(struct ucounts *ucounts)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
if (atomic_dec_and_lock_irqsave(&ucounts->count, &ucounts_lock, flags)) {
|
||||
hlist_del_init(&ucounts->node);
|
||||
if (rcuref_put(&ucounts->count)) {
|
||||
spin_lock_irqsave(&ucounts_lock, flags);
|
||||
hlist_nulls_del_rcu(&ucounts->node);
|
||||
spin_unlock_irqrestore(&ucounts_lock, flags);
|
||||
|
||||
put_user_ns(ucounts->ns);
|
||||
kfree(ucounts);
|
||||
kfree_rcu(ucounts, rcu);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -269,12 +269,10 @@ void __init hardlockup_config_perf_event(const char *str)
|
||||
} else {
|
||||
unsigned int len = comma - str;
|
||||
|
||||
if (len >= sizeof(buf))
|
||||
if (len > sizeof(buf))
|
||||
return;
|
||||
|
||||
if (strscpy(buf, str, sizeof(buf)) < 0)
|
||||
return;
|
||||
buf[len] = 0;
|
||||
strscpy(buf, str, len);
|
||||
if (kstrtoull(buf, 16, &config))
|
||||
return;
|
||||
}
|
||||
|
@ -1280,6 +1280,17 @@ config BOOTPARAM_HUNG_TASK_PANIC
|
||||
|
||||
Say N if unsure.
|
||||
|
||||
config DETECT_HUNG_TASK_BLOCKER
|
||||
bool "Dump Hung Tasks Blocker"
|
||||
depends on DETECT_HUNG_TASK
|
||||
depends on !PREEMPT_RT
|
||||
default y
|
||||
help
|
||||
Say Y here to show the blocker task's stacktrace who acquires
|
||||
the mutex lock which "hung tasks" are waiting.
|
||||
This will add overhead a bit but shows suspicious tasks and
|
||||
call trace if it comes from waiting a mutex.
|
||||
|
||||
config WQ_WATCHDOG
|
||||
bool "Detect Workqueue Stalls"
|
||||
depends on DEBUG_KERNEL
|
||||
|
@ -20,9 +20,15 @@ EXPORT_SYMBOL_GPL(interval_tree_iter_next);
|
||||
/*
|
||||
* Roll nodes[1] into nodes[0] by advancing nodes[1] to the end of a contiguous
|
||||
* span of nodes. This makes nodes[0]->last the end of that contiguous used span
|
||||
* indexes that started at the original nodes[1]->start. nodes[1] is now the
|
||||
* first node starting the next used span. A hole span is between nodes[0]->last
|
||||
* and nodes[1]->start. nodes[1] must be !NULL.
|
||||
* of indexes that started at the original nodes[1]->start.
|
||||
*
|
||||
* If there is an interior hole, nodes[1] is now the first node starting the
|
||||
* next used span. A hole span is between nodes[0]->last and nodes[1]->start.
|
||||
*
|
||||
* If there is a tailing hole, nodes[1] is now NULL. A hole span is between
|
||||
* nodes[0]->last and last_index.
|
||||
*
|
||||
* If the contiguous used range span to last_index, nodes[1] is set to NULL.
|
||||
*/
|
||||
static void
|
||||
interval_tree_span_iter_next_gap(struct interval_tree_span_iter *state)
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include <linux/prandom.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/timex.h>
|
||||
#include <linux/bitmap.h>
|
||||
#include <linux/maple_tree.h>
|
||||
|
||||
#define __param(type, name, init, msg) \
|
||||
static type name = init; \
|
||||
@ -19,6 +21,7 @@ __param(int, search_loops, 1000, "Number of iterations searching the tree");
|
||||
__param(bool, search_all, false, "Searches will iterate all nodes in the tree");
|
||||
|
||||
__param(uint, max_endpoint, ~0, "Largest value for the interval's endpoint");
|
||||
__param(ullong, seed, 3141592653589793238ULL, "Random seed");
|
||||
|
||||
static struct rb_root_cached root = RB_ROOT_CACHED;
|
||||
static struct interval_tree_node *nodes = NULL;
|
||||
@ -59,26 +62,13 @@ static void init(void)
|
||||
queries[i] = (prandom_u32_state(&rnd) >> 4) % max_endpoint;
|
||||
}
|
||||
|
||||
static int interval_tree_test_init(void)
|
||||
static int basic_check(void)
|
||||
{
|
||||
int i, j;
|
||||
unsigned long results;
|
||||
cycles_t time1, time2, time;
|
||||
|
||||
nodes = kmalloc_array(nnodes, sizeof(struct interval_tree_node),
|
||||
GFP_KERNEL);
|
||||
if (!nodes)
|
||||
return -ENOMEM;
|
||||
|
||||
queries = kmalloc_array(nsearches, sizeof(int), GFP_KERNEL);
|
||||
if (!queries) {
|
||||
kfree(nodes);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
printk(KERN_ALERT "interval tree insert/remove");
|
||||
|
||||
prandom_seed_state(&rnd, 3141592653589793238ULL);
|
||||
init();
|
||||
|
||||
time1 = get_cycles();
|
||||
@ -96,8 +86,19 @@ static int interval_tree_test_init(void)
|
||||
time = div_u64(time, perf_loops);
|
||||
printk(" -> %llu cycles\n", (unsigned long long)time);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int search_check(void)
|
||||
{
|
||||
int i, j;
|
||||
unsigned long results;
|
||||
cycles_t time1, time2, time;
|
||||
|
||||
printk(KERN_ALERT "interval tree search");
|
||||
|
||||
init();
|
||||
|
||||
for (j = 0; j < nnodes; j++)
|
||||
interval_tree_insert(nodes + j, &root);
|
||||
|
||||
@ -120,6 +121,214 @@ static int interval_tree_test_init(void)
|
||||
printk(" -> %llu cycles (%lu results)\n",
|
||||
(unsigned long long)time, results);
|
||||
|
||||
for (j = 0; j < nnodes; j++)
|
||||
interval_tree_remove(nodes + j, &root);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int intersection_range_check(void)
|
||||
{
|
||||
int i, j, k;
|
||||
unsigned long start, last;
|
||||
struct interval_tree_node *node;
|
||||
unsigned long *intxn1;
|
||||
unsigned long *intxn2;
|
||||
|
||||
printk(KERN_ALERT "interval tree iteration\n");
|
||||
|
||||
intxn1 = bitmap_alloc(nnodes, GFP_KERNEL);
|
||||
if (!intxn1) {
|
||||
WARN_ON_ONCE("Failed to allocate intxn1\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
intxn2 = bitmap_alloc(nnodes, GFP_KERNEL);
|
||||
if (!intxn2) {
|
||||
WARN_ON_ONCE("Failed to allocate intxn2\n");
|
||||
bitmap_free(intxn1);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
for (i = 0; i < search_loops; i++) {
|
||||
/* Initialize interval tree for each round */
|
||||
init();
|
||||
for (j = 0; j < nnodes; j++)
|
||||
interval_tree_insert(nodes + j, &root);
|
||||
|
||||
/* Let's try nsearches different ranges */
|
||||
for (k = 0; k < nsearches; k++) {
|
||||
/* Try whole range once */
|
||||
if (!k) {
|
||||
start = 0UL;
|
||||
last = ULONG_MAX;
|
||||
} else {
|
||||
last = (prandom_u32_state(&rnd) >> 4) % max_endpoint;
|
||||
start = (prandom_u32_state(&rnd) >> 4) % last;
|
||||
}
|
||||
|
||||
/* Walk nodes to mark intersection nodes */
|
||||
bitmap_zero(intxn1, nnodes);
|
||||
for (j = 0; j < nnodes; j++) {
|
||||
node = nodes + j;
|
||||
|
||||
if (start <= node->last && last >= node->start)
|
||||
bitmap_set(intxn1, j, 1);
|
||||
}
|
||||
|
||||
/* Iterate tree to clear intersection nodes */
|
||||
bitmap_zero(intxn2, nnodes);
|
||||
for (node = interval_tree_iter_first(&root, start, last); node;
|
||||
node = interval_tree_iter_next(node, start, last))
|
||||
bitmap_set(intxn2, node - nodes, 1);
|
||||
|
||||
WARN_ON_ONCE(!bitmap_equal(intxn1, intxn2, nnodes));
|
||||
}
|
||||
|
||||
for (j = 0; j < nnodes; j++)
|
||||
interval_tree_remove(nodes + j, &root);
|
||||
}
|
||||
|
||||
bitmap_free(intxn1);
|
||||
bitmap_free(intxn2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_INTERVAL_TREE_SPAN_ITER
|
||||
/*
|
||||
* Helper function to get span of current position from maple tree point of
|
||||
* view.
|
||||
*/
|
||||
static void mas_cur_span(struct ma_state *mas, struct interval_tree_span_iter *state)
|
||||
{
|
||||
unsigned long cur_start;
|
||||
unsigned long cur_last;
|
||||
int is_hole;
|
||||
|
||||
if (mas->status == ma_overflow)
|
||||
return;
|
||||
|
||||
/* walk to current position */
|
||||
state->is_hole = mas_walk(mas) ? 0 : 1;
|
||||
|
||||
cur_start = mas->index < state->first_index ?
|
||||
state->first_index : mas->index;
|
||||
|
||||
/* whether we have followers */
|
||||
do {
|
||||
|
||||
cur_last = mas->last > state->last_index ?
|
||||
state->last_index : mas->last;
|
||||
|
||||
is_hole = mas_next_range(mas, state->last_index) ? 0 : 1;
|
||||
|
||||
} while (mas->status != ma_overflow && is_hole == state->is_hole);
|
||||
|
||||
if (state->is_hole) {
|
||||
state->start_hole = cur_start;
|
||||
state->last_hole = cur_last;
|
||||
} else {
|
||||
state->start_used = cur_start;
|
||||
state->last_used = cur_last;
|
||||
}
|
||||
|
||||
/* advance position for next round */
|
||||
if (mas->status != ma_overflow)
|
||||
mas_set(mas, cur_last + 1);
|
||||
}
|
||||
|
||||
static int span_iteration_check(void)
|
||||
{
|
||||
int i, j, k;
|
||||
unsigned long start, last;
|
||||
struct interval_tree_span_iter span, mas_span;
|
||||
|
||||
DEFINE_MTREE(tree);
|
||||
|
||||
MA_STATE(mas, &tree, 0, 0);
|
||||
|
||||
printk(KERN_ALERT "interval tree span iteration\n");
|
||||
|
||||
for (i = 0; i < search_loops; i++) {
|
||||
/* Initialize interval tree for each round */
|
||||
init();
|
||||
for (j = 0; j < nnodes; j++)
|
||||
interval_tree_insert(nodes + j, &root);
|
||||
|
||||
/* Put all the range into maple tree */
|
||||
mt_init_flags(&tree, MT_FLAGS_ALLOC_RANGE);
|
||||
mt_set_in_rcu(&tree);
|
||||
|
||||
for (j = 0; j < nnodes; j++)
|
||||
WARN_ON_ONCE(mtree_store_range(&tree, nodes[j].start,
|
||||
nodes[j].last, nodes + j, GFP_KERNEL));
|
||||
|
||||
/* Let's try nsearches different ranges */
|
||||
for (k = 0; k < nsearches; k++) {
|
||||
/* Try whole range once */
|
||||
if (!k) {
|
||||
start = 0UL;
|
||||
last = ULONG_MAX;
|
||||
} else {
|
||||
last = (prandom_u32_state(&rnd) >> 4) % max_endpoint;
|
||||
start = (prandom_u32_state(&rnd) >> 4) % last;
|
||||
}
|
||||
|
||||
mas_span.first_index = start;
|
||||
mas_span.last_index = last;
|
||||
mas_span.is_hole = -1;
|
||||
mas_set(&mas, start);
|
||||
|
||||
interval_tree_for_each_span(&span, &root, start, last) {
|
||||
mas_cur_span(&mas, &mas_span);
|
||||
|
||||
WARN_ON_ONCE(span.is_hole != mas_span.is_hole);
|
||||
|
||||
if (span.is_hole) {
|
||||
WARN_ON_ONCE(span.start_hole != mas_span.start_hole);
|
||||
WARN_ON_ONCE(span.last_hole != mas_span.last_hole);
|
||||
} else {
|
||||
WARN_ON_ONCE(span.start_used != mas_span.start_used);
|
||||
WARN_ON_ONCE(span.last_used != mas_span.last_used);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
WARN_ON_ONCE(mas.status != ma_overflow);
|
||||
|
||||
/* Cleanup maple tree for each round */
|
||||
mtree_destroy(&tree);
|
||||
/* Cleanup interval tree for each round */
|
||||
for (j = 0; j < nnodes; j++)
|
||||
interval_tree_remove(nodes + j, &root);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
static inline int span_iteration_check(void) {return 0; }
|
||||
#endif
|
||||
|
||||
static int interval_tree_test_init(void)
|
||||
{
|
||||
nodes = kmalloc_array(nnodes, sizeof(struct interval_tree_node),
|
||||
GFP_KERNEL);
|
||||
if (!nodes)
|
||||
return -ENOMEM;
|
||||
|
||||
queries = kmalloc_array(nsearches, sizeof(int), GFP_KERNEL);
|
||||
if (!queries) {
|
||||
kfree(nodes);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
prandom_seed_state(&rnd, seed);
|
||||
|
||||
basic_check();
|
||||
search_check();
|
||||
intersection_range_check();
|
||||
span_iteration_check();
|
||||
|
||||
kfree(queries);
|
||||
kfree(nodes);
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
#include <linux/export.h>
|
||||
#include <linux/min_heap.h>
|
||||
|
||||
void __min_heap_init(min_heap_char *heap, void *data, int size)
|
||||
void __min_heap_init(min_heap_char *heap, void *data, size_t size)
|
||||
{
|
||||
__min_heap_init_inline(heap, data, size);
|
||||
}
|
||||
@ -20,7 +20,7 @@ bool __min_heap_full(min_heap_char *heap)
|
||||
}
|
||||
EXPORT_SYMBOL(__min_heap_full);
|
||||
|
||||
void __min_heap_sift_down(min_heap_char *heap, int pos, size_t elem_size,
|
||||
void __min_heap_sift_down(min_heap_char *heap, size_t pos, size_t elem_size,
|
||||
const struct min_heap_callbacks *func, void *args)
|
||||
{
|
||||
__min_heap_sift_down_inline(heap, pos, elem_size, func, args);
|
||||
|
12
lib/plist.c
12
lib/plist.c
@ -171,12 +171,24 @@ void plist_requeue(struct plist_node *node, struct plist_head *head)
|
||||
|
||||
plist_del(node, head);
|
||||
|
||||
/*
|
||||
* After plist_del(), iter is the replacement of the node. If the node
|
||||
* was on prio_list, take shortcut to find node_next instead of looping.
|
||||
*/
|
||||
if (!list_empty(&iter->prio_list)) {
|
||||
iter = list_entry(iter->prio_list.next, struct plist_node,
|
||||
prio_list);
|
||||
node_next = &iter->node_list;
|
||||
goto queue;
|
||||
}
|
||||
|
||||
plist_for_each_continue(iter, head) {
|
||||
if (node->prio != iter->prio) {
|
||||
node_next = &iter->node_list;
|
||||
break;
|
||||
}
|
||||
}
|
||||
queue:
|
||||
list_add_tail(&node->node_list, node_next);
|
||||
|
||||
plist_check_head(head);
|
||||
|
@ -14,6 +14,7 @@
|
||||
__param(int, nnodes, 100, "Number of nodes in the rb-tree");
|
||||
__param(int, perf_loops, 1000, "Number of iterations modifying the rb-tree");
|
||||
__param(int, check_loops, 100, "Number of iterations modifying and verifying the rb-tree");
|
||||
__param(ullong, seed, 3141592653589793238ULL, "Random seed");
|
||||
|
||||
struct test_node {
|
||||
u32 key;
|
||||
@ -239,19 +240,14 @@ static void check_augmented(int nr_nodes)
|
||||
}
|
||||
}
|
||||
|
||||
static int __init rbtree_test_init(void)
|
||||
static int basic_check(void)
|
||||
{
|
||||
int i, j;
|
||||
cycles_t time1, time2, time;
|
||||
struct rb_node *node;
|
||||
|
||||
nodes = kmalloc_array(nnodes, sizeof(*nodes), GFP_KERNEL);
|
||||
if (!nodes)
|
||||
return -ENOMEM;
|
||||
|
||||
printk(KERN_ALERT "rbtree testing");
|
||||
|
||||
prandom_seed_state(&rnd, 3141592653589793238ULL);
|
||||
init();
|
||||
|
||||
time1 = get_cycles();
|
||||
@ -343,6 +339,14 @@ static int __init rbtree_test_init(void)
|
||||
check(0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int augmented_check(void)
|
||||
{
|
||||
int i, j;
|
||||
cycles_t time1, time2, time;
|
||||
|
||||
printk(KERN_ALERT "augmented rbtree testing");
|
||||
|
||||
init();
|
||||
@ -390,6 +394,20 @@ static int __init rbtree_test_init(void)
|
||||
check_augmented(0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __init rbtree_test_init(void)
|
||||
{
|
||||
nodes = kmalloc_array(nnodes, sizeof(*nodes), GFP_KERNEL);
|
||||
if (!nodes)
|
||||
return -ENOMEM;
|
||||
|
||||
prandom_seed_state(&rnd, seed);
|
||||
|
||||
basic_check();
|
||||
augmented_check();
|
||||
|
||||
kfree(nodes);
|
||||
|
||||
return -EAGAIN; /* Fail will directly unload the module */
|
||||
|
@ -151,9 +151,6 @@ static const config configuration_table[10] = {
|
||||
* meaning.
|
||||
*/
|
||||
|
||||
#define EQUAL 0
|
||||
/* result of memcmp for equal strings */
|
||||
|
||||
/* ===========================================================================
|
||||
* Update a hash value with the given input byte
|
||||
* IN assertion: all calls to UPDATE_HASH are made with consecutive
|
||||
@ -713,8 +710,7 @@ static void check_match(
|
||||
)
|
||||
{
|
||||
/* check that the match is indeed a match */
|
||||
if (memcmp((char *)s->window + match,
|
||||
(char *)s->window + start, length) != EQUAL) {
|
||||
if (memcmp((char *)s->window + match, (char *)s->window + start, length)) {
|
||||
fprintf(stderr, " start %u, match %u, length %d\n",
|
||||
start, match, length);
|
||||
do {
|
||||
|
@ -300,6 +300,15 @@ config SAMPLE_CHECK_EXEC
|
||||
demonstrate how they should be used with execveat(2) +
|
||||
AT_EXECVE_CHECK.
|
||||
|
||||
config SAMPLE_HUNG_TASK
|
||||
tristate "Hung task detector test code"
|
||||
depends on DETECT_HUNG_TASK && DEBUG_FS
|
||||
help
|
||||
Build a module which provide a simple debugfs file. If user reads
|
||||
the file, it will sleep long time (256 seconds) with holding a
|
||||
mutex. Thus if there are 2 or more processes read this file, it
|
||||
will be detected by the hung_task watchdog.
|
||||
|
||||
source "samples/rust/Kconfig"
|
||||
|
||||
source "samples/damon/Kconfig"
|
||||
|
@ -42,3 +42,4 @@ obj-$(CONFIG_SAMPLE_FPROBE) += fprobe/
|
||||
obj-$(CONFIG_SAMPLES_RUST) += rust/
|
||||
obj-$(CONFIG_SAMPLE_DAMON_WSSE) += damon/
|
||||
obj-$(CONFIG_SAMPLE_DAMON_PRCL) += damon/
|
||||
obj-$(CONFIG_SAMPLE_HUNG_TASK) += hung_task/
|
||||
|
2
samples/hung_task/Makefile
Normal file
2
samples/hung_task/Makefile
Normal file
@ -0,0 +1,2 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
obj-$(CONFIG_SAMPLE_HUNG_TASK) += hung_task_mutex.o
|
66
samples/hung_task/hung_task_mutex.c
Normal file
66
samples/hung_task/hung_task_mutex.c
Normal file
@ -0,0 +1,66 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
/*
|
||||
* hung_task_mutex.c - Sample code which causes hung task by mutex
|
||||
*
|
||||
* Usage: load this module and read `<debugfs>/hung_task/mutex`
|
||||
* by 2 or more processes.
|
||||
*
|
||||
* This is for testing kernel hung_task error message.
|
||||
* Note that this will make your system freeze and maybe
|
||||
* cause panic. So do not use this except for the test.
|
||||
*/
|
||||
|
||||
#include <linux/debugfs.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mutex.h>
|
||||
|
||||
#define HUNG_TASK_DIR "hung_task"
|
||||
#define HUNG_TASK_FILE "mutex"
|
||||
#define SLEEP_SECOND 256
|
||||
|
||||
static const char dummy_string[] = "This is a dummy string.";
|
||||
static DEFINE_MUTEX(dummy_mutex);
|
||||
static struct dentry *hung_task_dir;
|
||||
|
||||
static ssize_t read_dummy(struct file *file, char __user *user_buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
/* If the second task waits on the lock, it is uninterruptible sleep. */
|
||||
guard(mutex)(&dummy_mutex);
|
||||
|
||||
/* When the first task sleep here, it is interruptible. */
|
||||
msleep_interruptible(SLEEP_SECOND * 1000);
|
||||
|
||||
return simple_read_from_buffer(user_buf, count, ppos,
|
||||
dummy_string, sizeof(dummy_string));
|
||||
}
|
||||
|
||||
static const struct file_operations hung_task_fops = {
|
||||
.read = read_dummy,
|
||||
};
|
||||
|
||||
static int __init hung_task_sample_init(void)
|
||||
{
|
||||
hung_task_dir = debugfs_create_dir(HUNG_TASK_DIR, NULL);
|
||||
if (IS_ERR(hung_task_dir))
|
||||
return PTR_ERR(hung_task_dir);
|
||||
|
||||
debugfs_create_file(HUNG_TASK_FILE, 0400, hung_task_dir,
|
||||
NULL, &hung_task_fops);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit hung_task_sample_exit(void)
|
||||
{
|
||||
debugfs_remove_recursive(hung_task_dir);
|
||||
}
|
||||
|
||||
module_init(hung_task_sample_init);
|
||||
module_exit(hung_task_sample_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_AUTHOR("Masami Hiramatsu");
|
||||
MODULE_DESCRIPTION("Simple sleep under mutex file for testing hung task");
|
@ -113,7 +113,8 @@ Options:
|
||||
--max-line-length=n set the maximum line length, (default $max_line_length)
|
||||
if exceeded, warn on patches
|
||||
requires --strict for use with --file
|
||||
--min-conf-desc-length=n set the min description length, if shorter, warn
|
||||
--min-conf-desc-length=n set the minimum description length for config symbols
|
||||
in lines, if shorter, warn (default $min_conf_desc_length)
|
||||
--tab-size=n set the number of spaces for tab (default $tabsize)
|
||||
--root=PATH PATH to the kernel tree root
|
||||
--no-summary suppress the per-file summary
|
||||
@ -3645,7 +3646,7 @@ sub process {
|
||||
$help_length < $min_conf_desc_length) {
|
||||
my $stat_real = get_stat_real($linenr, $ln - 1);
|
||||
WARN("CONFIG_DESCRIPTION",
|
||||
"please write a help paragraph that fully describes the config symbol\n" . "$here\n$stat_real\n");
|
||||
"please write a help paragraph that fully describes the config symbol with at least $min_conf_desc_length lines\n" . "$here\n$stat_real\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -20,3 +20,13 @@ virtual patch
|
||||
|
||||
- msecs_to_jiffies(C * MSEC_PER_SEC)
|
||||
+ secs_to_jiffies(C)
|
||||
|
||||
@depends on patch@ expression E; @@
|
||||
|
||||
- msecs_to_jiffies(E * 1000)
|
||||
+ secs_to_jiffies(E)
|
||||
|
||||
@depends on patch@ expression E; @@
|
||||
|
||||
- msecs_to_jiffies(E * MSEC_PER_SEC)
|
||||
+ secs_to_jiffies(E)
|
||||
|
30
scripts/extract-fwblobs
Executable file
30
scripts/extract-fwblobs
Executable file
@ -0,0 +1,30 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
#
|
||||
# -----------------------------------------------------------------------------
|
||||
# Extracts the vmlinux built-in firmware blobs - requires a non-stripped image
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
if [ -z "$1" ]; then
|
||||
echo "Must provide a non-stripped vmlinux as argument"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
read -r RD_ADDR_HEX RD_OFF_HEX <<< "$( readelf -SW "$1" |\
|
||||
grep -w rodata | awk '{print "0x"$5" 0x"$6}' )"
|
||||
|
||||
FW_SYMS="$(readelf -sW "$1" |\
|
||||
awk -n '/fw_end/ { end=$2 ; print name " 0x" start " 0x" end; } { start=$2; name=$8; }')"
|
||||
|
||||
while IFS= read -r entry; do
|
||||
read -r FW_NAME FW_ADDR_ST_HEX FW_ADDR_END_HEX <<< "$entry"
|
||||
|
||||
# Notice kernel prepends _fw_ and appends _bin to the FW name
|
||||
# in rodata; hence we hereby filter that out.
|
||||
FW_NAME=${FW_NAME:4:-4}
|
||||
|
||||
FW_OFFSET="$(printf "%d" $((FW_ADDR_ST_HEX - RD_ADDR_HEX + RD_OFF_HEX)))"
|
||||
FW_SIZE="$(printf "%d" $((FW_ADDR_END_HEX - FW_ADDR_ST_HEX)))"
|
||||
|
||||
dd if="$1" of="./${FW_NAME}" bs="${FW_SIZE}" count=1 iflag=skip_bytes skip="${FW_OFFSET}"
|
||||
done <<< "${FW_SYMS}"
|
@ -46,7 +46,7 @@ def per_cpu(var_ptr, cpu):
|
||||
# !CONFIG_SMP case
|
||||
offset = 0
|
||||
pointer = var_ptr.cast(utils.get_long_type()) + offset
|
||||
return pointer.cast(var_ptr.type).dereference()
|
||||
return pointer.cast(var_ptr.type)
|
||||
|
||||
|
||||
cpu_mask = {}
|
||||
@ -149,11 +149,29 @@ Note that VAR has to be quoted as string."""
|
||||
super(PerCpu, self).__init__("lx_per_cpu")
|
||||
|
||||
def invoke(self, var, cpu=-1):
|
||||
return per_cpu(var.address, cpu)
|
||||
return per_cpu(var.address, cpu).dereference()
|
||||
|
||||
|
||||
PerCpu()
|
||||
|
||||
|
||||
class PerCpuPtr(gdb.Function):
|
||||
"""Return per-cpu pointer.
|
||||
|
||||
$lx_per_cpu_ptr("VAR"[, CPU]): Return the per-cpu pointer called VAR for the
|
||||
given CPU number. If CPU is omitted, the CPU of the current context is used.
|
||||
Note that VAR has to be quoted as string."""
|
||||
|
||||
def __init__(self):
|
||||
super(PerCpuPtr, self).__init__("lx_per_cpu_ptr")
|
||||
|
||||
def invoke(self, var, cpu=-1):
|
||||
return per_cpu(var, cpu)
|
||||
|
||||
|
||||
PerCpuPtr()
|
||||
|
||||
|
||||
def get_current_task(cpu):
|
||||
task_ptr_type = task_type.get_type().pointer()
|
||||
|
||||
|
@ -14,7 +14,9 @@
|
||||
import gdb
|
||||
import os
|
||||
import re
|
||||
import struct
|
||||
|
||||
from itertools import count
|
||||
from linux import modules, utils, constants
|
||||
|
||||
|
||||
@ -53,6 +55,29 @@ if hasattr(gdb, 'Breakpoint'):
|
||||
return False
|
||||
|
||||
|
||||
def get_vmcore_s390():
|
||||
with utils.qemu_phy_mem_mode():
|
||||
vmcore_info = 0x0e0c
|
||||
paddr_vmcoreinfo_note = gdb.parse_and_eval("*(unsigned long long *)" +
|
||||
hex(vmcore_info))
|
||||
inferior = gdb.selected_inferior()
|
||||
elf_note = inferior.read_memory(paddr_vmcoreinfo_note, 12)
|
||||
n_namesz, n_descsz, n_type = struct.unpack(">III", elf_note)
|
||||
desc_paddr = paddr_vmcoreinfo_note + len(elf_note) + n_namesz + 1
|
||||
return gdb.parse_and_eval("(char *)" + hex(desc_paddr)).string()
|
||||
|
||||
|
||||
def get_kerneloffset():
|
||||
if utils.is_target_arch('s390'):
|
||||
try:
|
||||
vmcore_str = get_vmcore_s390()
|
||||
except gdb.error as e:
|
||||
gdb.write("{}\n".format(e))
|
||||
return None
|
||||
return utils.parse_vmcore(vmcore_str).kerneloffset
|
||||
return None
|
||||
|
||||
|
||||
class LxSymbols(gdb.Command):
|
||||
"""(Re-)load symbols of Linux kernel and currently loaded modules.
|
||||
|
||||
@ -95,10 +120,14 @@ lx-symbols command."""
|
||||
except gdb.error:
|
||||
return str(module_addr)
|
||||
|
||||
attrs = sect_attrs['attrs']
|
||||
section_name_to_address = {
|
||||
attrs[n]['battr']['attr']['name'].string(): attrs[n]['address']
|
||||
for n in range(int(sect_attrs['nsections']))}
|
||||
section_name_to_address = {}
|
||||
for i in count():
|
||||
# this is a NULL terminated array
|
||||
if sect_attrs['grp']['bin_attrs'][i] == 0x0:
|
||||
break
|
||||
|
||||
attr = sect_attrs['grp']['bin_attrs'][i].dereference()
|
||||
section_name_to_address[attr['attr']['name'].string()] = attr['private']
|
||||
|
||||
textaddr = section_name_to_address.get(".text", module_addr)
|
||||
args = []
|
||||
@ -155,7 +184,12 @@ lx-symbols command."""
|
||||
obj.filename.endswith('vmlinux.debug')):
|
||||
orig_vmlinux = obj.filename
|
||||
gdb.execute("symbol-file", to_string=True)
|
||||
gdb.execute("symbol-file {0}".format(orig_vmlinux))
|
||||
kerneloffset = get_kerneloffset()
|
||||
if kerneloffset is None:
|
||||
offset_arg = ""
|
||||
else:
|
||||
offset_arg = " -o " + hex(kerneloffset)
|
||||
gdb.execute("symbol-file {0}{1}".format(orig_vmlinux, offset_arg))
|
||||
|
||||
self.loaded_modules = []
|
||||
module_list = modules.module_list()
|
||||
|
@ -11,6 +11,11 @@
|
||||
# This work is licensed under the terms of the GNU GPL version 2.
|
||||
#
|
||||
|
||||
import contextlib
|
||||
import dataclasses
|
||||
import re
|
||||
import typing
|
||||
|
||||
import gdb
|
||||
|
||||
|
||||
@ -216,3 +221,33 @@ def gdb_eval_or_none(expresssion):
|
||||
return gdb.parse_and_eval(expresssion)
|
||||
except gdb.error:
|
||||
return None
|
||||
|
||||
|
||||
@contextlib.contextmanager
|
||||
def qemu_phy_mem_mode():
|
||||
connection = gdb.selected_inferior().connection
|
||||
orig = connection.send_packet("qqemu.PhyMemMode")
|
||||
if orig not in b"01":
|
||||
raise gdb.error("Unexpected qemu.PhyMemMode")
|
||||
orig = orig.decode()
|
||||
if connection.send_packet("Qqemu.PhyMemMode:1") != b"OK":
|
||||
raise gdb.error("Failed to set qemu.PhyMemMode")
|
||||
try:
|
||||
yield
|
||||
finally:
|
||||
if connection.send_packet("Qqemu.PhyMemMode:" + orig) != b"OK":
|
||||
raise gdb.error("Failed to restore qemu.PhyMemMode")
|
||||
|
||||
|
||||
@dataclasses.dataclass
|
||||
class VmCore:
|
||||
kerneloffset: typing.Optional[int]
|
||||
|
||||
|
||||
def parse_vmcore(s):
|
||||
match = re.search(r"KERNELOFFSET=([0-9a-f]+)", s)
|
||||
if match is None:
|
||||
kerneloffset = None
|
||||
else:
|
||||
kerneloffset = int(match.group(1), 16)
|
||||
return VmCore(kerneloffset=kerneloffset)
|
||||
|
@ -50,6 +50,7 @@ my $output_multiline = 1;
|
||||
my $output_separator = ", ";
|
||||
my $output_roles = 0;
|
||||
my $output_rolestats = 1;
|
||||
my $output_substatus = undef;
|
||||
my $output_section_maxlen = 50;
|
||||
my $scm = 0;
|
||||
my $tree = 1;
|
||||
@ -269,6 +270,7 @@ if (!GetOptions(
|
||||
'separator=s' => \$output_separator,
|
||||
'subsystem!' => \$subsystem,
|
||||
'status!' => \$status,
|
||||
'substatus!' => \$output_substatus,
|
||||
'scm!' => \$scm,
|
||||
'tree!' => \$tree,
|
||||
'web!' => \$web,
|
||||
@ -314,6 +316,10 @@ $output_multiline = 0 if ($output_separator ne ", ");
|
||||
$output_rolestats = 1 if ($interactive);
|
||||
$output_roles = 1 if ($output_rolestats);
|
||||
|
||||
if (!defined $output_substatus) {
|
||||
$output_substatus = $email && $output_roles && -t STDOUT;
|
||||
}
|
||||
|
||||
if ($sections || $letters ne "") {
|
||||
$sections = 1;
|
||||
$email = 0;
|
||||
@ -637,6 +643,7 @@ my @web = ();
|
||||
my @bug = ();
|
||||
my @subsystem = ();
|
||||
my @status = ();
|
||||
my @substatus = ();
|
||||
my %deduplicate_name_hash = ();
|
||||
my %deduplicate_address_hash = ();
|
||||
|
||||
@ -651,6 +658,11 @@ if ($scm) {
|
||||
output(@scm);
|
||||
}
|
||||
|
||||
if ($output_substatus) {
|
||||
@substatus = uniq(@substatus);
|
||||
output(@substatus);
|
||||
}
|
||||
|
||||
if ($status) {
|
||||
@status = uniq(@status);
|
||||
output(@status);
|
||||
@ -859,6 +871,7 @@ sub get_maintainers {
|
||||
@bug = ();
|
||||
@subsystem = ();
|
||||
@status = ();
|
||||
@substatus = ();
|
||||
%deduplicate_name_hash = ();
|
||||
%deduplicate_address_hash = ();
|
||||
if ($email_git_all_signature_types) {
|
||||
@ -1071,8 +1084,9 @@ MAINTAINER field selection options:
|
||||
--moderated => include moderated lists(s) if any (default: true)
|
||||
--s => include subscriber only list(s) if any (default: false)
|
||||
--remove-duplicates => minimize duplicate email names/addresses
|
||||
--roles => show roles (status:subsystem, git-signer, list, etc...)
|
||||
--roles => show roles (role:subsystem, git-signer, list, etc...)
|
||||
--rolestats => show roles and statistics (commits/total_commits, %)
|
||||
--substatus => show subsystem status if not Maintained (default: match --roles when output is tty)"
|
||||
--file-emails => add email addresses found in -f file (default: 0 (off))
|
||||
--fixes => for patches, add signatures of commits with 'Fixes: <commit>' (default: 1 (on))
|
||||
--scm => print SCM tree(s) if any
|
||||
@ -1284,8 +1298,9 @@ sub get_maintainer_role {
|
||||
my $start = find_starting_index($index);
|
||||
my $end = find_ending_index($index);
|
||||
|
||||
my $role = "unknown";
|
||||
my $role = "maintainer";
|
||||
my $subsystem = get_subsystem_name($index);
|
||||
my $status = "unknown";
|
||||
|
||||
for ($i = $start + 1; $i < $end; $i++) {
|
||||
my $tv = $typevalue[$i];
|
||||
@ -1293,23 +1308,13 @@ sub get_maintainer_role {
|
||||
my $ptype = $1;
|
||||
my $pvalue = $2;
|
||||
if ($ptype eq "S") {
|
||||
$role = $pvalue;
|
||||
$status = $pvalue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$role = lc($role);
|
||||
if ($role eq "supported") {
|
||||
$role = "supporter";
|
||||
} elsif ($role eq "maintained") {
|
||||
$role = "maintainer";
|
||||
} elsif ($role eq "odd fixes") {
|
||||
$role = "odd fixer";
|
||||
} elsif ($role eq "orphan") {
|
||||
$role = "orphan minder";
|
||||
} elsif ($role eq "obsolete") {
|
||||
$role = "obsolete minder";
|
||||
} elsif ($role eq "buried alive in reporters") {
|
||||
$status = lc($status);
|
||||
if ($status eq "buried alive in reporters") {
|
||||
$role = "chief penguin";
|
||||
}
|
||||
|
||||
@ -1335,7 +1340,9 @@ sub add_categories {
|
||||
my $start = find_starting_index($index);
|
||||
my $end = find_ending_index($index);
|
||||
|
||||
push(@subsystem, $typevalue[$start]);
|
||||
my $subsystem = $typevalue[$start];
|
||||
push(@subsystem, $subsystem);
|
||||
my $status = "Unknown";
|
||||
|
||||
for ($i = $start + 1; $i < $end; $i++) {
|
||||
my $tv = $typevalue[$i];
|
||||
@ -1386,8 +1393,8 @@ sub add_categories {
|
||||
}
|
||||
} elsif ($ptype eq "R") {
|
||||
if ($email_reviewer) {
|
||||
my $subsystem = get_subsystem_name($i);
|
||||
push_email_addresses($pvalue, "reviewer:$subsystem" . $suffix);
|
||||
my $subs = get_subsystem_name($i);
|
||||
push_email_addresses($pvalue, "reviewer:$subs" . $suffix);
|
||||
}
|
||||
} elsif ($ptype eq "T") {
|
||||
push(@scm, $pvalue . $suffix);
|
||||
@ -1397,9 +1404,14 @@ sub add_categories {
|
||||
push(@bug, $pvalue . $suffix);
|
||||
} elsif ($ptype eq "S") {
|
||||
push(@status, $pvalue . $suffix);
|
||||
$status = $pvalue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($subsystem ne "THE REST" and $status ne "Maintained") {
|
||||
push(@substatus, $subsystem . " status: " . $status . $suffix)
|
||||
}
|
||||
}
|
||||
|
||||
sub email_inuse {
|
||||
@ -1903,6 +1915,7 @@ EOT
|
||||
$done = 1;
|
||||
$output_rolestats = 0;
|
||||
$output_roles = 0;
|
||||
$output_substatus = 0;
|
||||
last;
|
||||
} elsif ($nr =~ /^\d+$/ && $nr > 0 && $nr <= $count) {
|
||||
$selected{$nr - 1} = !$selected{$nr - 1};
|
||||
|
@ -2461,8 +2461,7 @@ int snd_ac97_update_power(struct snd_ac97 *ac97, int reg, int powerup)
|
||||
* (for avoiding loud click noises for many (OSS) apps
|
||||
* that open/close frequently)
|
||||
*/
|
||||
schedule_delayed_work(&ac97->power_work,
|
||||
msecs_to_jiffies(power_save * 1000));
|
||||
schedule_delayed_work(&ac97->power_work, secs_to_jiffies(power_save));
|
||||
else {
|
||||
cancel_delayed_work(&ac97->power_work);
|
||||
update_power_regs(ac97);
|
||||
|
13
tools/include/asm/timex.h
Normal file
13
tools/include/asm/timex.h
Normal file
@ -0,0 +1,13 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __TOOLS_LINUX_ASM_TIMEX_H
|
||||
#define __TOOLS_LINUX_ASM_TIMEX_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#define cycles_t clock_t
|
||||
|
||||
static inline cycles_t get_cycles(void)
|
||||
{
|
||||
return clock();
|
||||
}
|
||||
#endif // __TOOLS_LINUX_ASM_TIMEX_H
|
@ -19,6 +19,7 @@ bool __bitmap_and(unsigned long *dst, const unsigned long *bitmap1,
|
||||
const unsigned long *bitmap2, unsigned int bits);
|
||||
bool __bitmap_equal(const unsigned long *bitmap1,
|
||||
const unsigned long *bitmap2, unsigned int bits);
|
||||
void __bitmap_set(unsigned long *map, unsigned int start, int len);
|
||||
void __bitmap_clear(unsigned long *map, unsigned int start, int len);
|
||||
bool __bitmap_intersects(const unsigned long *bitmap1,
|
||||
const unsigned long *bitmap2, unsigned int bits);
|
||||
@ -79,6 +80,11 @@ static inline void bitmap_or(unsigned long *dst, const unsigned long *src1,
|
||||
__bitmap_or(dst, src1, src2, nbits);
|
||||
}
|
||||
|
||||
static inline unsigned long *bitmap_alloc(unsigned int nbits, gfp_t flags __maybe_unused)
|
||||
{
|
||||
return malloc(bitmap_size(nbits));
|
||||
}
|
||||
|
||||
/**
|
||||
* bitmap_zalloc - Allocate bitmap
|
||||
* @nbits: Number of bits
|
||||
@ -150,6 +156,21 @@ static inline bool bitmap_intersects(const unsigned long *src1,
|
||||
return __bitmap_intersects(src1, src2, nbits);
|
||||
}
|
||||
|
||||
static inline void bitmap_set(unsigned long *map, unsigned int start, unsigned int nbits)
|
||||
{
|
||||
if (__builtin_constant_p(nbits) && nbits == 1)
|
||||
__set_bit(start, map);
|
||||
else if (small_const_nbits(start + nbits))
|
||||
*map |= GENMASK(start + nbits - 1, start);
|
||||
else if (__builtin_constant_p(start & BITMAP_MEM_MASK) &&
|
||||
IS_ALIGNED(start, BITMAP_MEM_ALIGNMENT) &&
|
||||
__builtin_constant_p(nbits & BITMAP_MEM_MASK) &&
|
||||
IS_ALIGNED(nbits, BITMAP_MEM_ALIGNMENT))
|
||||
memset((char *)map + start / 8, 0xff, nbits / 8);
|
||||
else
|
||||
__bitmap_set(map, start, nbits);
|
||||
}
|
||||
|
||||
static inline void bitmap_clear(unsigned long *map, unsigned int start,
|
||||
unsigned int nbits)
|
||||
{
|
||||
|
18
tools/include/linux/container_of.h
Normal file
18
tools/include/linux/container_of.h
Normal file
@ -0,0 +1,18 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _TOOLS_LINUX_CONTAINER_OF_H
|
||||
#define _TOOLS_LINUX_CONTAINER_OF_H
|
||||
|
||||
#ifndef container_of
|
||||
/**
|
||||
* container_of - cast a member of a structure out to the containing structure
|
||||
* @ptr: the pointer to the member.
|
||||
* @type: the type of the container struct this is embedded in.
|
||||
* @member: the name of the member within the struct.
|
||||
*
|
||||
*/
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
const typeof(((type *)0)->member) * __mptr = (ptr); \
|
||||
(type *)((char *)__mptr - offsetof(type, member)); })
|
||||
#endif
|
||||
|
||||
#endif /* _TOOLS_LINUX_CONTAINER_OF_H */
|
@ -11,6 +11,7 @@
|
||||
#include <linux/panic.h>
|
||||
#include <endian.h>
|
||||
#include <byteswap.h>
|
||||
#include <linux/container_of.h>
|
||||
|
||||
#ifndef UINT_MAX
|
||||
#define UINT_MAX (~0U)
|
||||
@ -25,19 +26,6 @@
|
||||
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
|
||||
#endif
|
||||
|
||||
#ifndef container_of
|
||||
/**
|
||||
* container_of - cast a member of a structure out to the containing structure
|
||||
* @ptr: the pointer to the member.
|
||||
* @type: the type of the container struct this is embedded in.
|
||||
* @member: the name of the member within the struct.
|
||||
*
|
||||
*/
|
||||
#define container_of(ptr, type, member) ({ \
|
||||
const typeof(((type *)0)->member) * __mptr = (ptr); \
|
||||
(type *)((char *)__mptr - offsetof(type, member)); })
|
||||
#endif
|
||||
|
||||
#ifndef max
|
||||
#define max(x, y) ({ \
|
||||
typeof(x) _max1 = (x); \
|
||||
|
@ -72,4 +72,9 @@ static inline u64 mul_u64_u64_div64(u64 a, u64 b, u64 c)
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline u64 div_u64(u64 dividend, u32 divisor)
|
||||
{
|
||||
return dividend / divisor;
|
||||
}
|
||||
|
||||
#endif /* _LINUX_MATH64_H */
|
||||
|
7
tools/include/linux/moduleparam.h
Normal file
7
tools/include/linux/moduleparam.h
Normal file
@ -0,0 +1,7 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef _TOOLS_LINUX_MODULE_PARAMS_H
|
||||
#define _TOOLS_LINUX_MODULE_PARAMS_H
|
||||
|
||||
#define MODULE_PARM_DESC(parm, desc)
|
||||
|
||||
#endif // _TOOLS_LINUX_MODULE_PARAMS_H
|
51
tools/include/linux/prandom.h
Normal file
51
tools/include/linux/prandom.h
Normal file
@ -0,0 +1,51 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
#ifndef __TOOLS_LINUX_PRANDOM_H
|
||||
#define __TOOLS_LINUX_PRANDOM_H
|
||||
|
||||
#include <linux/types.h>
|
||||
|
||||
struct rnd_state {
|
||||
__u32 s1, s2, s3, s4;
|
||||
};
|
||||
|
||||
/*
|
||||
* Handle minimum values for seeds
|
||||
*/
|
||||
static inline u32 __seed(u32 x, u32 m)
|
||||
{
|
||||
return (x < m) ? x + m : x;
|
||||
}
|
||||
|
||||
/**
|
||||
* prandom_seed_state - set seed for prandom_u32_state().
|
||||
* @state: pointer to state structure to receive the seed.
|
||||
* @seed: arbitrary 64-bit value to use as a seed.
|
||||
*/
|
||||
static inline void prandom_seed_state(struct rnd_state *state, u64 seed)
|
||||
{
|
||||
u32 i = ((seed >> 32) ^ (seed << 10) ^ seed) & 0xffffffffUL;
|
||||
|
||||
state->s1 = __seed(i, 2U);
|
||||
state->s2 = __seed(i, 8U);
|
||||
state->s3 = __seed(i, 16U);
|
||||
state->s4 = __seed(i, 128U);
|
||||
}
|
||||
|
||||
/**
|
||||
* prandom_u32_state - seeded pseudo-random number generator.
|
||||
* @state: pointer to state structure holding seeded state.
|
||||
*
|
||||
* This is used for pseudo-randomness with no outside seeding.
|
||||
* For more random results, use get_random_u32().
|
||||
*/
|
||||
static inline u32 prandom_u32_state(struct rnd_state *state)
|
||||
{
|
||||
#define TAUSWORTHE(s, a, b, c, d) (((s & c) << d) ^ (((s << a) ^ s) >> b))
|
||||
state->s1 = TAUSWORTHE(state->s1, 6U, 13U, 4294967294U, 18U);
|
||||
state->s2 = TAUSWORTHE(state->s2, 2U, 27U, 4294967288U, 2U);
|
||||
state->s3 = TAUSWORTHE(state->s3, 13U, 21U, 4294967280U, 7U);
|
||||
state->s4 = TAUSWORTHE(state->s4, 3U, 12U, 4294967168U, 13U);
|
||||
|
||||
return (state->s1 ^ state->s2 ^ state->s3 ^ state->s4);
|
||||
}
|
||||
#endif // __TOOLS_LINUX_PRANDOM_H
|
@ -12,6 +12,7 @@
|
||||
|
||||
void *kmalloc(size_t size, gfp_t gfp);
|
||||
void kfree(void *p);
|
||||
void *kmalloc_array(size_t n, size_t size, gfp_t gfp);
|
||||
|
||||
bool slab_is_available(void);
|
||||
|
||||
|
@ -42,6 +42,8 @@ typedef __s16 s16;
|
||||
typedef __u8 u8;
|
||||
typedef __s8 s8;
|
||||
|
||||
typedef unsigned long long ullong;
|
||||
|
||||
#ifdef __CHECKER__
|
||||
#define __bitwise __attribute__((bitwise))
|
||||
#else
|
||||
|
@ -101,6 +101,26 @@ bool __bitmap_intersects(const unsigned long *bitmap1,
|
||||
return false;
|
||||
}
|
||||
|
||||
void __bitmap_set(unsigned long *map, unsigned int start, int len)
|
||||
{
|
||||
unsigned long *p = map + BIT_WORD(start);
|
||||
const unsigned int size = start + len;
|
||||
int bits_to_set = BITS_PER_LONG - (start % BITS_PER_LONG);
|
||||
unsigned long mask_to_set = BITMAP_FIRST_WORD_MASK(start);
|
||||
|
||||
while (len - bits_to_set >= 0) {
|
||||
*p |= mask_to_set;
|
||||
len -= bits_to_set;
|
||||
bits_to_set = BITS_PER_LONG;
|
||||
mask_to_set = ~0UL;
|
||||
p++;
|
||||
}
|
||||
if (len) {
|
||||
mask_to_set &= BITMAP_LAST_WORD_MASK(size);
|
||||
*p |= mask_to_set;
|
||||
}
|
||||
}
|
||||
|
||||
void __bitmap_clear(unsigned long *map, unsigned int start, int len)
|
||||
{
|
||||
unsigned long *p = map + BIT_WORD(start);
|
||||
|
@ -36,3 +36,19 @@ void kfree(void *p)
|
||||
printf("Freeing %p to malloc\n", p);
|
||||
free(p);
|
||||
}
|
||||
|
||||
void *kmalloc_array(size_t n, size_t size, gfp_t gfp)
|
||||
{
|
||||
void *ret;
|
||||
|
||||
if (!(gfp & __GFP_DIRECT_RECLAIM))
|
||||
return NULL;
|
||||
|
||||
ret = calloc(n, size);
|
||||
uatomic_inc(&kmalloc_nr_allocated);
|
||||
if (kmalloc_verbose)
|
||||
printf("Allocating %p from calloc\n", ret);
|
||||
if (gfp & __GFP_ZERO)
|
||||
memset(ret, 0, n * size);
|
||||
return ret;
|
||||
}
|
||||
|
33
tools/testing/rbtree/Makefile
Normal file
33
tools/testing/rbtree/Makefile
Normal file
@ -0,0 +1,33 @@
|
||||
# SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
.PHONY: clean
|
||||
|
||||
TARGETS = rbtree_test interval_tree_test
|
||||
OFILES = $(SHARED_OFILES) rbtree-shim.o interval_tree-shim.o maple-shim.o
|
||||
DEPS = ../../../include/linux/rbtree.h \
|
||||
../../../include/linux/rbtree_types.h \
|
||||
../../../include/linux/rbtree_augmented.h \
|
||||
../../../include/linux/interval_tree.h \
|
||||
../../../include/linux/interval_tree_generic.h \
|
||||
../../../lib/rbtree.c \
|
||||
../../../lib/interval_tree.c
|
||||
|
||||
targets: $(TARGETS)
|
||||
|
||||
include ../shared/shared.mk
|
||||
|
||||
ifeq ($(DEBUG), 1)
|
||||
CFLAGS += -g
|
||||
endif
|
||||
|
||||
$(TARGETS): $(OFILES)
|
||||
|
||||
rbtree-shim.o: $(DEPS)
|
||||
rbtree_test.o: ../../../lib/rbtree_test.c
|
||||
interval_tree-shim.o: $(DEPS)
|
||||
interval_tree-shim.o: CFLAGS += -DCONFIG_INTERVAL_TREE_SPAN_ITER
|
||||
interval_tree_test.o: ../../../lib/interval_tree_test.c
|
||||
interval_tree_test.o: CFLAGS += -DCONFIG_INTERVAL_TREE_SPAN_ITER
|
||||
|
||||
clean:
|
||||
$(RM) $(TARGETS) *.o radix-tree.c idr.c generated/*
|
58
tools/testing/rbtree/interval_tree_test.c
Normal file
58
tools/testing/rbtree/interval_tree_test.c
Normal file
@ -0,0 +1,58 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* interval_tree.c: Userspace Interval Tree test-suite
|
||||
* Copyright (c) 2025 Wei Yang <richard.weiyang@gmail.com>
|
||||
*/
|
||||
#include <linux/math64.h>
|
||||
#include <linux/kern_levels.h>
|
||||
#include "shared.h"
|
||||
#include "maple-shared.h"
|
||||
|
||||
#include "../../../lib/interval_tree_test.c"
|
||||
|
||||
int usage(void)
|
||||
{
|
||||
fprintf(stderr, "Userland interval tree test cases\n");
|
||||
fprintf(stderr, " -n: Number of nodes in the interval tree\n");
|
||||
fprintf(stderr, " -p: Number of iterations modifying the tree\n");
|
||||
fprintf(stderr, " -q: Number of searches to the interval tree\n");
|
||||
fprintf(stderr, " -s: Number of iterations searching the tree\n");
|
||||
fprintf(stderr, " -a: Searches will iterate all nodes in the tree\n");
|
||||
fprintf(stderr, " -m: Largest value for the interval's endpoint\n");
|
||||
fprintf(stderr, " -r: Random seed\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
void interval_tree_tests(void)
|
||||
{
|
||||
interval_tree_test_init();
|
||||
interval_tree_test_exit();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
|
||||
while ((opt = getopt(argc, argv, "n:p:q:s:am:r:")) != -1) {
|
||||
if (opt == 'n')
|
||||
nnodes = strtoul(optarg, NULL, 0);
|
||||
else if (opt == 'p')
|
||||
perf_loops = strtoul(optarg, NULL, 0);
|
||||
else if (opt == 'q')
|
||||
nsearches = strtoul(optarg, NULL, 0);
|
||||
else if (opt == 's')
|
||||
search_loops = strtoul(optarg, NULL, 0);
|
||||
else if (opt == 'a')
|
||||
search_all = true;
|
||||
else if (opt == 'm')
|
||||
max_endpoint = strtoul(optarg, NULL, 0);
|
||||
else if (opt == 'r')
|
||||
seed = strtoul(optarg, NULL, 0);
|
||||
else
|
||||
usage();
|
||||
}
|
||||
|
||||
maple_tree_init();
|
||||
interval_tree_tests();
|
||||
return 0;
|
||||
}
|
48
tools/testing/rbtree/rbtree_test.c
Normal file
48
tools/testing/rbtree/rbtree_test.c
Normal file
@ -0,0 +1,48 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
/*
|
||||
* rbtree_test.c: Userspace Red Black Tree test-suite
|
||||
* Copyright (c) 2025 Wei Yang <richard.weiyang@gmail.com>
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/math64.h>
|
||||
#include <linux/kern_levels.h>
|
||||
#include "shared.h"
|
||||
|
||||
#include "../../../lib/rbtree_test.c"
|
||||
|
||||
int usage(void)
|
||||
{
|
||||
fprintf(stderr, "Userland rbtree test cases\n");
|
||||
fprintf(stderr, " -n: Number of nodes in the rb-tree\n");
|
||||
fprintf(stderr, " -p: Number of iterations modifying the rb-tree\n");
|
||||
fprintf(stderr, " -c: Number of iterations modifying and verifying the rb-tree\n");
|
||||
fprintf(stderr, " -r: Random seed\n");
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
void rbtree_tests(void)
|
||||
{
|
||||
rbtree_test_init();
|
||||
rbtree_test_exit();
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
int opt;
|
||||
|
||||
while ((opt = getopt(argc, argv, "n:p:c:r:")) != -1) {
|
||||
if (opt == 'n')
|
||||
nnodes = strtoul(optarg, NULL, 0);
|
||||
else if (opt == 'p')
|
||||
perf_loops = strtoul(optarg, NULL, 0);
|
||||
else if (opt == 'c')
|
||||
check_loops = strtoul(optarg, NULL, 0);
|
||||
else if (opt == 'r')
|
||||
seed = strtoul(optarg, NULL, 0);
|
||||
else
|
||||
usage();
|
||||
}
|
||||
|
||||
rbtree_tests();
|
||||
return 0;
|
||||
}
|
4
tools/testing/rbtree/test.h
Normal file
4
tools/testing/rbtree/test.h
Normal file
@ -0,0 +1,4 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0 */
|
||||
|
||||
void rbtree_tests(void);
|
||||
void interval_tree_tests(void);
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user