mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-02-13 09:34:23 -05:00
If the kernel is compiled with LTO, hwerr_data symbol might be lost, and
vmcoreinfo doesn't have it dumped. This is currently seen in some
production kernels with LTO enabled.
Remove the static qualifier from hwerr_data so that the information is
still preserved when the kernel is built with LTO. Making hwerr_data a
global symbol ensures its debug info survives the LTO link process and
appears in kallsyms. Also document it, so it doesn't get removed in
the future as suggested by akpm.
Link: https://lkml.kernel.org/r/20260122-fix_vmcoreinfo-v2-1-2d6311f9e36c@debian.org
Fixes: 3fa805c37d ("vmcoreinfo: track and log recoverable hardware errors")
Signed-off-by: Breno Leitao <leitao@debian.org>
Acked-by: Baoquan He <bhe@redhat.com>
Cc: Dave Young <dyoung@redhat.com>
Cc: "Luck, Tony" <tony.luck@intel.com>
Cc: Omar Sandoval <osandov@osandov.com>
Cc: Shuai Xue <xueshuai@linux.alibaba.com>
Cc: Vivek Goyal <vgoyal@redhat.com>
Cc: Zhiquan Li <zhiquan1.li@intel.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
255 lines
6.8 KiB
C
255 lines
6.8 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* crash.c - kernel crash support code.
|
|
* Copyright (C) 2002-2004 Eric Biederman <ebiederm@xmission.com>
|
|
*/
|
|
|
|
#include <linux/buildid.h>
|
|
#include <linux/init.h>
|
|
#include <linux/utsname.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/sizes.h>
|
|
#include <linux/kexec.h>
|
|
#include <linux/memory.h>
|
|
#include <linux/cpuhotplug.h>
|
|
#include <linux/memblock.h>
|
|
#include <linux/kmemleak.h>
|
|
|
|
#include <asm/page.h>
|
|
#include <asm/sections.h>
|
|
|
|
#include <crypto/sha1.h>
|
|
|
|
#include "kallsyms_internal.h"
|
|
#include "kexec_internal.h"
|
|
|
|
/* vmcoreinfo stuff */
|
|
unsigned char *vmcoreinfo_data;
|
|
size_t vmcoreinfo_size;
|
|
u32 *vmcoreinfo_note;
|
|
|
|
/* trusted vmcoreinfo, e.g. we can make a copy in the crash memory */
|
|
static unsigned char *vmcoreinfo_data_safecopy;
|
|
|
|
struct hwerr_info {
|
|
atomic_t count;
|
|
time64_t timestamp;
|
|
};
|
|
|
|
/*
|
|
* The hwerr_data[] array is declared with global scope so that it remains
|
|
* accessible to vmcoreinfo even when Link Time Optimization (LTO) is enabled.
|
|
*/
|
|
struct hwerr_info hwerr_data[HWERR_RECOV_MAX];
|
|
|
|
Elf_Word *append_elf_note(Elf_Word *buf, char *name, unsigned int type,
|
|
void *data, size_t data_len)
|
|
{
|
|
struct elf_note *note = (struct elf_note *)buf;
|
|
|
|
note->n_namesz = strlen(name) + 1;
|
|
note->n_descsz = data_len;
|
|
note->n_type = type;
|
|
buf += DIV_ROUND_UP(sizeof(*note), sizeof(Elf_Word));
|
|
memcpy(buf, name, note->n_namesz);
|
|
buf += DIV_ROUND_UP(note->n_namesz, sizeof(Elf_Word));
|
|
memcpy(buf, data, data_len);
|
|
buf += DIV_ROUND_UP(data_len, sizeof(Elf_Word));
|
|
|
|
return buf;
|
|
}
|
|
|
|
void final_note(Elf_Word *buf)
|
|
{
|
|
memset(buf, 0, sizeof(struct elf_note));
|
|
}
|
|
|
|
static void update_vmcoreinfo_note(void)
|
|
{
|
|
u32 *buf = vmcoreinfo_note;
|
|
|
|
if (!vmcoreinfo_size)
|
|
return;
|
|
buf = append_elf_note(buf, VMCOREINFO_NOTE_NAME, 0, vmcoreinfo_data,
|
|
vmcoreinfo_size);
|
|
final_note(buf);
|
|
}
|
|
|
|
void crash_update_vmcoreinfo_safecopy(void *ptr)
|
|
{
|
|
if (ptr)
|
|
memcpy(ptr, vmcoreinfo_data, vmcoreinfo_size);
|
|
|
|
vmcoreinfo_data_safecopy = ptr;
|
|
}
|
|
|
|
void crash_save_vmcoreinfo(void)
|
|
{
|
|
if (!vmcoreinfo_note)
|
|
return;
|
|
|
|
/* Use the safe copy to generate vmcoreinfo note if have */
|
|
if (vmcoreinfo_data_safecopy)
|
|
vmcoreinfo_data = vmcoreinfo_data_safecopy;
|
|
|
|
vmcoreinfo_append_str("CRASHTIME=%lld\n", ktime_get_real_seconds());
|
|
update_vmcoreinfo_note();
|
|
}
|
|
|
|
void vmcoreinfo_append_str(const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
char buf[0x50];
|
|
size_t r;
|
|
|
|
va_start(args, fmt);
|
|
r = vscnprintf(buf, sizeof(buf), fmt, args);
|
|
va_end(args);
|
|
|
|
r = min(r, (size_t)VMCOREINFO_BYTES - vmcoreinfo_size);
|
|
|
|
memcpy(&vmcoreinfo_data[vmcoreinfo_size], buf, r);
|
|
|
|
vmcoreinfo_size += r;
|
|
|
|
WARN_ONCE(vmcoreinfo_size == VMCOREINFO_BYTES,
|
|
"vmcoreinfo data exceeds allocated size, truncating");
|
|
}
|
|
|
|
/*
|
|
* provide an empty default implementation here -- architecture
|
|
* code may override this
|
|
*/
|
|
void __weak arch_crash_save_vmcoreinfo(void)
|
|
{}
|
|
|
|
phys_addr_t __weak paddr_vmcoreinfo_note(void)
|
|
{
|
|
return __pa(vmcoreinfo_note);
|
|
}
|
|
EXPORT_SYMBOL(paddr_vmcoreinfo_note);
|
|
|
|
void hwerr_log_error_type(enum hwerr_error_type src)
|
|
{
|
|
if (src < 0 || src >= HWERR_RECOV_MAX)
|
|
return;
|
|
|
|
atomic_inc(&hwerr_data[src].count);
|
|
WRITE_ONCE(hwerr_data[src].timestamp, ktime_get_real_seconds());
|
|
}
|
|
EXPORT_SYMBOL_GPL(hwerr_log_error_type);
|
|
|
|
static int __init crash_save_vmcoreinfo_init(void)
|
|
{
|
|
vmcoreinfo_data = (unsigned char *)get_zeroed_page(GFP_KERNEL);
|
|
if (!vmcoreinfo_data) {
|
|
pr_warn("Memory allocation for vmcoreinfo_data failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
vmcoreinfo_note = alloc_pages_exact(VMCOREINFO_NOTE_SIZE,
|
|
GFP_KERNEL | __GFP_ZERO);
|
|
if (!vmcoreinfo_note) {
|
|
free_page((unsigned long)vmcoreinfo_data);
|
|
vmcoreinfo_data = NULL;
|
|
pr_warn("Memory allocation for vmcoreinfo_note failed\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
VMCOREINFO_OSRELEASE(init_uts_ns.name.release);
|
|
VMCOREINFO_BUILD_ID();
|
|
VMCOREINFO_PAGESIZE(PAGE_SIZE);
|
|
|
|
VMCOREINFO_SYMBOL(init_uts_ns);
|
|
VMCOREINFO_OFFSET(uts_namespace, name);
|
|
VMCOREINFO_SYMBOL(node_online_map);
|
|
#ifdef CONFIG_MMU
|
|
VMCOREINFO_SYMBOL_ARRAY(swapper_pg_dir);
|
|
#endif
|
|
VMCOREINFO_SYMBOL(_stext);
|
|
vmcoreinfo_append_str("NUMBER(VMALLOC_START)=0x%lx\n", (unsigned long) VMALLOC_START);
|
|
|
|
#ifndef CONFIG_NUMA
|
|
VMCOREINFO_SYMBOL(mem_map);
|
|
VMCOREINFO_SYMBOL(contig_page_data);
|
|
#endif
|
|
#ifdef CONFIG_SPARSEMEM_VMEMMAP
|
|
VMCOREINFO_SYMBOL_ARRAY(vmemmap);
|
|
#endif
|
|
#ifdef CONFIG_SPARSEMEM
|
|
VMCOREINFO_SYMBOL_ARRAY(mem_section);
|
|
VMCOREINFO_LENGTH(mem_section, NR_SECTION_ROOTS);
|
|
VMCOREINFO_STRUCT_SIZE(mem_section);
|
|
VMCOREINFO_OFFSET(mem_section, section_mem_map);
|
|
VMCOREINFO_NUMBER(SECTION_SIZE_BITS);
|
|
VMCOREINFO_NUMBER(MAX_PHYSMEM_BITS);
|
|
#endif
|
|
VMCOREINFO_STRUCT_SIZE(page);
|
|
VMCOREINFO_STRUCT_SIZE(pglist_data);
|
|
VMCOREINFO_STRUCT_SIZE(zone);
|
|
VMCOREINFO_STRUCT_SIZE(free_area);
|
|
VMCOREINFO_STRUCT_SIZE(list_head);
|
|
VMCOREINFO_SIZE(nodemask_t);
|
|
VMCOREINFO_OFFSET(page, flags);
|
|
VMCOREINFO_OFFSET(page, _refcount);
|
|
VMCOREINFO_OFFSET(page, mapping);
|
|
VMCOREINFO_OFFSET(page, lru);
|
|
VMCOREINFO_OFFSET(page, _mapcount);
|
|
VMCOREINFO_OFFSET(page, private);
|
|
VMCOREINFO_OFFSET(page, compound_head);
|
|
VMCOREINFO_OFFSET(pglist_data, node_zones);
|
|
VMCOREINFO_OFFSET(pglist_data, nr_zones);
|
|
#ifdef CONFIG_FLATMEM
|
|
VMCOREINFO_OFFSET(pglist_data, node_mem_map);
|
|
#endif
|
|
VMCOREINFO_OFFSET(pglist_data, node_start_pfn);
|
|
VMCOREINFO_OFFSET(pglist_data, node_spanned_pages);
|
|
VMCOREINFO_OFFSET(pglist_data, node_id);
|
|
VMCOREINFO_OFFSET(zone, free_area);
|
|
VMCOREINFO_OFFSET(zone, vm_stat);
|
|
VMCOREINFO_OFFSET(zone, spanned_pages);
|
|
VMCOREINFO_OFFSET(free_area, free_list);
|
|
VMCOREINFO_OFFSET(list_head, next);
|
|
VMCOREINFO_OFFSET(list_head, prev);
|
|
VMCOREINFO_LENGTH(zone.free_area, NR_PAGE_ORDERS);
|
|
log_buf_vmcoreinfo_setup();
|
|
VMCOREINFO_LENGTH(free_area.free_list, MIGRATE_TYPES);
|
|
VMCOREINFO_NUMBER(NR_FREE_PAGES);
|
|
VMCOREINFO_NUMBER(PG_lru);
|
|
VMCOREINFO_NUMBER(PG_private);
|
|
VMCOREINFO_NUMBER(PG_swapcache);
|
|
VMCOREINFO_NUMBER(PG_swapbacked);
|
|
#define PAGE_SLAB_MAPCOUNT_VALUE (PGTY_slab << 24)
|
|
VMCOREINFO_NUMBER(PAGE_SLAB_MAPCOUNT_VALUE);
|
|
#ifdef CONFIG_MEMORY_FAILURE
|
|
VMCOREINFO_NUMBER(PG_hwpoison);
|
|
#endif
|
|
VMCOREINFO_NUMBER(PG_head_mask);
|
|
#define PAGE_BUDDY_MAPCOUNT_VALUE (PGTY_buddy << 24)
|
|
VMCOREINFO_NUMBER(PAGE_BUDDY_MAPCOUNT_VALUE);
|
|
#define PAGE_HUGETLB_MAPCOUNT_VALUE (PGTY_hugetlb << 24)
|
|
VMCOREINFO_NUMBER(PAGE_HUGETLB_MAPCOUNT_VALUE);
|
|
#define PAGE_OFFLINE_MAPCOUNT_VALUE (PGTY_offline << 24)
|
|
VMCOREINFO_NUMBER(PAGE_OFFLINE_MAPCOUNT_VALUE);
|
|
#ifdef CONFIG_UNACCEPTED_MEMORY
|
|
#define PAGE_UNACCEPTED_MAPCOUNT_VALUE (PGTY_unaccepted << 24)
|
|
VMCOREINFO_NUMBER(PAGE_UNACCEPTED_MAPCOUNT_VALUE);
|
|
#endif
|
|
|
|
#ifdef CONFIG_KALLSYMS
|
|
VMCOREINFO_SYMBOL(kallsyms_names);
|
|
VMCOREINFO_SYMBOL(kallsyms_num_syms);
|
|
VMCOREINFO_SYMBOL(kallsyms_token_table);
|
|
VMCOREINFO_SYMBOL(kallsyms_token_index);
|
|
VMCOREINFO_SYMBOL(kallsyms_offsets);
|
|
VMCOREINFO_SYMBOL(kallsyms_relative_base);
|
|
#endif /* CONFIG_KALLSYMS */
|
|
|
|
arch_crash_save_vmcoreinfo();
|
|
update_vmcoreinfo_note();
|
|
|
|
return 0;
|
|
}
|
|
|
|
subsys_initcall(crash_save_vmcoreinfo_init);
|