Merge tag 'mm-nonmm-stable-2025-10-02-15-29' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm
Pull non-MM updates from Andrew Morton: - "ida: Remove the ida_simple_xxx() API" from Christophe Jaillet completes the removal of this legacy IDR API - "panic: introduce panic status function family" from Jinchao Wang provides a number of cleanups to the panic code and its various helpers, which were rather ad-hoc and scattered all over the place - "tools/delaytop: implement real-time keyboard interaction support" from Fan Yu adds a few nice user-facing usability changes to the delaytop monitoring tool - "efi: Fix EFI boot with kexec handover (KHO)" from Evangelos Petrongonas fixes a panic which was happening with the combination of EFI and KHO - "Squashfs: performance improvement and a sanity check" from Phillip Lougher teaches squashfs's lseek() about SEEK_DATA/SEEK_HOLE. A mere 150x speedup was measured for a well-chosen microbenchmark - plus another 50-odd singleton patches all over the place * tag 'mm-nonmm-stable-2025-10-02-15-29' of git://git.kernel.org/pub/scm/linux/kernel/git/akpm/mm: (75 commits) Squashfs: reject negative file sizes in squashfs_read_inode() kallsyms: use kmalloc_array() instead of kmalloc() MAINTAINERS: update Sibi Sankar's email address Squashfs: add SEEK_DATA/SEEK_HOLE support Squashfs: add additional inode sanity checking lib/genalloc: fix device leak in of_gen_pool_get() panic: remove CONFIG_PANIC_ON_OOPS_VALUE ocfs2: fix double free in user_cluster_connect() checkpatch: suppress strscpy warnings for userspace tools cramfs: fix incorrect physical page address calculation kernel: prevent prctl(PR_SET_PDEATHSIG) from racing with parent process exit Squashfs: fix uninit-value in squashfs_get_parent kho: only fill kimage if KHO is finalized ocfs2: avoid extra calls to strlen() after ocfs2_sprintf_system_inode_name() kernel/sys.c: fix the racy usage of task_lock(tsk->group_leader) in sys_prlimit64() paths sched/task.h: fix the wrong comment on task_lock() nesting with tasklist_lock coccinelle: platform_no_drv_owner: handle also built-in drivers coccinelle: of_table: handle SPI device ID tables lib/decompress: use designated initializers for struct compress_format efi: support booting with kexec handover (KHO) ...
This commit is contained in:
@@ -148,6 +148,17 @@ config CRASH_DM_CRYPT_CONFIGS
|
||||
CRASH_DM_CRYPT cannot directly select CONFIGFS_FS, because that
|
||||
is required to be built-in.
|
||||
|
||||
config CRASH_DUMP_KUNIT_TEST
|
||||
tristate "Unit Tests for kernel crash dumps" if !KUNIT_ALL_TESTS
|
||||
depends on CRASH_DUMP && KUNIT
|
||||
default KUNIT_ALL_TESTS
|
||||
help
|
||||
This option builds KUnit unit tests for kernel crash dumps. The unit
|
||||
tests will be used to verify the correctness of covered functions and
|
||||
also prevent any regression.
|
||||
|
||||
If unsure, say N.
|
||||
|
||||
config CRASH_HOTPLUG
|
||||
bool "Update the crash elfcorehdr on system configuration changes"
|
||||
default y
|
||||
|
||||
@@ -78,6 +78,7 @@ obj-$(CONFIG_CRASH_RESERVE) += crash_reserve.o
|
||||
obj-$(CONFIG_KEXEC_CORE) += kexec_core.o
|
||||
obj-$(CONFIG_CRASH_DUMP) += crash_core.o
|
||||
obj-$(CONFIG_CRASH_DM_CRYPT) += crash_dump_dm_crypt.o
|
||||
obj-$(CONFIG_CRASH_DUMP_KUNIT_TEST) += crash_core_test.o
|
||||
obj-$(CONFIG_KEXEC) += kexec.o
|
||||
obj-$(CONFIG_KEXEC_FILE) += kexec_file.o
|
||||
obj-$(CONFIG_KEXEC_ELF) += kexec_elf.o
|
||||
|
||||
+18
-12
@@ -22,6 +22,7 @@
|
||||
#include <linux/btf.h>
|
||||
#include <linux/objtool.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/panic.h>
|
||||
|
||||
#include <asm/page.h>
|
||||
#include <asm/sections.h>
|
||||
@@ -143,17 +144,7 @@ STACK_FRAME_NON_STANDARD(__crash_kexec);
|
||||
|
||||
__bpf_kfunc void crash_kexec(struct pt_regs *regs)
|
||||
{
|
||||
int old_cpu, this_cpu;
|
||||
|
||||
/*
|
||||
* Only one CPU is allowed to execute the crash_kexec() code as with
|
||||
* panic(). Otherwise parallel calls of panic() and crash_kexec()
|
||||
* may stop each other. To exclude them, we use panic_cpu here too.
|
||||
*/
|
||||
old_cpu = PANIC_CPU_INVALID;
|
||||
this_cpu = raw_smp_processor_id();
|
||||
|
||||
if (atomic_try_cmpxchg(&panic_cpu, &old_cpu, this_cpu)) {
|
||||
if (panic_try_start()) {
|
||||
/* This is the 1st CPU which comes here, so go ahead. */
|
||||
__crash_kexec(regs);
|
||||
|
||||
@@ -161,7 +152,7 @@ __bpf_kfunc void crash_kexec(struct pt_regs *regs)
|
||||
* Reset panic_cpu to allow another panic()/crash_kexec()
|
||||
* call.
|
||||
*/
|
||||
atomic_set(&panic_cpu, PANIC_CPU_INVALID);
|
||||
panic_reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -274,6 +265,20 @@ int crash_prepare_elf64_headers(struct crash_mem *mem, int need_kernel_map,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* crash_exclude_mem_range - exclude a mem range for existing ranges
|
||||
* @mem: mem->range contains an array of ranges sorted in ascending order
|
||||
* @mstart: the start of to-be-excluded range
|
||||
* @mend: the start of to-be-excluded range
|
||||
*
|
||||
* If you are unsure if a range split will happen, to avoid function call
|
||||
* failure because of -ENOMEM, always make sure
|
||||
* mem->max_nr_ranges == mem->nr_ranges + 1
|
||||
* before calling the function each time.
|
||||
*
|
||||
* returns 0 if a memory range is excluded successfully
|
||||
* return -ENOMEM if mem->ranges doesn't have space to hold split ranges
|
||||
*/
|
||||
int crash_exclude_mem_range(struct crash_mem *mem,
|
||||
unsigned long long mstart, unsigned long long mend)
|
||||
{
|
||||
@@ -333,6 +338,7 @@ int crash_exclude_mem_range(struct crash_mem *mem,
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(crash_exclude_mem_range);
|
||||
|
||||
ssize_t crash_get_memory_size(void)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,343 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
#include <kunit/test.h>
|
||||
#include <linux/crash_core.h> // For struct crash_mem and struct range if defined there
|
||||
|
||||
// Helper to create and initialize crash_mem
|
||||
static struct crash_mem *create_crash_mem(struct kunit *test, unsigned int max_ranges,
|
||||
unsigned int nr_initial_ranges,
|
||||
const struct range *initial_ranges)
|
||||
{
|
||||
struct crash_mem *mem;
|
||||
size_t alloc_size;
|
||||
|
||||
// Check if max_ranges can even hold initial_ranges
|
||||
if (max_ranges < nr_initial_ranges) {
|
||||
kunit_err(test, "max_ranges (%u) < nr_initial_ranges (%u)\n",
|
||||
max_ranges, nr_initial_ranges);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
alloc_size = sizeof(struct crash_mem) + (size_t)max_ranges * sizeof(struct range);
|
||||
mem = kunit_kzalloc(test, alloc_size, GFP_KERNEL);
|
||||
if (!mem) {
|
||||
kunit_err(test, "Failed to allocate crash_mem\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
mem->max_nr_ranges = max_ranges;
|
||||
mem->nr_ranges = nr_initial_ranges;
|
||||
if (initial_ranges && nr_initial_ranges > 0) {
|
||||
memcpy(mem->ranges, initial_ranges,
|
||||
nr_initial_ranges * sizeof(struct range));
|
||||
}
|
||||
|
||||
return mem;
|
||||
}
|
||||
|
||||
// Helper to compare ranges for assertions
|
||||
static void assert_ranges_equal(struct kunit *test,
|
||||
const struct range *actual_ranges,
|
||||
unsigned int actual_nr_ranges,
|
||||
const struct range *expected_ranges,
|
||||
unsigned int expected_nr_ranges,
|
||||
const char *case_name)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
KUNIT_ASSERT_EQ_MSG(test, expected_nr_ranges, actual_nr_ranges,
|
||||
"%s: Number of ranges mismatch.", case_name);
|
||||
|
||||
for (i = 0; i < expected_nr_ranges; i++) {
|
||||
KUNIT_ASSERT_EQ_MSG(test, expected_ranges[i].start, actual_ranges[i].start,
|
||||
"%s: Range %u start mismatch.", case_name, i);
|
||||
KUNIT_ASSERT_EQ_MSG(test, expected_ranges[i].end, actual_ranges[i].end,
|
||||
"%s: Range %u end mismatch.", case_name, i);
|
||||
}
|
||||
}
|
||||
|
||||
// Structure for test parameters
|
||||
struct exclude_test_param {
|
||||
const char *description;
|
||||
unsigned long long exclude_start;
|
||||
unsigned long long exclude_end;
|
||||
unsigned int initial_max_ranges;
|
||||
const struct range *initial_ranges;
|
||||
unsigned int initial_nr_ranges;
|
||||
const struct range *expected_ranges;
|
||||
unsigned int expected_nr_ranges;
|
||||
int expected_ret;
|
||||
};
|
||||
|
||||
static void run_exclude_test_case(struct kunit *test, const struct exclude_test_param *params)
|
||||
{
|
||||
struct crash_mem *mem;
|
||||
int ret;
|
||||
|
||||
kunit_info(test, "%s", params->description);
|
||||
|
||||
mem = create_crash_mem(test, params->initial_max_ranges,
|
||||
params->initial_nr_ranges, params->initial_ranges);
|
||||
if (!mem)
|
||||
return; // Error already logged by create_crash_mem or kunit_kzalloc
|
||||
|
||||
ret = crash_exclude_mem_range(mem, params->exclude_start, params->exclude_end);
|
||||
|
||||
KUNIT_ASSERT_EQ_MSG(test, params->expected_ret, ret,
|
||||
"%s: Return value mismatch.", params->description);
|
||||
|
||||
if (params->expected_ret == 0) {
|
||||
assert_ranges_equal(test, mem->ranges, mem->nr_ranges,
|
||||
params->expected_ranges, params->expected_nr_ranges,
|
||||
params->description);
|
||||
} else {
|
||||
// If an error is expected, nr_ranges might still be relevant to check
|
||||
// depending on the exact point of failure. For ENOMEM on split,
|
||||
// nr_ranges shouldn't have changed.
|
||||
KUNIT_ASSERT_EQ_MSG(test, params->initial_nr_ranges,
|
||||
mem->nr_ranges,
|
||||
"%s: Number of ranges mismatch on error.",
|
||||
params->description);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Test Strategy 1: One to-be-excluded range A and one existing range B.
|
||||
*
|
||||
* Exhaust all possibilities of the position of A regarding B.
|
||||
*/
|
||||
|
||||
static const struct range single_range_b = { .start = 100, .end = 199 };
|
||||
|
||||
static const struct exclude_test_param exclude_single_range_test_data[] = {
|
||||
{
|
||||
.description = "1.1: A is left of B, no overlap",
|
||||
.exclude_start = 10, .exclude_end = 50,
|
||||
.initial_max_ranges = 1,
|
||||
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
|
||||
.expected_ranges = &single_range_b, .expected_nr_ranges = 1,
|
||||
.expected_ret = 0,
|
||||
},
|
||||
{
|
||||
.description = "1.2: A's right boundary touches B's left boundary",
|
||||
.exclude_start = 10, .exclude_end = 99,
|
||||
.initial_max_ranges = 1,
|
||||
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
|
||||
.expected_ranges = &single_range_b, .expected_nr_ranges = 1,
|
||||
.expected_ret = 0,
|
||||
},
|
||||
{
|
||||
.description = "1.3: A overlaps B's left part",
|
||||
.exclude_start = 50, .exclude_end = 149,
|
||||
.initial_max_ranges = 1,
|
||||
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
|
||||
.expected_ranges = (const struct range[]){{ .start = 150, .end = 199 }},
|
||||
.expected_nr_ranges = 1,
|
||||
.expected_ret = 0,
|
||||
},
|
||||
{
|
||||
.description = "1.4: A is completely inside B",
|
||||
.exclude_start = 120, .exclude_end = 179,
|
||||
.initial_max_ranges = 2, // Needs space for split
|
||||
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
|
||||
.expected_ranges = (const struct range[]){
|
||||
{ .start = 100, .end = 119 },
|
||||
{ .start = 180, .end = 199 }
|
||||
},
|
||||
.expected_nr_ranges = 2,
|
||||
.expected_ret = 0,
|
||||
},
|
||||
{
|
||||
.description = "1.5: A overlaps B's right part",
|
||||
.exclude_start = 150, .exclude_end = 249,
|
||||
.initial_max_ranges = 1,
|
||||
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
|
||||
.expected_ranges = (const struct range[]){{ .start = 100, .end = 149 }},
|
||||
.expected_nr_ranges = 1,
|
||||
.expected_ret = 0,
|
||||
},
|
||||
{
|
||||
.description = "1.6: A's left boundary touches B's right boundary",
|
||||
.exclude_start = 200, .exclude_end = 250,
|
||||
.initial_max_ranges = 1,
|
||||
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
|
||||
.expected_ranges = &single_range_b, .expected_nr_ranges = 1,
|
||||
.expected_ret = 0,
|
||||
},
|
||||
{
|
||||
.description = "1.7: A is right of B, no overlap",
|
||||
.exclude_start = 250, .exclude_end = 300,
|
||||
.initial_max_ranges = 1,
|
||||
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
|
||||
.expected_ranges = &single_range_b, .expected_nr_ranges = 1,
|
||||
.expected_ret = 0,
|
||||
},
|
||||
{
|
||||
.description = "1.8: A completely covers B and extends beyond",
|
||||
.exclude_start = 50, .exclude_end = 250,
|
||||
.initial_max_ranges = 1,
|
||||
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
|
||||
.expected_ranges = NULL, .expected_nr_ranges = 0,
|
||||
.expected_ret = 0,
|
||||
},
|
||||
{
|
||||
.description = "1.9: A covers B and extends to the left",
|
||||
.exclude_start = 50, .exclude_end = 199, // A ends exactly where B ends
|
||||
.initial_max_ranges = 1,
|
||||
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
|
||||
.expected_ranges = NULL, .expected_nr_ranges = 0,
|
||||
.expected_ret = 0,
|
||||
},
|
||||
{
|
||||
.description = "1.10: A covers B and extends to the right",
|
||||
.exclude_start = 100, .exclude_end = 250, // A starts exactly where B starts
|
||||
.initial_max_ranges = 1,
|
||||
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
|
||||
.expected_ranges = NULL, .expected_nr_ranges = 0,
|
||||
.expected_ret = 0,
|
||||
},
|
||||
{
|
||||
.description = "1.11: A is identical to B",
|
||||
.exclude_start = 100, .exclude_end = 199,
|
||||
.initial_max_ranges = 1,
|
||||
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
|
||||
.expected_ranges = NULL, .expected_nr_ranges = 0,
|
||||
.expected_ret = 0,
|
||||
},
|
||||
{
|
||||
.description = "1.12: A is a point, left of B, no overlap",
|
||||
.exclude_start = 10, .exclude_end = 10,
|
||||
.initial_max_ranges = 1,
|
||||
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
|
||||
.expected_ranges = &single_range_b, .expected_nr_ranges = 1,
|
||||
.expected_ret = 0,
|
||||
},
|
||||
{
|
||||
.description = "1.13: A is a point, at start of B",
|
||||
.exclude_start = 100, .exclude_end = 100,
|
||||
.initial_max_ranges = 1,
|
||||
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
|
||||
.expected_ranges = (const struct range[]){{ .start = 101, .end = 199 }},
|
||||
.expected_nr_ranges = 1,
|
||||
.expected_ret = 0,
|
||||
},
|
||||
{
|
||||
.description = "1.14: A is a point, in middle of B (causes split)",
|
||||
.exclude_start = 150, .exclude_end = 150,
|
||||
.initial_max_ranges = 2, // Needs space for split
|
||||
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
|
||||
.expected_ranges = (const struct range[]){
|
||||
{ .start = 100, .end = 149 },
|
||||
{ .start = 151, .end = 199 }
|
||||
},
|
||||
.expected_nr_ranges = 2,
|
||||
.expected_ret = 0,
|
||||
},
|
||||
{
|
||||
.description = "1.15: A is a point, at end of B",
|
||||
.exclude_start = 199, .exclude_end = 199,
|
||||
.initial_max_ranges = 1,
|
||||
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
|
||||
.expected_ranges = (const struct range[]){{ .start = 100, .end = 198 }},
|
||||
.expected_nr_ranges = 1,
|
||||
.expected_ret = 0,
|
||||
},
|
||||
{
|
||||
.description = "1.16: A is a point, right of B, no overlap",
|
||||
.exclude_start = 250, .exclude_end = 250,
|
||||
.initial_max_ranges = 1,
|
||||
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
|
||||
.expected_ranges = &single_range_b, .expected_nr_ranges = 1,
|
||||
.expected_ret = 0,
|
||||
},
|
||||
// ENOMEM case for single range split
|
||||
{
|
||||
.description = "1.17: A completely inside B (split), no space (ENOMEM)",
|
||||
.exclude_start = 120, .exclude_end = 179,
|
||||
.initial_max_ranges = 1, // Not enough for split
|
||||
.initial_ranges = &single_range_b, .initial_nr_ranges = 1,
|
||||
.expected_ranges = NULL, // Not checked on error by assert_ranges_equal for content
|
||||
.expected_nr_ranges = 1, // Should remain unchanged
|
||||
.expected_ret = -ENOMEM,
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
static void exclude_single_range_test(struct kunit *test)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(exclude_single_range_test_data); i++) {
|
||||
kunit_log(KERN_INFO, test, "Running: %s", exclude_single_range_test_data[i].description);
|
||||
run_exclude_test_case(test, &exclude_single_range_test_data[i]);
|
||||
// KUnit will stop on first KUNIT_ASSERT failure within run_exclude_test_case
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Test Strategy 2: Regression test.
|
||||
*/
|
||||
|
||||
static const struct exclude_test_param exclude_range_regression_test_data[] = {
|
||||
// Test data from commit a2e9a95d2190
|
||||
{
|
||||
.description = "2.1: exclude low 1M",
|
||||
.exclude_start = 0, .exclude_end = (1 << 20) - 1,
|
||||
.initial_max_ranges = 3,
|
||||
.initial_ranges = (const struct range[]){
|
||||
{ .start = 0, .end = 0x3efff },
|
||||
{ .start = 0x3f000, .end = 0x3ffff },
|
||||
{ .start = 0x40000, .end = 0x9ffff }
|
||||
},
|
||||
.initial_nr_ranges = 3,
|
||||
.expected_nr_ranges = 0,
|
||||
.expected_ret = 0,
|
||||
},
|
||||
// Test data from https://lore.kernel.org/all/ZXrY7QbXAlxydsSC@MiWiFi-R3L-srv/T/#u
|
||||
{
|
||||
.description = "2.2: when range out of bound",
|
||||
.exclude_start = 100, .exclude_end = 200,
|
||||
.initial_max_ranges = 3,
|
||||
.initial_ranges = (const struct range[]){
|
||||
{ .start = 1, .end = 299 },
|
||||
{ .start = 401, .end = 1000 },
|
||||
{ .start = 1001, .end = 2000 }
|
||||
},
|
||||
.initial_nr_ranges = 3,
|
||||
.expected_ranges = NULL, // Not checked on error by assert_ranges_equal for content
|
||||
.expected_nr_ranges = 3, // Should remain unchanged
|
||||
.expected_ret = -ENOMEM
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
|
||||
static void exclude_range_regression_test(struct kunit *test)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(exclude_range_regression_test_data); i++) {
|
||||
kunit_log(KERN_INFO, test, "Running: %s", exclude_range_regression_test_data[i].description);
|
||||
run_exclude_test_case(test, &exclude_range_regression_test_data[i]);
|
||||
// KUnit will stop on first KUNIT_ASSERT failure within run_exclude_test_case
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* KUnit Test Suite
|
||||
*/
|
||||
static struct kunit_case crash_exclude_mem_range_test_cases[] = {
|
||||
KUNIT_CASE(exclude_single_range_test),
|
||||
KUNIT_CASE(exclude_range_regression_test),
|
||||
{}
|
||||
};
|
||||
|
||||
static struct kunit_suite crash_exclude_mem_range_suite = {
|
||||
.name = "crash_exclude_mem_range_tests",
|
||||
.test_cases = crash_exclude_mem_range_test_cases,
|
||||
// .init and .exit can be NULL if not needed globally for the suite
|
||||
};
|
||||
|
||||
kunit_test_suite(crash_exclude_mem_range_suite);
|
||||
|
||||
MODULE_DESCRIPTION("crash dump KUnit test suite");
|
||||
MODULE_LICENSE("GPL");
|
||||
+6
-12
@@ -2132,9 +2132,7 @@ __latent_entropy struct task_struct *copy_process(
|
||||
|
||||
p->pagefault_disabled = 0;
|
||||
|
||||
#ifdef CONFIG_LOCKDEP
|
||||
lockdep_init_task(p);
|
||||
#endif
|
||||
|
||||
p->blocked_on = NULL; /* not blocked yet */
|
||||
|
||||
@@ -2547,11 +2545,9 @@ struct task_struct * __init fork_idle(int cpu)
|
||||
struct task_struct *create_io_thread(int (*fn)(void *), void *arg, int node)
|
||||
{
|
||||
unsigned long flags = CLONE_FS|CLONE_FILES|CLONE_SIGHAND|CLONE_THREAD|
|
||||
CLONE_IO;
|
||||
CLONE_IO|CLONE_VM|CLONE_UNTRACED;
|
||||
struct kernel_clone_args args = {
|
||||
.flags = ((lower_32_bits(flags) | CLONE_VM |
|
||||
CLONE_UNTRACED) & ~CSIGNAL),
|
||||
.exit_signal = (lower_32_bits(flags) & CSIGNAL),
|
||||
.flags = flags,
|
||||
.fn = fn,
|
||||
.fn_arg = arg,
|
||||
.io_thread = 1,
|
||||
@@ -2663,9 +2659,8 @@ pid_t kernel_thread(int (*fn)(void *), void *arg, const char *name,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct kernel_clone_args args = {
|
||||
.flags = ((lower_32_bits(flags) | CLONE_VM |
|
||||
CLONE_UNTRACED) & ~CSIGNAL),
|
||||
.exit_signal = (lower_32_bits(flags) & CSIGNAL),
|
||||
.flags = ((flags | CLONE_VM | CLONE_UNTRACED) & ~CSIGNAL),
|
||||
.exit_signal = (flags & CSIGNAL),
|
||||
.fn = fn,
|
||||
.fn_arg = arg,
|
||||
.name = name,
|
||||
@@ -2681,9 +2676,8 @@ pid_t kernel_thread(int (*fn)(void *), void *arg, const char *name,
|
||||
pid_t user_mode_thread(int (*fn)(void *), void *arg, unsigned long flags)
|
||||
{
|
||||
struct kernel_clone_args args = {
|
||||
.flags = ((lower_32_bits(flags) | CLONE_VM |
|
||||
CLONE_UNTRACED) & ~CSIGNAL),
|
||||
.exit_signal = (lower_32_bits(flags) & CSIGNAL),
|
||||
.flags = ((flags | CLONE_VM | CLONE_UNTRACED) & ~CSIGNAL),
|
||||
.exit_signal = (flags & CSIGNAL),
|
||||
.fn = fn,
|
||||
.fn_arg = arg,
|
||||
};
|
||||
|
||||
+41
-37
@@ -95,9 +95,41 @@ static struct notifier_block panic_block = {
|
||||
.notifier_call = hung_task_panic,
|
||||
};
|
||||
|
||||
static bool task_is_hung(struct task_struct *t, unsigned long timeout)
|
||||
{
|
||||
unsigned long switch_count = t->nvcsw + t->nivcsw;
|
||||
unsigned int state = READ_ONCE(t->__state);
|
||||
|
||||
/*
|
||||
* skip the TASK_KILLABLE tasks -- these can be killed
|
||||
* skip the TASK_IDLE tasks -- those are genuinely idle
|
||||
* skip the TASK_FROZEN task -- it reasonably stops scheduling by freezer
|
||||
*/
|
||||
if (!(state & TASK_UNINTERRUPTIBLE) ||
|
||||
(state & (TASK_WAKEKILL | TASK_NOLOAD | TASK_FROZEN)))
|
||||
return false;
|
||||
|
||||
/*
|
||||
* When a freshly created task is scheduled once, changes its state to
|
||||
* TASK_UNINTERRUPTIBLE without having ever been switched out once, it
|
||||
* musn't be checked.
|
||||
*/
|
||||
if (unlikely(!switch_count))
|
||||
return false;
|
||||
|
||||
if (switch_count != t->last_switch_count) {
|
||||
t->last_switch_count = switch_count;
|
||||
t->last_switch_time = jiffies;
|
||||
return false;
|
||||
}
|
||||
if (time_is_after_jiffies(t->last_switch_time + timeout * HZ))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_DETECT_HUNG_TASK_BLOCKER
|
||||
static void debug_show_blocker(struct task_struct *task)
|
||||
static void debug_show_blocker(struct task_struct *task, unsigned long timeout)
|
||||
{
|
||||
struct task_struct *g, *t;
|
||||
unsigned long owner, blocker, blocker_type;
|
||||
@@ -174,41 +206,21 @@ static void debug_show_blocker(struct task_struct *task)
|
||||
t->pid, rwsem_blocked_by);
|
||||
break;
|
||||
}
|
||||
sched_show_task(t);
|
||||
/* Avoid duplicated task dump, skip if the task is also hung. */
|
||||
if (!task_is_hung(t, timeout))
|
||||
sched_show_task(t);
|
||||
return;
|
||||
}
|
||||
}
|
||||
#else
|
||||
static inline void debug_show_blocker(struct task_struct *task)
|
||||
static inline void debug_show_blocker(struct task_struct *task, unsigned long timeout)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
static void check_hung_task(struct task_struct *t, unsigned long timeout)
|
||||
{
|
||||
unsigned long switch_count = t->nvcsw + t->nivcsw;
|
||||
|
||||
/*
|
||||
* Ensure the task is not frozen.
|
||||
* Also, skip vfork and any other user process that freezer should skip.
|
||||
*/
|
||||
if (unlikely(READ_ONCE(t->__state) & TASK_FROZEN))
|
||||
return;
|
||||
|
||||
/*
|
||||
* When a freshly created task is scheduled once, changes its state to
|
||||
* TASK_UNINTERRUPTIBLE without having ever been switched out once, it
|
||||
* musn't be checked.
|
||||
*/
|
||||
if (unlikely(!switch_count))
|
||||
return;
|
||||
|
||||
if (switch_count != t->last_switch_count) {
|
||||
t->last_switch_count = switch_count;
|
||||
t->last_switch_time = jiffies;
|
||||
return;
|
||||
}
|
||||
if (time_is_after_jiffies(t->last_switch_time + timeout * HZ))
|
||||
if (!task_is_hung(t, timeout))
|
||||
return;
|
||||
|
||||
/*
|
||||
@@ -243,7 +255,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);
|
||||
debug_show_blocker(t, timeout);
|
||||
hung_task_show_lock = true;
|
||||
|
||||
if (sysctl_hung_task_all_cpu_backtrace)
|
||||
@@ -299,7 +311,6 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
|
||||
hung_task_show_lock = false;
|
||||
rcu_read_lock();
|
||||
for_each_process_thread(g, t) {
|
||||
unsigned int state;
|
||||
|
||||
if (!max_count--)
|
||||
goto unlock;
|
||||
@@ -308,15 +319,8 @@ static void check_hung_uninterruptible_tasks(unsigned long timeout)
|
||||
goto unlock;
|
||||
last_break = jiffies;
|
||||
}
|
||||
/*
|
||||
* skip the TASK_KILLABLE tasks -- these can be killed
|
||||
* skip the TASK_IDLE tasks -- those are genuinely idle
|
||||
*/
|
||||
state = READ_ONCE(t->__state);
|
||||
if ((state & TASK_UNINTERRUPTIBLE) &&
|
||||
!(state & TASK_WAKEKILL) &&
|
||||
!(state & TASK_NOLOAD))
|
||||
check_hung_task(t, timeout);
|
||||
|
||||
check_hung_task(t, timeout);
|
||||
}
|
||||
unlock:
|
||||
rcu_read_unlock();
|
||||
|
||||
@@ -264,7 +264,7 @@ static int test_kallsyms_basic_function(void)
|
||||
char namebuf[KSYM_NAME_LEN];
|
||||
struct test_stat *stat, *stat2;
|
||||
|
||||
stat = kmalloc(sizeof(*stat) * 2, GFP_KERNEL);
|
||||
stat = kmalloc_array(2, sizeof(*stat), GFP_KERNEL);
|
||||
if (!stat)
|
||||
return -ENOMEM;
|
||||
stat2 = stat + 1;
|
||||
|
||||
@@ -978,6 +978,15 @@ static void kcov_move_area(enum kcov_mode mode, void *dst_area,
|
||||
memcpy(dst_entries, src_entries, bytes_to_move);
|
||||
entries_moved = bytes_to_move >> entry_size_log;
|
||||
|
||||
/*
|
||||
* A write memory barrier is required here, to ensure
|
||||
* that the writes from the memcpy() are visible before
|
||||
* the count is updated. Without this, it is possible for
|
||||
* a user to observe a new count value but stale
|
||||
* coverage data.
|
||||
*/
|
||||
smp_wmb();
|
||||
|
||||
switch (mode) {
|
||||
case KCOV_MODE_TRACE_PC:
|
||||
WRITE_ONCE(*(unsigned long *)dst_area, dst_len + entries_moved);
|
||||
|
||||
@@ -233,7 +233,6 @@ struct kimage *do_kimage_alloc_init(void)
|
||||
if (!image)
|
||||
return NULL;
|
||||
|
||||
image->head = 0;
|
||||
image->entry = &image->head;
|
||||
image->last_entry = &image->head;
|
||||
image->control_page = ~0; /* By default this does not apply */
|
||||
|
||||
@@ -255,6 +255,7 @@ kimage_file_prepare_segments(struct kimage *image, int kernel_fd, int initrd_fd,
|
||||
}
|
||||
|
||||
image->no_cma = !!(flags & KEXEC_FILE_NO_CMA);
|
||||
image->force_dtb = flags & KEXEC_FILE_FORCE_DTB;
|
||||
|
||||
if (cmdline_len) {
|
||||
image->cmdline_buf = memdup_user(cmdline_ptr, cmdline_len);
|
||||
|
||||
+21
-1
@@ -987,6 +987,26 @@ static const void *kho_get_fdt(void)
|
||||
return kho_in.fdt_phys ? phys_to_virt(kho_in.fdt_phys) : NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* is_kho_boot - check if current kernel was booted via KHO-enabled
|
||||
* kexec
|
||||
*
|
||||
* This function checks if the current kernel was loaded through a kexec
|
||||
* operation with KHO enabled, by verifying that a valid KHO FDT
|
||||
* was passed.
|
||||
*
|
||||
* Note: This function returns reliable results only after
|
||||
* kho_populate() has been called during early boot. Before that,
|
||||
* it may return false even if KHO data is present.
|
||||
*
|
||||
* Return: true if booted via KHO-enabled kexec, false otherwise
|
||||
*/
|
||||
bool is_kho_boot(void)
|
||||
{
|
||||
return !!kho_get_fdt();
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(is_kho_boot);
|
||||
|
||||
/**
|
||||
* kho_retrieve_subtree - retrieve a preserved sub FDT by its name.
|
||||
* @name: the name of the sub FDT passed to kho_add_subtree().
|
||||
@@ -1269,7 +1289,7 @@ int kho_fill_kimage(struct kimage *image)
|
||||
int err = 0;
|
||||
struct kexec_buf scratch;
|
||||
|
||||
if (!kho_enable)
|
||||
if (!kho_out.finalized)
|
||||
return 0;
|
||||
|
||||
image->kho.fdt = page_to_phys(kho_out.ser.fdt);
|
||||
|
||||
+103
-26
@@ -53,7 +53,7 @@ static unsigned int __read_mostly sysctl_oops_all_cpu_backtrace;
|
||||
#define sysctl_oops_all_cpu_backtrace 0
|
||||
#endif /* CONFIG_SMP */
|
||||
|
||||
int panic_on_oops = CONFIG_PANIC_ON_OOPS_VALUE;
|
||||
int panic_on_oops = IS_ENABLED(CONFIG_PANIC_ON_OOPS);
|
||||
static unsigned long tainted_mask =
|
||||
IS_ENABLED(CONFIG_RANDSTRUCT) ? (1 << TAINT_RANDSTRUCT) : 0;
|
||||
static int pause_on_oops;
|
||||
@@ -67,6 +67,7 @@ static unsigned int warn_limit __read_mostly;
|
||||
static bool panic_console_replay;
|
||||
|
||||
bool panic_triggering_all_cpu_backtrace;
|
||||
static bool panic_this_cpu_backtrace_printed;
|
||||
|
||||
int panic_timeout = CONFIG_PANIC_TIMEOUT;
|
||||
EXPORT_SYMBOL_GPL(panic_timeout);
|
||||
@@ -77,6 +78,11 @@ ATOMIC_NOTIFIER_HEAD(panic_notifier_list);
|
||||
|
||||
EXPORT_SYMBOL(panic_notifier_list);
|
||||
|
||||
static void panic_print_deprecated(void)
|
||||
{
|
||||
pr_info_once("Kernel: The 'panic_print' parameter is now deprecated. Please use 'panic_sys_info' and 'panic_console_replay' instead.\n");
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
|
||||
/*
|
||||
@@ -125,7 +131,7 @@ static int proc_taint(const struct ctl_table *table, int write,
|
||||
static int sysctl_panic_print_handler(const struct ctl_table *table, int write,
|
||||
void *buffer, size_t *lenp, loff_t *ppos)
|
||||
{
|
||||
pr_info_once("Kernel: 'panic_print' sysctl interface will be obsoleted by both 'panic_sys_info' and 'panic_console_replay'\n");
|
||||
panic_print_deprecated();
|
||||
return proc_doulongvec_minmax(table, write, buffer, lenp, ppos);
|
||||
}
|
||||
|
||||
@@ -294,6 +300,59 @@ void __weak crash_smp_send_stop(void)
|
||||
|
||||
atomic_t panic_cpu = ATOMIC_INIT(PANIC_CPU_INVALID);
|
||||
|
||||
bool panic_try_start(void)
|
||||
{
|
||||
int old_cpu, this_cpu;
|
||||
|
||||
/*
|
||||
* Only one CPU is allowed to execute the crash_kexec() code as with
|
||||
* panic(). Otherwise parallel calls of panic() and crash_kexec()
|
||||
* may stop each other. To exclude them, we use panic_cpu here too.
|
||||
*/
|
||||
old_cpu = PANIC_CPU_INVALID;
|
||||
this_cpu = raw_smp_processor_id();
|
||||
|
||||
return atomic_try_cmpxchg(&panic_cpu, &old_cpu, this_cpu);
|
||||
}
|
||||
EXPORT_SYMBOL(panic_try_start);
|
||||
|
||||
void panic_reset(void)
|
||||
{
|
||||
atomic_set(&panic_cpu, PANIC_CPU_INVALID);
|
||||
}
|
||||
EXPORT_SYMBOL(panic_reset);
|
||||
|
||||
bool panic_in_progress(void)
|
||||
{
|
||||
return unlikely(atomic_read(&panic_cpu) != PANIC_CPU_INVALID);
|
||||
}
|
||||
EXPORT_SYMBOL(panic_in_progress);
|
||||
|
||||
/* Return true if a panic is in progress on the current CPU. */
|
||||
bool panic_on_this_cpu(void)
|
||||
{
|
||||
/*
|
||||
* We can use raw_smp_processor_id() here because it is impossible for
|
||||
* the task to be migrated to the panic_cpu, or away from it. If
|
||||
* panic_cpu has already been set, and we're not currently executing on
|
||||
* that CPU, then we never will be.
|
||||
*/
|
||||
return unlikely(atomic_read(&panic_cpu) == raw_smp_processor_id());
|
||||
}
|
||||
EXPORT_SYMBOL(panic_on_this_cpu);
|
||||
|
||||
/*
|
||||
* Return true if a panic is in progress on a remote CPU.
|
||||
*
|
||||
* On true, the local CPU should immediately release any printing resources
|
||||
* that may be needed by the panic CPU.
|
||||
*/
|
||||
bool panic_on_other_cpu(void)
|
||||
{
|
||||
return (panic_in_progress() && !panic_on_this_cpu());
|
||||
}
|
||||
EXPORT_SYMBOL(panic_on_other_cpu);
|
||||
|
||||
/*
|
||||
* A variant of panic() called from NMI context. We return if we've already
|
||||
* panicked on this CPU. If another CPU already panicked, loop in
|
||||
@@ -302,15 +361,9 @@ atomic_t panic_cpu = ATOMIC_INIT(PANIC_CPU_INVALID);
|
||||
*/
|
||||
void nmi_panic(struct pt_regs *regs, const char *msg)
|
||||
{
|
||||
int old_cpu, this_cpu;
|
||||
|
||||
old_cpu = PANIC_CPU_INVALID;
|
||||
this_cpu = raw_smp_processor_id();
|
||||
|
||||
/* atomic_try_cmpxchg updates old_cpu on failure */
|
||||
if (atomic_try_cmpxchg(&panic_cpu, &old_cpu, this_cpu))
|
||||
if (panic_try_start())
|
||||
panic("%s", msg);
|
||||
else if (old_cpu != this_cpu)
|
||||
else if (panic_on_other_cpu())
|
||||
nmi_panic_self_stop(regs);
|
||||
}
|
||||
EXPORT_SYMBOL(nmi_panic);
|
||||
@@ -328,6 +381,19 @@ void check_panic_on_warn(const char *origin)
|
||||
origin, limit);
|
||||
}
|
||||
|
||||
static void panic_trigger_all_cpu_backtrace(void)
|
||||
{
|
||||
/* Temporary allow non-panic CPUs to write their backtraces. */
|
||||
panic_triggering_all_cpu_backtrace = true;
|
||||
|
||||
if (panic_this_cpu_backtrace_printed)
|
||||
trigger_allbutcpu_cpu_backtrace(raw_smp_processor_id());
|
||||
else
|
||||
trigger_all_cpu_backtrace();
|
||||
|
||||
panic_triggering_all_cpu_backtrace = false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Helper that triggers the NMI backtrace (if set in panic_print)
|
||||
* and then performs the secondary CPUs shutdown - we cannot have
|
||||
@@ -335,12 +401,8 @@ void check_panic_on_warn(const char *origin)
|
||||
*/
|
||||
static void panic_other_cpus_shutdown(bool crash_kexec)
|
||||
{
|
||||
if (panic_print & SYS_INFO_ALL_CPU_BT) {
|
||||
/* Temporary allow non-panic CPUs to write their backtraces. */
|
||||
panic_triggering_all_cpu_backtrace = true;
|
||||
trigger_all_cpu_backtrace();
|
||||
panic_triggering_all_cpu_backtrace = false;
|
||||
}
|
||||
if (panic_print & SYS_INFO_ALL_CPU_BT)
|
||||
panic_trigger_all_cpu_backtrace();
|
||||
|
||||
/*
|
||||
* Note that smp_send_stop() is the usual SMP shutdown function,
|
||||
@@ -368,7 +430,6 @@ void vpanic(const char *fmt, va_list args)
|
||||
static char buf[1024];
|
||||
long i, i_next = 0, len;
|
||||
int state = 0;
|
||||
int old_cpu, this_cpu;
|
||||
bool _crash_kexec_post_notifiers = crash_kexec_post_notifiers;
|
||||
|
||||
if (panic_on_warn) {
|
||||
@@ -405,13 +466,10 @@ void vpanic(const char *fmt, va_list args)
|
||||
* `old_cpu == this_cpu' means we came from nmi_panic() which sets
|
||||
* panic_cpu to this CPU. In this case, this is also the 1st CPU.
|
||||
*/
|
||||
old_cpu = PANIC_CPU_INVALID;
|
||||
this_cpu = raw_smp_processor_id();
|
||||
|
||||
/* atomic_try_cmpxchg updates old_cpu on failure */
|
||||
if (atomic_try_cmpxchg(&panic_cpu, &old_cpu, this_cpu)) {
|
||||
if (panic_try_start()) {
|
||||
/* go ahead */
|
||||
} else if (old_cpu != this_cpu)
|
||||
} else if (panic_on_other_cpu())
|
||||
panic_smp_self_stop();
|
||||
|
||||
console_verbose();
|
||||
@@ -422,13 +480,15 @@ void vpanic(const char *fmt, va_list args)
|
||||
buf[len - 1] = '\0';
|
||||
|
||||
pr_emerg("Kernel panic - not syncing: %s\n", buf);
|
||||
#ifdef CONFIG_DEBUG_BUGVERBOSE
|
||||
/*
|
||||
* Avoid nested stack-dumping if a panic occurs during oops processing
|
||||
*/
|
||||
if (!test_taint(TAINT_DIE) && oops_in_progress <= 1)
|
||||
if (test_taint(TAINT_DIE) || oops_in_progress > 1) {
|
||||
panic_this_cpu_backtrace_printed = true;
|
||||
} else if (IS_ENABLED(CONFIG_DEBUG_BUGVERBOSE)) {
|
||||
dump_stack();
|
||||
#endif
|
||||
panic_this_cpu_backtrace_printed = true;
|
||||
}
|
||||
|
||||
/*
|
||||
* If kgdb is enabled, give it a chance to run before we stop all
|
||||
@@ -937,12 +997,29 @@ EXPORT_SYMBOL(__stack_chk_fail);
|
||||
#endif
|
||||
|
||||
core_param(panic, panic_timeout, int, 0644);
|
||||
core_param(panic_print, panic_print, ulong, 0644);
|
||||
core_param(pause_on_oops, pause_on_oops, int, 0644);
|
||||
core_param(panic_on_warn, panic_on_warn, int, 0644);
|
||||
core_param(crash_kexec_post_notifiers, crash_kexec_post_notifiers, bool, 0644);
|
||||
core_param(panic_console_replay, panic_console_replay, bool, 0644);
|
||||
|
||||
static int panic_print_set(const char *val, const struct kernel_param *kp)
|
||||
{
|
||||
panic_print_deprecated();
|
||||
return param_set_ulong(val, kp);
|
||||
}
|
||||
|
||||
static int panic_print_get(char *val, const struct kernel_param *kp)
|
||||
{
|
||||
panic_print_deprecated();
|
||||
return param_get_ulong(val, kp);
|
||||
}
|
||||
|
||||
static const struct kernel_param_ops panic_print_ops = {
|
||||
.set = panic_print_set,
|
||||
.get = panic_print_get,
|
||||
};
|
||||
__core_param_cb(panic_print, &panic_print_ops, &panic_print, 0644);
|
||||
|
||||
static int __init oops_setup(char *s)
|
||||
{
|
||||
if (!s)
|
||||
|
||||
@@ -332,7 +332,6 @@ struct printk_message {
|
||||
unsigned long dropped;
|
||||
};
|
||||
|
||||
bool other_cpu_in_panic(void);
|
||||
bool printk_get_next_message(struct printk_message *pmsg, u64 seq,
|
||||
bool is_extended, bool may_supress);
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <linux/irqflags.h>
|
||||
#include <linux/kthread.h>
|
||||
#include <linux/minmax.h>
|
||||
#include <linux/panic.h>
|
||||
#include <linux/percpu.h>
|
||||
#include <linux/preempt.h>
|
||||
#include <linux/slab.h>
|
||||
@@ -254,7 +255,7 @@ static int nbcon_context_try_acquire_direct(struct nbcon_context *ctxt,
|
||||
* opportunity to perform any necessary cleanup if they were
|
||||
* interrupted by the panic CPU while printing.
|
||||
*/
|
||||
if (other_cpu_in_panic() &&
|
||||
if (panic_on_other_cpu() &&
|
||||
(!is_reacquire || cur->unsafe_takeover)) {
|
||||
return -EPERM;
|
||||
}
|
||||
@@ -309,7 +310,7 @@ static bool nbcon_waiter_matches(struct nbcon_state *cur, int expected_prio)
|
||||
* Event #2 implies the new context is PANIC.
|
||||
* Event #3 occurs when panic() has flushed the console.
|
||||
* Event #4 occurs when a non-panic CPU reacquires.
|
||||
* Event #5 is not possible due to the other_cpu_in_panic() check
|
||||
* Event #5 is not possible due to the panic_on_other_cpu() check
|
||||
* in nbcon_context_try_acquire_handover().
|
||||
*/
|
||||
|
||||
@@ -348,7 +349,7 @@ static int nbcon_context_try_acquire_requested(struct nbcon_context *ctxt,
|
||||
struct nbcon_state new;
|
||||
|
||||
/* Note that the caller must still remove the request! */
|
||||
if (other_cpu_in_panic())
|
||||
if (panic_on_other_cpu())
|
||||
return -EPERM;
|
||||
|
||||
/*
|
||||
@@ -446,7 +447,7 @@ static int nbcon_context_try_acquire_handover(struct nbcon_context *ctxt,
|
||||
* nbcon_waiter_matches(). In particular, the assumption that
|
||||
* lower priorities are ignored during panic.
|
||||
*/
|
||||
if (other_cpu_in_panic())
|
||||
if (panic_on_other_cpu())
|
||||
return -EPERM;
|
||||
|
||||
/* Handover is not possible on the same CPU. */
|
||||
@@ -589,7 +590,6 @@ static struct printk_buffers panic_nbcon_pbufs;
|
||||
*/
|
||||
static bool nbcon_context_try_acquire(struct nbcon_context *ctxt, bool is_reacquire)
|
||||
{
|
||||
unsigned int cpu = smp_processor_id();
|
||||
struct console *con = ctxt->console;
|
||||
struct nbcon_state cur;
|
||||
int err;
|
||||
@@ -614,7 +614,7 @@ out:
|
||||
/* Acquire succeeded. */
|
||||
|
||||
/* Assign the appropriate buffer for this context. */
|
||||
if (atomic_read(&panic_cpu) == cpu)
|
||||
if (panic_on_this_cpu())
|
||||
ctxt->pbufs = &panic_nbcon_pbufs;
|
||||
else
|
||||
ctxt->pbufs = con->pbufs;
|
||||
@@ -1394,7 +1394,7 @@ enum nbcon_prio nbcon_get_default_prio(void)
|
||||
{
|
||||
unsigned int *cpu_emergency_nesting;
|
||||
|
||||
if (this_cpu_in_panic())
|
||||
if (panic_on_this_cpu())
|
||||
return NBCON_PRIO_PANIC;
|
||||
|
||||
cpu_emergency_nesting = nbcon_get_cpu_emergency_nesting();
|
||||
|
||||
+5
-32
@@ -48,6 +48,7 @@
|
||||
#include <linux/sched/clock.h>
|
||||
#include <linux/sched/debug.h>
|
||||
#include <linux/sched/task_stack.h>
|
||||
#include <linux/panic.h>
|
||||
|
||||
#include <linux/uaccess.h>
|
||||
#include <asm/sections.h>
|
||||
@@ -345,34 +346,6 @@ static void __up_console_sem(unsigned long ip)
|
||||
}
|
||||
#define up_console_sem() __up_console_sem(_RET_IP_)
|
||||
|
||||
static bool panic_in_progress(void)
|
||||
{
|
||||
return unlikely(atomic_read(&panic_cpu) != PANIC_CPU_INVALID);
|
||||
}
|
||||
|
||||
/* Return true if a panic is in progress on the current CPU. */
|
||||
bool this_cpu_in_panic(void)
|
||||
{
|
||||
/*
|
||||
* We can use raw_smp_processor_id() here because it is impossible for
|
||||
* the task to be migrated to the panic_cpu, or away from it. If
|
||||
* panic_cpu has already been set, and we're not currently executing on
|
||||
* that CPU, then we never will be.
|
||||
*/
|
||||
return unlikely(atomic_read(&panic_cpu) == raw_smp_processor_id());
|
||||
}
|
||||
|
||||
/*
|
||||
* Return true if a panic is in progress on a remote CPU.
|
||||
*
|
||||
* On true, the local CPU should immediately release any printing resources
|
||||
* that may be needed by the panic CPU.
|
||||
*/
|
||||
bool other_cpu_in_panic(void)
|
||||
{
|
||||
return (panic_in_progress() && !this_cpu_in_panic());
|
||||
}
|
||||
|
||||
/*
|
||||
* This is used for debugging the mess that is the VT code by
|
||||
* keeping track if we have the console semaphore held. It's
|
||||
@@ -2407,7 +2380,7 @@ asmlinkage int vprintk_emit(int facility, int level,
|
||||
* non-panic CPUs are generating any messages, they will be
|
||||
* silently dropped.
|
||||
*/
|
||||
if (other_cpu_in_panic() &&
|
||||
if (panic_on_other_cpu() &&
|
||||
!debug_non_panic_cpus &&
|
||||
!panic_triggering_all_cpu_backtrace)
|
||||
return 0;
|
||||
@@ -2843,7 +2816,7 @@ void console_lock(void)
|
||||
might_sleep();
|
||||
|
||||
/* On panic, the console_lock must be left to the panic cpu. */
|
||||
while (other_cpu_in_panic())
|
||||
while (panic_on_other_cpu())
|
||||
msleep(1000);
|
||||
|
||||
down_console_sem();
|
||||
@@ -2863,7 +2836,7 @@ EXPORT_SYMBOL(console_lock);
|
||||
int console_trylock(void)
|
||||
{
|
||||
/* On panic, the console_lock must be left to the panic cpu. */
|
||||
if (other_cpu_in_panic())
|
||||
if (panic_on_other_cpu())
|
||||
return 0;
|
||||
if (down_trylock_console_sem())
|
||||
return 0;
|
||||
@@ -3243,7 +3216,7 @@ static bool console_flush_all(bool do_cond_resched, u64 *next_seq, bool *handove
|
||||
any_progress = true;
|
||||
|
||||
/* Allow panic_cpu to take over the consoles safely. */
|
||||
if (other_cpu_in_panic())
|
||||
if (panic_on_other_cpu())
|
||||
goto abandon;
|
||||
|
||||
if (do_cond_resched)
|
||||
|
||||
@@ -2143,7 +2143,7 @@ static bool _prb_read_valid(struct printk_ringbuffer *rb, u64 *seq,
|
||||
* But it would have the sequence number returned
|
||||
* by "prb_next_reserve_seq() - 1".
|
||||
*/
|
||||
if (this_cpu_in_panic() &&
|
||||
if (panic_on_this_cpu() &&
|
||||
(!debug_non_panic_cpus || legacy_allow_panic_sync) &&
|
||||
((*seq + 1) < prb_next_reserve_seq(rb))) {
|
||||
(*seq)++;
|
||||
|
||||
+30
-2
@@ -1734,6 +1734,7 @@ SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,
|
||||
struct rlimit old, new;
|
||||
struct task_struct *tsk;
|
||||
unsigned int checkflags = 0;
|
||||
bool need_tasklist;
|
||||
int ret;
|
||||
|
||||
if (old_rlim)
|
||||
@@ -1760,8 +1761,25 @@ SYSCALL_DEFINE4(prlimit64, pid_t, pid, unsigned int, resource,
|
||||
get_task_struct(tsk);
|
||||
rcu_read_unlock();
|
||||
|
||||
ret = do_prlimit(tsk, resource, new_rlim ? &new : NULL,
|
||||
old_rlim ? &old : NULL);
|
||||
need_tasklist = !same_thread_group(tsk, current);
|
||||
if (need_tasklist) {
|
||||
/*
|
||||
* Ensure we can't race with group exit or de_thread(),
|
||||
* so tsk->group_leader can't be freed or changed until
|
||||
* read_unlock(tasklist_lock) below.
|
||||
*/
|
||||
read_lock(&tasklist_lock);
|
||||
if (!pid_alive(tsk))
|
||||
ret = -ESRCH;
|
||||
}
|
||||
|
||||
if (!ret) {
|
||||
ret = do_prlimit(tsk, resource, new_rlim ? &new : NULL,
|
||||
old_rlim ? &old : NULL);
|
||||
}
|
||||
|
||||
if (need_tasklist)
|
||||
read_unlock(&tasklist_lock);
|
||||
|
||||
if (!ret && old_rlim) {
|
||||
rlim_to_rlim64(&old, &old64);
|
||||
@@ -2515,7 +2533,17 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3,
|
||||
error = -EINVAL;
|
||||
break;
|
||||
}
|
||||
/*
|
||||
* Ensure that either:
|
||||
*
|
||||
* 1. Subsequent getppid() calls reflect the parent process having died.
|
||||
* 2. forget_original_parent() will send the new me->pdeath_signal.
|
||||
*
|
||||
* Also prevent the read of me->pdeath_signal from being a data race.
|
||||
*/
|
||||
read_lock(&tasklist_lock);
|
||||
me->pdeath_signal = arg2;
|
||||
read_unlock(&tasklist_lock);
|
||||
break;
|
||||
case PR_GET_PDEATHSIG:
|
||||
error = put_user(me->pdeath_signal, (int __user *)arg2);
|
||||
|
||||
+23
-5
@@ -425,7 +425,11 @@ static DEFINE_PER_CPU(u8, cpustat_tail);
|
||||
*/
|
||||
static u16 get_16bit_precision(u64 data_ns)
|
||||
{
|
||||
return data_ns >> 24LL; /* 2^24ns ~= 16.8ms */
|
||||
/*
|
||||
* 2^24ns ~= 16.8ms
|
||||
* Round to the nearest multiple of 16.8 milliseconds.
|
||||
*/
|
||||
return (data_ns + (1 << 23)) >> 24LL;
|
||||
}
|
||||
|
||||
static void update_cpustat(void)
|
||||
@@ -444,6 +448,14 @@ static void update_cpustat(void)
|
||||
old_stat = __this_cpu_read(cpustat_old[i]);
|
||||
new_stat = get_16bit_precision(cpustat[tracked_stats[i]]);
|
||||
util = DIV_ROUND_UP(100 * (new_stat - old_stat), sample_period_16);
|
||||
/*
|
||||
* Since we use 16-bit precision, the raw data will undergo
|
||||
* integer division, which may sometimes result in data loss,
|
||||
* and then result might exceed 100%. To avoid confusion,
|
||||
* we enforce a 100% display cap when calculations exceed this threshold.
|
||||
*/
|
||||
if (util > 100)
|
||||
util = 100;
|
||||
__this_cpu_write(cpustat_util[tail][i], util);
|
||||
__this_cpu_write(cpustat_old[i], new_stat);
|
||||
}
|
||||
@@ -455,17 +467,17 @@ static void print_cpustat(void)
|
||||
{
|
||||
int i, group;
|
||||
u8 tail = __this_cpu_read(cpustat_tail);
|
||||
u64 sample_period_second = sample_period;
|
||||
u64 sample_period_msecond = sample_period;
|
||||
|
||||
do_div(sample_period_second, NSEC_PER_SEC);
|
||||
do_div(sample_period_msecond, NSEC_PER_MSEC);
|
||||
|
||||
/*
|
||||
* Outputting the "watchdog" prefix on every line is redundant and not
|
||||
* concise, and the original alarm information is sufficient for
|
||||
* positioning in logs, hence here printk() is used instead of pr_crit().
|
||||
*/
|
||||
printk(KERN_CRIT "CPU#%d Utilization every %llus during lockup:\n",
|
||||
smp_processor_id(), sample_period_second);
|
||||
printk(KERN_CRIT "CPU#%d Utilization every %llums during lockup:\n",
|
||||
smp_processor_id(), sample_period_msecond);
|
||||
|
||||
for (i = 0; i < NUM_SAMPLE_PERIODS; i++) {
|
||||
group = (tail + i) % NUM_SAMPLE_PERIODS;
|
||||
@@ -740,6 +752,12 @@ static enum hrtimer_restart watchdog_timer_fn(struct hrtimer *hrtimer)
|
||||
if (!watchdog_enabled)
|
||||
return HRTIMER_NORESTART;
|
||||
|
||||
/*
|
||||
* pass the buddy check if a panic is in process
|
||||
*/
|
||||
if (panic_in_progress())
|
||||
return HRTIMER_NORESTART;
|
||||
|
||||
watchdog_hardlockup_kick();
|
||||
|
||||
/* kick the softlockup detector */
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
#define pr_fmt(fmt) "NMI watchdog: " fmt
|
||||
|
||||
#include <linux/panic.h>
|
||||
#include <linux/nmi.h>
|
||||
#include <linux/atomic.h>
|
||||
#include <linux/module.h>
|
||||
@@ -108,6 +109,9 @@ static void watchdog_overflow_callback(struct perf_event *event,
|
||||
/* Ensure the watchdog never gets throttled */
|
||||
event->hw.interrupts = 0;
|
||||
|
||||
if (panic_in_progress())
|
||||
return;
|
||||
|
||||
if (!watchdog_check_timestamp())
|
||||
return;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user