Skip to content

Commit a83967a

Browse files
author
Herton R. Krzesinski
committed
Merge: panic, kexec: make __crash_kexec() NMI safe
MR: https://gitlab.com/redhat/centos-stream/src/kernel/centos-stream-9/-/merge_requests/2007 Bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=2166717 Upstream-status: https://github.com/torvalds/linux.git Using a mutex to synchronize access to the crash kernel image is very much not NMI safe, these patches address that. Tested: Triggered NMIs via ipmitool using the following script: echo 1 > /proc/sys/kernel/panic_on_unrecovered_nmi echo 1 > /proc/sys/kernel/unknown_nmi_panic echo 1 > /proc/sys/kernel/panic ipmitool power diag Signed-off-by: Valentin Schneider <vschneid@redhat.com> Approved-by: Baoquan He <5820488-baoquan_he@users.noreply.gitlab.com> Approved-by: Luis Claudio R. Goncalves <lgoncalv@redhat.com> Approved-by: Wander Lairson Costa <wander@redhat.com> Signed-off-by: Herton R. Krzesinski <herton@redhat.com>
2 parents 0cbdc00 + 2e6264f commit a83967a

File tree

6 files changed

+43
-24
lines changed

6 files changed

+43
-24
lines changed

include/linux/kexec.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -427,7 +427,7 @@ extern int kexec_load_disabled;
427427
extern bool kexec_in_progress;
428428

429429
int crash_shrink_memory(unsigned long new_size);
430-
size_t crash_get_memory_size(void);
430+
ssize_t crash_get_memory_size(void);
431431

432432
#ifndef arch_kexec_protect_crashkres
433433
/*

kernel/kexec.c

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -93,13 +93,10 @@ static int do_kexec_load(unsigned long entry, unsigned long nr_segments,
9393

9494
/*
9595
* Because we write directly to the reserved memory region when loading
96-
* crash kernels we need a mutex here to prevent multiple crash kernels
97-
* from attempting to load simultaneously, and to prevent a crash kernel
98-
* from loading over the top of a in use crash kernel.
99-
*
100-
* KISS: always take the mutex.
96+
* crash kernels we need a serialization here to prevent multiple crash
97+
* kernels from attempting to load simultaneously.
10198
*/
102-
if (!mutex_trylock(&kexec_mutex))
99+
if (!kexec_trylock())
103100
return -EBUSY;
104101

105102
if (flags & KEXEC_ON_CRASH) {
@@ -165,7 +162,7 @@ static int do_kexec_load(unsigned long entry, unsigned long nr_segments,
165162

166163
kimage_free(image);
167164
out_unlock:
168-
mutex_unlock(&kexec_mutex);
165+
kexec_unlock();
169166
return ret;
170167
}
171168

kernel/kexec_core.c

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
#include <crypto/hash.h>
4747
#include "kexec_internal.h"
4848

49-
DEFINE_MUTEX(kexec_mutex);
49+
atomic_t __kexec_lock = ATOMIC_INIT(0);
5050

5151
/* Per cpu memory for storing cpu states in case of system crash. */
5252
note_buf_t __percpu *crash_notes;
@@ -959,15 +959,15 @@ late_initcall(kexec_core_sysctl_init);
959959
*/
960960
void __noclone __crash_kexec(struct pt_regs *regs)
961961
{
962-
/* Take the kexec_mutex here to prevent sys_kexec_load
962+
/* Take the kexec_lock here to prevent sys_kexec_load
963963
* running on one cpu from replacing the crash kernel
964964
* we are using after a panic on a different cpu.
965965
*
966966
* If the crash kernel was not located in a fixed area
967967
* of memory the xchg(&kexec_crash_image) would be
968968
* sufficient. But since I reuse the memory...
969969
*/
970-
if (mutex_trylock(&kexec_mutex)) {
970+
if (kexec_trylock()) {
971971
if (kexec_crash_image) {
972972
struct pt_regs fixed_regs;
973973

@@ -976,7 +976,7 @@ void __noclone __crash_kexec(struct pt_regs *regs)
976976
machine_crash_shutdown(&fixed_regs);
977977
machine_kexec(kexec_crash_image);
978978
}
979-
mutex_unlock(&kexec_mutex);
979+
kexec_unlock();
980980
}
981981
}
982982
STACK_FRAME_NON_STANDARD(__crash_kexec);
@@ -1004,14 +1004,17 @@ void crash_kexec(struct pt_regs *regs)
10041004
}
10051005
}
10061006

