Skip to content

Commit 76d3453

Browse files
author
AKASHI Takahiro
committed
arm64: hibernate: preserve kdump image around hibernation
Since arch_kexec_protect_crashkres() removes a mapping for crash dump kernel image, the loaded data won't be preserved around hibernation. In this patch, helper functions, crash_prepare_suspend()/ crash_post_resume(), are additionally called before/after hibernation so that the relevant memory segments will be mapped again and preserved just as the others are. In addition, to minimize the size of hibernation image, crash_is_nosave() is added to pfn_is_nosave() in order to recognize only the pages that hold loaded crash dump kernel image as saveable. Hibernation excludes any pages that are marked as Reserved and yet "nosave." Signed-off-by: AKASHI Takahiro <takahiro.akashi@linaro.org> Reviewed-by: James Morse <james.morse@arm.com> Signed-off-by: Catalin Marinas <catalin.marinas@arm.com> Conflicts: arch/arm64/kernel/hibernate.c due to missing commit 8ec058fd2710 ("arm64: hibernate: Resume when hibernate image created on non-boot CPU")
1 parent 32dcc8e commit 76d3453

4 files changed

Lines changed: 117 additions & 1 deletion

File tree

arch/arm64/include/asm/kexec.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,16 @@ static inline void crash_setup_regs(struct pt_regs *newregs,
4343
/* Empty routine needed to avoid build errors. */
4444
}
4545

46+
#if defined(CONFIG_KEXEC_CORE) && defined(CONFIG_HIBERNATION)
47+
extern bool crash_is_nosave(unsigned long pfn);
48+
extern void crash_prepare_suspend(void);
49+
extern void crash_post_resume(void);
50+
#else
51+
static inline bool crash_is_nosave(unsigned long pfn) {return false; }
52+
static inline void crash_prepare_suspend(void) {}
53+
static inline void crash_post_resume(void) {}
54+
#endif
55+
4656
#endif /* __ASSEMBLY__ */
4757

4858
#endif

arch/arm64/kernel/hibernate.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <asm/barrier.h>
2828
#include <asm/cacheflush.h>
2929
#include <asm/irqflags.h>
30+
#include <asm/kexec.h>
3031
#include <asm/memory.h>
3132
#include <asm/mmu_context.h>
3233
#include <asm/pgalloc.h>
@@ -100,7 +101,8 @@ int pfn_is_nosave(unsigned long pfn)
100101
unsigned long nosave_begin_pfn = virt_to_pfn(&__nosave_begin);
101102
unsigned long nosave_end_pfn = virt_to_pfn(&__nosave_end - 1);
102103

103-
return (pfn >= nosave_begin_pfn) && (pfn <= nosave_end_pfn);
104+
return ((pfn >= nosave_begin_pfn) && (pfn <= nosave_end_pfn)) ||
105+
crash_is_nosave(pfn);
104106
}
105107

106108
void notrace save_processor_state(void)
@@ -250,11 +252,17 @@ int swsusp_arch_suspend(void)
250252
local_dbg_save(flags);
251253

