2023-02-08 18:11:25 +01:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0 */
|
|
|
|
#ifndef _ASM_S390_MEM_DETECT_H
|
|
|
|
#define _ASM_S390_MEM_DETECT_H
|
|
|
|
|
|
|
|
#include <linux/types.h>
|
2023-06-06 15:30:56 +02:00
|
|
|
#include <asm/page.h>
|
2023-02-08 18:11:25 +01:00
|
|
|
|
|
|
|
enum physmem_info_source {
|
|
|
|
MEM_DETECT_NONE = 0,
|
|
|
|
MEM_DETECT_SCLP_STOR_INFO,
|
|
|
|
MEM_DETECT_DIAG260,
|
s390/physmem_info: Query diag500(STORAGE LIMIT) to support QEMU/KVM memory devices
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>
2024-10-25 16:14:48 +02:00
|
|
|
MEM_DETECT_DIAG500_STOR_LIMIT,
|
2023-02-08 18:11:25 +01:00
|
|
|
MEM_DETECT_SCLP_READ_INFO,
|
|
|
|
MEM_DETECT_BIN_SEARCH
|
|
|
|
};
|
|
|
|
|
|
|
|
struct physmem_range {
|
|
|
|
u64 start;
|
|
|
|
u64 end;
|
|
|
|
};
|
|
|
|
|
2023-02-02 13:59:36 +01:00
|
|
|
enum reserved_range_type {
|
|
|
|
RR_DECOMPRESSOR,
|
|
|
|
RR_INITRD,
|
|
|
|
RR_VMLINUX,
|
|
|
|
RR_AMODE31,
|
|
|
|
RR_IPLREPORT,
|
|
|
|
RR_CERT_COMP_LIST,
|
2024-12-11 11:06:19 +01:00
|
|
|
RR_MEM_DETECT_EXT,
|
2023-02-02 13:59:36 +01:00
|
|
|
RR_VMEM,
|
|
|
|
RR_MAX
|
|
|
|
};
|
|
|
|
|
|
|
|
struct reserved_range {
|
|
|
|
unsigned long start;
|
|
|
|
unsigned long end;
|
|
|
|
struct reserved_range *chain;
|
|
|
|
};
|
|
|
|
|
2023-02-08 18:11:25 +01:00
|
|
|
/*
|
|
|
|
* 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;
|
2023-02-02 13:59:36 +01:00
|
|
|
struct reserved_range reserved[RR_MAX];
|
2023-02-08 18:11:25 +01:00
|
|
|
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++)
|
|
|
|
|
2023-02-02 13:59:36 +01:00
|
|
|
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";
|
s390/physmem_info: Query diag500(STORAGE LIMIT) to support QEMU/KVM memory devices
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>
2024-10-25 16:14:48 +02:00
|
|
|
case MEM_DETECT_DIAG500_STOR_LIMIT:
|
|
|
|
return "diag500 storage limit";
|
2023-02-02 13:59:36 +01:00
|
|
|
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);
|
2024-12-11 11:06:19 +01:00
|
|
|
RR_TYPE_NAME(MEM_DETECT_EXT);
|
2023-02-02 13:59:36 +01:00
|
|
|
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; \
|
2023-06-06 15:30:56 +02:00
|
|
|
range && range->end; range = range->chain ? __va(range->chain) : NULL, \
|
2023-02-02 13:59:36 +01:00
|
|
|
*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)
|
2023-06-06 15:30:56 +02:00
|
|
|
return __va(range->chain);
|
2023-02-02 13:59:36 +01:00
|
|
|
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)
|
2023-02-08 18:11:25 +01:00
|
|
|
{
|
2023-02-02 13:59:36 +01:00
|
|
|
*addr = physmem_info.reserved[type].start;
|
|
|
|
*size = physmem_info.reserved[type].end - physmem_info.reserved[type].start;
|
|
|
|
return *size;
|
2023-02-08 18:11:25 +01:00
|
|
|
}
|
|
|
|
|
2023-08-11 09:49:27 +02:00
|
|
|
#define AMODE31_START (physmem_info.reserved[RR_AMODE31].start)
|
|
|
|
#define AMODE31_END (physmem_info.reserved[RR_AMODE31].end)
|
|
|
|
|
2023-02-08 18:11:25 +01:00
|
|
|
#endif
|