1007-
size_t crash_get_memory_size(void)
1007+
ssize_t crash_get_memory_size(void)
10081008
{
1009-
size_t size = 0;
1009+
ssize_t size = 0;
1010+
1011+
if (!kexec_trylock())
1012+
return -EBUSY;
10101013

1011-
mutex_lock(&kexec_mutex);
10121014
if (crashk_res.end != crashk_res.start)
10131015
size = resource_size(&crashk_res);
1014-
mutex_unlock(&kexec_mutex);
1016+
1017+
kexec_unlock();
10151018
return size;
10161019
}
10171020

@@ -1022,7 +1025,8 @@ int crash_shrink_memory(unsigned long new_size)
10221025
unsigned long old_size;
10231026
struct resource *ram_res;
10241027

1025-
mutex_lock(&kexec_mutex);
1028+
if (!kexec_trylock())
1029+
return -EBUSY;
10261030

10271031
if (kexec_crash_image) {
10281032
ret = -ENOENT;
@@ -1060,7 +1064,7 @@ int crash_shrink_memory(unsigned long new_size)
10601064
insert_resource(&iomem_resource, ram_res);
10611065

10621066
unlock:
1063-
mutex_unlock(&kexec_mutex);
1067+
kexec_unlock();
10641068
return ret;
10651069
}
10661070

@@ -1132,7 +1136,7 @@ int kernel_kexec(void)
11321136
{
11331137
int error = 0;
11341138

1135-
if (!mutex_trylock(&kexec_mutex))
1139+
if (!kexec_trylock())
11361140
return -EBUSY;
11371141
if (!kexec_image) {
11381142
error = -EINVAL;
@@ -1208,6 +1212,6 @@ int kernel_kexec(void)
12081212
#endif
12091213

12101214
Unlock:
1211-
mutex_unlock(&kexec_mutex);
1215+
kexec_unlock();
12121216
return error;
12131217
}

kernel/kexec_file.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd,
339339

340340
image = NULL;
341341

342-
if (!mutex_trylock(&kexec_mutex))
342+
if (!kexec_trylock())
343343
return -EBUSY;
344344

345345
dest_image = &kexec_image;
@@ -411,7 +411,7 @@ SYSCALL_DEFINE5(kexec_file_load, int, kernel_fd, int, initrd_fd,
411411
if ((flags & KEXEC_FILE_ON_CRASH) && kexec_crash_image)
412412
arch_kexec_protect_crashkres();
413413

414-
mutex_unlock(&kexec_mutex);
414+
kexec_unlock();
415415
kimage_free(image);
416416
return ret;
417417
}

kernel/kexec_internal.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,20 @@ void kimage_terminate(struct kimage *image);
1313
int kimage_is_destination_range(struct kimage *image,
1414
unsigned long start, unsigned long end);
1515

16-
extern struct mutex kexec_mutex;
16+
/*
17+
* Whatever is used to serialize accesses to the kexec_crash_image needs to be
18+
* NMI safe, as __crash_kexec() can happen during nmi_panic(), so here we use a
19+
* "simple" atomic variable that is acquired with a cmpxchg().
20+
*/
21+
extern atomic_t __kexec_lock;
22+
static inline bool kexec_trylock(void)
23+
{
24+
return atomic_cmpxchg_acquire(&__kexec_lock, 0, 1) == 0;
25+
}
26+
static inline void kexec_unlock(void)
27+
{
28+
atomic_set_release(&__kexec_lock, 0);
29+
}
1730

1831
#ifdef CONFIG_KEXEC_FILE
1932
#include <linux/purgatory.h>

kernel/ksysfs.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,12 @@ KERNEL_ATTR_RO(kexec_crash_loaded);
106106
static ssize_t kexec_crash_size_show(struct kobject *kobj,
107107
struct kobj_attribute *attr, char *buf)
108108
{
109-
return sprintf(buf, "%zu\n", crash_get_memory_size());
109+
ssize_t size = crash_get_memory_size();
110+
111+
if (size < 0)
112+
return size;
113+
114+
return sprintf(buf, "%zd\n", size);
110115
}
111116
static ssize_t kexec_crash_size_store(struct kobject *kobj,
112117
struct kobj_attribute *attr,

0 commit comments

Comments
 (0)