252254
if (__cpu_suspend_enter(&state)) {
255+
/* make the crash dump kernel image visible/saveable */
256+
crash_prepare_suspend();
257+
253258
ret = swsusp_save();
254259
} else {
255260
/* Clean kernel to PoC for secondary core startup */
256261
__flush_dcache_area(LMADDR(KERNEL_START), KERNEL_END - KERNEL_START);
257262

263+
/* make the crash dump kernel image protected again */
264+
crash_post_resume();
265+
258266
/*
259267
* Tell the hibernation core that we've just restored
260268
* the memory

arch/arm64/kernel/machine_kexec.c

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
*/
1111

1212
#include <linux/kexec.h>
13+
#include <linux/page-flags.h>
1314
#include <linux/smp.h>
1415

1516
#include <asm/cacheflush.h>
@@ -230,3 +231,73 @@ void arch_kexec_unprotect_crashkres(void)
230231
__phys_to_virt(kexec_crash_image->segment[i].mem),
231232
kexec_crash_image->segment[i].memsz >> PAGE_SHIFT, 1);
232233
}
234+
235+
#ifdef CONFIG_HIBERNATION
236+
/*
237+
* To preserve the crash dump kernel image, the relevant memory segments
238+
* should be mapped again around the hibernation.
239+
*/
240+
void crash_prepare_suspend(void)
241+
{
242+
if (kexec_crash_image)
243+
arch_kexec_unprotect_crashkres();
244+
}
245+
246+
void crash_post_resume(void)
247+
{
248+
if (kexec_crash_image)
249+
arch_kexec_protect_crashkres();
250+
}
251+
252+
/*
253+
* crash_is_nosave
254+
*
255+
* Return true only if a page is part of reserved memory for crash dump kernel,
256+
* but does not hold any data of loaded kernel image.
257+
*
258+
* Note that all the pages in crash dump kernel memory have been initially
259+
* marked as Reserved in kexec_reserve_crashkres_pages().
260+
*
261+
* In hibernation, the pages which are Reserved and yet "nosave" are excluded
262+
* from the hibernation iamge. crash_is_nosave() does thich check for crash
263+
* dump kernel and will reduce the total size of hibernation image.
264+
*/
265+
266+
bool crash_is_nosave(unsigned long pfn)
267+
{
268+
int i;
269+
phys_addr_t addr;
270+
271+
if (!crashk_res.end)
272+
return false;
273+
274+
/* in reserved memory? */
275+
addr = __pfn_to_phys(pfn);
276+
if ((addr < crashk_res.start) || (crashk_res.end < addr))
277+
return false;
278+
279+
if (!kexec_crash_image)
280+
return true;
281+
282+
/* not part of loaded kernel image? */
283+
for (i = 0; i < kexec_crash_image->nr_segments; i++)
284+
if (addr >= kexec_crash_image->segment[i].mem &&
285+
addr < (kexec_crash_image->segment[i].mem +
286+
kexec_crash_image->segment[i].memsz))
287+
return false;
288+
289+
return true;
290+
}
291+
292+
void crash_free_reserved_phys_range(unsigned long begin, unsigned long end)
293+
{
294+
unsigned long addr;
295+
struct page *page;
296+
297+
for (addr = begin; addr < end; addr += PAGE_SIZE) {
298+
page = phys_to_page(addr);
299+
ClearPageReserved(page);
300+
free_reserved_page(page);
301+
}
302+
}
303+
#endif /* CONFIG_HIBERNATION */

arch/arm64/mm/init.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,10 +132,35 @@ static void __init reserve_crashkernel(void)
132132
crashk_res.start = crash_base;
133133
crashk_res.end = crash_base + crash_size - 1;
134134
}
135+
136+
static void __init kexec_reserve_crashkres_pages(void)
137+
{
138+
#ifdef CONFIG_HIBERNATION
139+
phys_addr_t addr;
140+
struct page *page;
141+
142+
if (!crashk_res.end)
143+
return;
144+
145+
/*
146+
* To reduce the size of hibernation image, all the pages are
147+
* marked as Reserved initially.
148+
*/
149+
for (addr = crashk_res.start; addr < (crashk_res.end + 1);
150+
addr += PAGE_SIZE) {
151+
page = phys_to_page(addr);
152+
SetPageReserved(page);
153+
}
154+
#endif
155+
}
135156
#else
136157
static void __init reserve_crashkernel(void)
137158
{
138159
}
160+
161+
static void __init kexec_reserve_crashkres_pages(void)
162+
{
163+
}
139164
#endif /* CONFIG_KEXEC_CORE */
140165

141166
/*
@@ -456,6 +481,8 @@ void __init mem_init(void)
456481
/* this will put all unused low memory onto the freelists */
457482
free_all_bootmem();
458483

484+
kexec_reserve_crashkres_pages();
485+
459486
mem_init_print_info(NULL);
460487

461488
#define MLK(b, t) b, t, ((t) - (b)) >> 10

0 commit comments

Comments
 (0)