mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-08-05 16:54:27 +00:00

To support memory devices under QEMU/KVM, such as virtio-mem, we have to prepare our kernel virtual address space accordingly and have to know the highest possible physical memory address we might see later: the storage limit. The good old SCLP interface is not suitable for this use case. In particular, memory owned by memory devices has no relationship to storage increments, it is always detected using the device driver, and unaware OSes (no driver) must never try making use of that memory. Consequently this memory is located outside of the "maximum storage increment"-indicated memory range. Let's use our new diag500 STORAGE_LIMIT subcode to query this storage limit that can exceed the "maximum storage increment", and use the existing interfaces (i.e., SCLP) to obtain information about the initial memory that is not owned+managed by memory devices. If a hypervisor does not support such memory devices, the address exposed through diag500 STORAGE_LIMIT will correspond to the maximum storage increment exposed through SCLP. To teach kdump on s390 to include memory owned by memory devices, there will be ways to query the relevant memory ranges from the device via a driver running in special kdump mode (like virtio-mem already implements to filter /proc/vmcore access so we don't end up reading from unplugged device blocks). Update setup_ident_map_size(), to clarify that there can be more than just online and standby memory. Tested-by: Mario Casquero <mcasquer@redhat.com> Signed-off-by: David Hildenbrand <david@redhat.com> Acked-by: Heiko Carstens <hca@linux.ibm.com> Reviewed-by: Alexander Gordeev <agordeev@linux.ibm.com> Tested-by: Sumanth Korikkar <sumanthk@linux.ibm.com> Acked-by: Christian Borntraeger <borntraeger@linux.ibm.com> Link: https://lore.kernel.org/r/20241025141453.1210600-4-david@redhat.com Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
178 lines
4.9 KiB
C
178 lines
4.9 KiB
C
/* SPDX-License-Identifier: GPL-2.0 */
|
|
#ifndef _ASM_S390_MEM_DETECT_H
|
|
#define _ASM_S390_MEM_DETECT_H
|
|
|
|
#include <linux/types.h>
|
|
#include <asm/page.h>
|
|
|
|
enum physmem_info_source {
|
|
MEM_DETECT_NONE = 0,
|
|
MEM_DETECT_SCLP_STOR_INFO,
|
|
MEM_DETECT_DIAG260,
|
|
MEM_DETECT_DIAG500_STOR_LIMIT,
|
|
MEM_DETECT_SCLP_READ_INFO,
|
|
MEM_DETECT_BIN_SEARCH
|
|
};
|
|
|
|
struct physmem_range {
|
|
u64 start;
|
|
u64 end;
|
|
};
|
|
|
|
enum reserved_range_type {
|
|
RR_DECOMPRESSOR,
|
|
RR_INITRD,
|
|
RR_VMLINUX,
|
|
RR_AMODE31,
|
|
RR_IPLREPORT,
|
|
RR_CERT_COMP_LIST,
|
|
RR_MEM_DETECT_EXTENDED,
|
|
RR_VMEM,
|
|
RR_MAX
|
|
};
|
|
|
|
struct reserved_range {
|
|
unsigned long start;
|
|
unsigned long end;
|
|
struct reserved_range *chain;
|
|
};
|
|
|
|
/*
|
|
* Storage element id is defined as 1 byte (up to 256 storage elements).
|
|
* In practise only storage element id 0 and 1 are used).
|
|
* According to architecture one storage element could have as much as
|
|
* 1020 subincrements. 255 physmem_ranges are embedded in physmem_info.
|
|
* If more physmem_ranges are required, a block of memory from already
|
|
* known physmem_range is taken (online_extended points to it).
|
|
*/
|
|
#define MEM_INLINED_ENTRIES 255 /* (PAGE_SIZE - 16) / 16 */
|
|
|
|
struct physmem_info {
|
|
u32 range_count;
|
|
u8 info_source;
|
|
unsigned long usable;
|
|
struct reserved_range reserved[RR_MAX];
|
|
struct physmem_range online[MEM_INLINED_ENTRIES];
|
|
struct physmem_range *online_extended;
|
|
};
|
|
|
|
extern struct physmem_info physmem_info;
|
|
|
|
void add_physmem_online_range(u64 start, u64 end);
|
|
|
|
static inline int __get_physmem_range(u32 n, unsigned long *start,
|
|
unsigned long *end, bool respect_usable_limit)
|
|
{
|
|
if (n >= physmem_info.range_count) {
|
|
*start = 0;
|
|
*end = 0;
|
|
return -1;
|
|
}
|
|
|
|
if (n < MEM_INLINED_ENTRIES) {
|
|
*start = (unsigned long)physmem_info.online[n].start;
|
|
*end = (unsigned long)physmem_info.online[n].end;
|
|
} else {
|
|
*start = (unsigned long)physmem_info.online_extended[n - MEM_INLINED_ENTRIES].start;
|
|
*end = (unsigned long)physmem_info.online_extended[n - MEM_INLINED_ENTRIES].end;
|
|
}
|
|
|
|
if (respect_usable_limit && physmem_info.usable) {
|
|
if (*start >= physmem_info.usable)
|
|
return -1;
|
|
if (*end > physmem_info.usable)
|
|
*end = physmem_info.usable;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* for_each_physmem_usable_range - early online memory range iterator
|
|
* @i: an integer used as loop variable
|
|
* @p_start: ptr to unsigned long for start address of the range
|
|
* @p_end: ptr to unsigned long for end address of the range
|
|
*
|
|
* Walks over detected online memory ranges below usable limit.
|
|
*/
|
|
#define for_each_physmem_usable_range(i, p_start, p_end) \
|
|
for (i = 0; !__get_physmem_range(i, p_start, p_end, true); i++)
|
|
|
|
/* Walks over all detected online memory ranges disregarding usable limit. */
|
|
#define for_each_physmem_online_range(i, p_start, p_end) \
|
|
for (i = 0; !__get_physmem_range(i, p_start, p_end, false); i++)
|
|
|
|
static inline const char *get_physmem_info_source(void)
|
|
{
|
|
switch (physmem_info.info_source) {
|
|
case MEM_DETECT_SCLP_STOR_INFO:
|
|
return "sclp storage info";
|
|
case MEM_DETECT_DIAG260:
|
|
return "diag260";
|
|
case MEM_DETECT_DIAG500_STOR_LIMIT:
|
|
return "diag500 storage limit";
|
|
case MEM_DETECT_SCLP_READ_INFO:
|
|
return "sclp read info";
|
|
case MEM_DETECT_BIN_SEARCH:
|
|
return "binary search";
|
|
}
|
|
return "none";
|
|
}
|
|
|
|
#define RR_TYPE_NAME(t) case RR_ ## t: return #t
|
|
static inline const char *get_rr_type_name(enum reserved_range_type t)
|
|
{
|
|
switch (t) {
|
|
RR_TYPE_NAME(DECOMPRESSOR);
|
|
RR_TYPE_NAME(INITRD);
|
|
RR_TYPE_NAME(VMLINUX);
|
|
RR_TYPE_NAME(AMODE31);
|
|
RR_TYPE_NAME(IPLREPORT);
|
|
RR_TYPE_NAME(CERT_COMP_LIST);
|
|
RR_TYPE_NAME(MEM_DETECT_EXTENDED);
|
|
RR_TYPE_NAME(VMEM);
|
|
default:
|
|
return "UNKNOWN";
|
|
}
|
|
}
|
|
|
|
#define for_each_physmem_reserved_type_range(t, range, p_start, p_end) \
|
|
for (range = &physmem_info.reserved[t], *p_start = range->start, *p_end = range->end; \
|
|
range && range->end; range = range->chain ? __va(range->chain) : NULL, \
|
|
*p_start = range ? range->start : 0, *p_end = range ? range->end : 0)
|
|
|
|
static inline struct reserved_range *__physmem_reserved_next(enum reserved_range_type *t,
|
|
struct reserved_range *range)
|
|
{
|
|
if (!range) {
|
|
range = &physmem_info.reserved[*t];
|
|
if (range->end)
|
|
return range;
|
|
}
|
|
if (range->chain)
|
|
return __va(range->chain);
|
|
while (++*t < RR_MAX) {
|
|
range = &physmem_info.reserved[*t];
|
|
if (range->end)
|
|
return range;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
#define for_each_physmem_reserved_range(t, range, p_start, p_end) \
|
|
for (t = 0, range = __physmem_reserved_next(&t, NULL), \
|
|
*p_start = range ? range->start : 0, *p_end = range ? range->end : 0; \
|
|
range; range = __physmem_reserved_next(&t, range), \
|
|
*p_start = range ? range->start : 0, *p_end = range ? range->end : 0)
|
|
|
|
static inline unsigned long get_physmem_reserved(enum reserved_range_type type,
|
|
unsigned long *addr, unsigned long *size)
|
|
{
|
|
*addr = physmem_info.reserved[type].start;
|
|
*size = physmem_info.reserved[type].end - physmem_info.reserved[type].start;
|
|
return *size;
|
|
}
|
|
|
|
#define AMODE31_START (physmem_info.reserved[RR_AMODE31].start)
|
|
#define AMODE31_END (physmem_info.reserved[RR_AMODE31].end)
|
|
|
|
#endif
|