[PATCH] kmem: Show page migration types
by Aaron Tomlin
Modify the "kmem" command to query and display the migration type of
pages and page blocks:
- Appending the migration type string (e.g., Unmovable, Movable,
Reclaimable, HighAtomic, CMA, Isolate) to the output of page flags
in kmem -p
- Showing the migration type for each free area sublist in kmem -f
and kmem -F when multiple lists are present per area
To support this, offsets and sizes for mem_section_usage,
mem_section_usage_pageblock_flags, and zone_pageblock_flags are
retrieved and cached. The pageblock flags bitmap is then parsed to
extract the migration type of each page block, matching the logic in the
Linux kernel buddy allocator.
An example excerpt of kmem -f is provided below:
ZONE NAME SIZE FREE MEM_MAP START_PADDR START_MAPNR
2 Normal 3680256 2752702 ffffedd284000000 100000000 1048575
AREA SIZE TYPE FREE_AREA_STRUCT BLOCKS PAGES
0 4k Unmovable ffff8a0b827d5f00 232 232
0 4k Movable ffff8a0b827d5f10 44 44
0 4k Reclaimable ffff8a0b827d5f20 0 0
0 4k HighAtomic ffff8a0b827d5f30 0 0
0 4k CMA ffff8a0b827d5f40 0 0
0 4k Isolate ffff8a0b827d5f50 0 0
1 8k Unmovable ffff8a0b827d5f68 382 764
1 8k Movable ffff8a0b827d5f78 104 208
1 8k Reclaimable ffff8a0b827d5f88 1 2
1 8k HighAtomic ffff8a0b827d5f98 0 0
Signed-off-by: Aaron Tomlin <atomlin(a)atomlin.com>
---
defs.h | 4 +
memory.c | 228 +++++++++++++++++++++++++++++++++++++++++++++++++++---
symbols.c | 8 ++
3 files changed, 230 insertions(+), 10 deletions(-)
diff --git a/defs.h b/defs.h
index 1ee8744..ec8cc37 100644
--- a/defs.h
+++ b/defs.h
@@ -2108,6 +2108,9 @@ struct offset_table { /* stash of commonly-used offsets */
long memory_block_state;
long memory_block_nid;
long mem_section_pageblock_flags;
+ long mem_section_usage;
+ long mem_section_usage_pageblock_flags;
+ long zone_pageblock_flags;
long bus_type_p;
long device_private_device;
long device_private_knode_bus;
@@ -2389,6 +2392,7 @@ struct size_table { /* stash of commonly-used sizes */
long task_struct_start_time;
long cputime_t;
long mem_section;
+ long mem_section_usage;
long pid_link;
long unwind_table;
long rlimit;
diff --git a/memory.c b/memory.c
index 3f3aa27..7ed59a4 100644
--- a/memory.c
+++ b/memory.c
@@ -171,6 +171,10 @@ static int get_bitfield_data(struct integer_data *);
static int show_page_member_data(char *, ulong, struct meminfo *, char *);
static void dump_mem_map(struct meminfo *);
static void dump_mem_map_SPARSEMEM(struct meminfo *);
+static ulong find_pfn_zone(ulong);
+static ulong get_pageblock_bitmap(ulong);
+static char *get_migratetype_name(int);
+static char *get_page_migration_type_str(physaddr_t);
static void fill_mem_map_cache(ulong, ulong, char *);
static void page_flags_init(void);
static int page_flags_init_from_pageflag_names(void);
@@ -1255,6 +1259,8 @@ vm_init(void)
"zone", "zone_start_pfn");
MEMBER_OFFSET_INIT(zone_spanned_pages,
"zone", "spanned_pages");
+ MEMBER_OFFSET_INIT(zone_pageblock_flags,
+ "zone", "pageblock_flags");
MEMBER_OFFSET_INIT(zone_present_pages,
"zone", "present_pages");
MEMBER_OFFSET_INIT(zone_pages_min,
@@ -6115,10 +6121,19 @@ dump_mem_map_SPARSEMEM(struct meminfo *mi)
bufferindex += sprintflag("%sreserved");
bufferindex += sprintf(outputbuffer+bufferindex, "\n");
} else if (THIS_KERNEL_VERSION > LINUX(2,4,9)) {
- if (vt->flags & PAGEFLAGS)
+ char *mig_type = get_page_migration_type_str(phys);
+ if (vt->flags & PAGEFLAGS) {
bufferindex += translate_page_flags(outputbuffer+bufferindex, flags);
- else
- bufferindex += sprintf(outputbuffer+bufferindex, "%lx\n", flags);
+ if (strlen(mig_type) > 0 && bufferindex > 0 && outputbuffer[bufferindex-1] == '\n') {
+ bufferindex--;
+ bufferindex += sprintf(outputbuffer+bufferindex, " (%s)\n", mig_type);
+ }
+ } else {
+ if (strlen(mig_type) > 0)
+ bufferindex += sprintf(outputbuffer+bufferindex, "%lx (%s)\n", flags, mig_type);
+ else
+ bufferindex += sprintf(outputbuffer+bufferindex, "%lx\n", flags);
+ }
} else {
if ((flags >> v24_PG_locked) & 1)
@@ -6215,6 +6230,170 @@ display_members:
FREEBUF(page_cache);
}
+static ulong
+find_pfn_zone(ulong pfn)
+{
+ int n, i;
+ struct node_table *nt;
+ ulong node_zones;
+ ulong zone_start_pfn = 0, spanned_pages = 0;
+
+ if (INVALID_MEMBER(pglist_data_node_zones) ||
+ INVALID_MEMBER(zone_zone_start_pfn) ||
+ INVALID_MEMBER(zone_spanned_pages))
+ return 0;
+
+ for (n = 0; n < vt->numnodes; n++) {
+ nt = &vt->node_table[n];
+ node_zones = nt->pgdat + OFFSET(pglist_data_node_zones);
+
+ for (i = 0; i < vt->nr_zones; i++) {
+ if (!readmem(node_zones + OFFSET(zone_zone_start_pfn),
+ KVADDR, &zone_start_pfn, sizeof(ulong),
+ "zone zone_start_pfn", RETURN_ON_ERROR|QUIET))
+ goto next_zone;
+ if (!readmem(node_zones + OFFSET(zone_spanned_pages),
+ KVADDR, &spanned_pages, sizeof(ulong),
+ "zone spanned_pages", RETURN_ON_ERROR|QUIET))
+ goto next_zone;
+
+ if (pfn >= zone_start_pfn && pfn < zone_start_pfn + spanned_pages)
+ return node_zones;
+
+next_zone:
+ node_zones += SIZE(zone);
+ }
+ }
+ return 0;
+}
+
+static ulong
+get_pageblock_bitmap(ulong pfn)
+{
+ if (IS_SPARSEMEM()) {
+ ulong section_nr = pfn / PAGES_PER_SECTION();
+ ulong mem_section_addr = nr_to_section(section_nr);
+ if (!mem_section_addr)
+ return 0;
+
+ char *mem_section = read_mem_section(mem_section_addr);
+ if (!mem_section)
+ return 0;
+
+ if (VALID_MEMBER(mem_section_pageblock_flags)) {
+ return ULONG(mem_section + OFFSET(mem_section_pageblock_flags));
+ } else if (VALID_MEMBER(mem_section_usage)) {
+ ulong usage_addr = ULONG(mem_section + OFFSET(mem_section_usage));
+ if (!usage_addr)
+ return 0;
+ return usage_addr + OFFSET(mem_section_usage_pageblock_flags);
+ }
+ } else {
+ ulong zone_addr = find_pfn_zone(pfn);
+ if (zone_addr && VALID_MEMBER(zone_pageblock_flags)) {
+ ulong pageblock_flags_addr = 0;
+ if (readmem(zone_addr + OFFSET(zone_pageblock_flags),
+ KVADDR, &pageblock_flags_addr, sizeof(ulong),
+ "zone pageblock_flags", RETURN_ON_ERROR|QUIET)) {
+ return pageblock_flags_addr;
+ }
+ }
+ }
+ return 0;
+}
+
+static char *
+get_migratetype_name(int migratetype)
+{
+ static char name_buf[64];
+ ulong array_addr, string_addr;
+
+ if (kernel_symbol_exists("migratetype_names")) {
+ array_addr = symbol_value("migratetype_names") + migratetype * sizeof(void *);
+ if (readmem(array_addr, KVADDR, &string_addr, sizeof(void *), "migratetype_names entry", RETURN_ON_ERROR|QUIET)) {
+ if (read_string(string_addr, name_buf, sizeof(name_buf) - 1)) {
+ return name_buf;
+ }
+ }
+ }
+
+ switch (migratetype) {
+ case 0:
+ return "Unmovable";
+ case 1:
+ return "Movable";
+ case 2:
+ return "Reclaimable";
+ case 3:
+ return "HighAtomic";
+ case 4:
+ return "CMA";
+ case 5:
+ return "Isolate";
+ default:
+ sprintf(name_buf, "unknown_%d", migratetype);
+ return name_buf;
+ }
+}
+
+static char *
+get_page_migration_type_str(physaddr_t phys)
+{
+ ulong pfn = phys / PAGESIZE();
+ ulong pageblock_flags_addr = get_pageblock_bitmap(pfn);
+ if (!pageblock_flags_addr)
+ return "";
+
+ int nr_pageblock_bits = 4;
+ long pb_migrate_isolate = 0;
+ if (enumerator_value("PB_migrate_isolate", &pb_migrate_isolate)) {
+ nr_pageblock_bits = 8;
+ }
+
+ unsigned int pageblock_order = 10;
+ if (kernel_symbol_exists("pageblock_order")) {
+ get_symbol_data("pageblock_order", sizeof(unsigned int), &pageblock_order);
+ }
+
+ int bitidx;
+ if (IS_SPARSEMEM()) {
+ bitidx = ((pfn & (PAGES_PER_SECTION() - 1)) >> pageblock_order) * nr_pageblock_bits;
+ } else {
+ ulong zone_addr = find_pfn_zone(pfn);
+ ulong zone_start_pfn = 0;
+ if (zone_addr) {
+ if (!readmem(zone_addr + OFFSET(zone_zone_start_pfn),
+ KVADDR, &zone_start_pfn, sizeof(ulong),
+ "zone zone_start_pfn", RETURN_ON_ERROR|QUIET))
+ zone_start_pfn = 0;
+ }
+ bitidx = ((pfn - zone_start_pfn) >> pageblock_order) * nr_pageblock_bits;
+ }
+
+ ulong word_bitidx = bitidx / (sizeof(ulong) * 8);
+ bitidx &= ((sizeof(ulong) * 8) - 1);
+
+ ulong word_addr = pageblock_flags_addr + word_bitidx * sizeof(ulong);
+ ulong word = 0;
+ if (!readmem(word_addr, KVADDR, &word, sizeof(ulong), "pageblock_flags word", RETURN_ON_ERROR|QUIET)) {
+ return "";
+ }
+
+ int migratetype = (word >> bitidx) & 7;
+ if (nr_pageblock_bits == 8) {
+ int isolate_bit = 4;
+ if ((word >> (bitidx + isolate_bit)) & 1) {
+ long migrate_isolate = 0;
+ if (enumerator_value("MIGRATE_ISOLATE", &migrate_isolate))
+ migratetype = migrate_isolate;
+ else
+ migratetype = 5;
+ }
+ }
+
+ return get_migratetype_name(migratetype);
+}
+
static void
dump_mem_map(struct meminfo *mi)
{
@@ -6566,10 +6745,19 @@ dump_mem_map(struct meminfo *mi)
bufferindex += sprintflag("%sreserved");
bufferindex += sprintf(outputbuffer+bufferindex, "\n");
} else if (THIS_KERNEL_VERSION > LINUX(2,4,9)) {
- if (vt->flags & PAGEFLAGS)
+ char *mig_type = get_page_migration_type_str(phys);
+ if (vt->flags & PAGEFLAGS) {
bufferindex += translate_page_flags(outputbuffer+bufferindex, flags);
- else
- bufferindex += sprintf(outputbuffer+bufferindex, "%lx\n", flags);
+ if (strlen(mig_type) > 0 && bufferindex > 0 && outputbuffer[bufferindex-1] == '\n') {
+ bufferindex--;
+ bufferindex += sprintf(outputbuffer+bufferindex, " (%s)\n", mig_type);
+ }
+ } else {
+ if (strlen(mig_type) > 0)
+ bufferindex += sprintf(outputbuffer+bufferindex, "%lx (%s)\n", flags, mig_type);
+ else
+ bufferindex += sprintf(outputbuffer+bufferindex, "%lx\n", flags);
+ }
} else {
if ((flags >> v24_PG_locked) & 1)
@@ -8412,8 +8600,12 @@ dump_zone_free_area(ulong free_area, int num, ulong verbose,
ld = &list_data;
- if (!verbose)
- fprintf(fp, "%s", free_area_hdr4);
+ if (!verbose) {
+ if (list_count > 1)
+ fprintf(fp, "AREA SIZE TYPE FREE_AREA_STRUCT BLOCKS PAGES\n");
+ else
+ fprintf(fp, "%s", free_area_hdr4);
+ }
total_free = 0;
flen = MAX(VADDR_PRLEN, strlen("FREE_AREA_STRUCT"));
@@ -8487,14 +8679,21 @@ multiple_lists:
for (j = 0, free_list = free_area; j < list_count;
j++, free_list += SIZE(list_head)) {
- if (verbose)
- fprintf(fp, "%s", free_area_hdr3);
+ if (verbose) {
+ if (list_count > 1)
+ fprintf(fp, "AREA SIZE TYPE FREE_AREA_STRUCT\n");
+ else
+ fprintf(fp, "%s", free_area_hdr3);
+ }
fprintf(fp, "%3d ", i);
chunk_size = power(2, i);
sprintf(buf, "%ldk", (chunk_size * PAGESIZE())/1024);
fprintf(fp, " %7s ", buf);
+ if (list_count > 1)
+ fprintf(fp, "%-12s ", get_migratetype_name(j));
+
readmem(free_list, KVADDR, free_list_buf,
SIZE(list_head), "free_area free_list",
FAULT_ON_ERROR);
@@ -17722,6 +17921,15 @@ sparse_mem_init(void)
MEMBER_OFFSET_INIT(mem_section_section_mem_map, "mem_section",
"section_mem_map");
+ if (MEMBER_EXISTS("mem_section", "pageblock_flags")) {
+ MEMBER_OFFSET_INIT(mem_section_pageblock_flags, "mem_section",
+ "pageblock_flags");
+ }
+ if (MEMBER_EXISTS("mem_section", "usage")) {
+ MEMBER_OFFSET_INIT(mem_section_usage, "mem_section", "usage");
+ STRUCT_SIZE_INIT(mem_section_usage, "mem_section_usage");
+ MEMBER_OFFSET_INIT(mem_section_usage_pageblock_flags, "mem_section_usage", "pageblock_flags");
+ }
if (!MAX_PHYSMEM_BITS())
error(FATAL,
diff --git a/symbols.c b/symbols.c
index 78e400b..2e3a797 100644
--- a/symbols.c
+++ b/symbols.c
@@ -11416,6 +11416,12 @@ dump_offset_table(char *spec, ulong makestruct)
OFFSET(mem_section_section_mem_map));
fprintf(fp, " mem_section_pageblock_flags: %ld\n",
OFFSET(mem_section_pageblock_flags));
+ fprintf(fp, " mem_section_usage: %ld\n",
+ OFFSET(mem_section_usage));
+ fprintf(fp, "mem_section_usage_pageblock_flags: %ld\n",
+ OFFSET(mem_section_usage_pageblock_flags));
+ fprintf(fp, " zone_pageblock_flags: %ld\n",
+ OFFSET(zone_pageblock_flags));
fprintf(fp, " memory_block_dev: %ld\n",
OFFSET(memory_block_dev));
fprintf(fp, " memory_block_nid: %ld\n",
@@ -12158,6 +12164,8 @@ dump_offset_table(char *spec, ulong makestruct)
SIZE(cputime_t));
fprintf(fp, " mem_section: %ld\n",
SIZE(mem_section));
+ fprintf(fp, " mem_section_usage: %ld\n",
+ SIZE(mem_section_usage));
fprintf(fp, " pid_link: %ld\n",
SIZE(pid_link));
fprintf(fp, " upid: %ld\n",
--
2.55.0
4 days, 23 hours
[PATCH 1/2] loongarch64: fix SECTION_SIZE_BITS value
by Dave Young
The loongarch64 SECTION_SIZE_BITS does not match the mainline kernel code.
crash test failed with mem_section init failure due to access non exist
memory address for mem sections caused by wrong NR_SECTION_ROOTS
According to Wang Ming, there was some historic reason, upstream kernel
did not take the SECTION_SIZE_BITS 28, but 28 is still better. Downstream
kernels took the 28 as default.
Thus fix this by setting the related values first according to vmcoreinfo
And falling back to the 29 which aligns to current upstrem kernel.
Signed-off-by: Dave Young <yangrr.2009(a)tsinghua.org.cn>
---
[v1->v2: use vmcoreinfo exported value first]
[future work: share code between arches for converting str to number]
defs.h | 2 +-
loongarch64.c | 18 ++++++++++++++++--
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/defs.h b/defs.h
index 134e7597e966..7c25754afbcd 100644
--- a/defs.h
+++ b/defs.h
@@ -3903,7 +3903,7 @@ typedef signed int s32;
#define TIF_SIGPENDING (1)
-#define _SECTION_SIZE_BITS 28
+#define _SECTION_SIZE_BITS 29
#define _MAX_PHYSMEM_BITS 48
#endif /* LOONGARCH64 */
diff --git a/loongarch64.c b/loongarch64.c
index 39a05b854832..c95910f5ea4a 100644
--- a/loongarch64.c
+++ b/loongarch64.c
@@ -1876,6 +1876,8 @@ pt_level_alloc(char **lvl, char *name)
void
loongarch64_init(int when)
{
+ char *string;
+
switch (when) {
case SETUP_ENV:
machdep->process_elf_notes = process_elf64_notes;
@@ -1933,8 +1935,20 @@ loongarch64_init(int when)
break;
case POST_GDB:
- machdep->section_size_bits = _SECTION_SIZE_BITS;
- machdep->max_physmem_bits = _MAX_PHYSMEM_BITS;
+ string = pc->read_vmcoreinfo("NUMBER(SECTION_SIZE_BITS)");
+ if (string) {
+ machdep->section_size_bits = strtoul(string, NULL, 10);
+ free(string);
+ } else
+ machdep->section_size_bits = _SECTION_SIZE_BITS;
+
+
+ string = pc->read_vmcoreinfo("NUMBER(MAX_PHYSMEM_BITS)");
+ if (string) {
+ machdep->max_physmem_bits = strtoul(string, NULL, 10);
+ free(string);
+ } else
+ machdep->max_physmem_bits = _MAX_PHYSMEM_BITS;
if (symbol_exists("irq_desc"))
ARRAY_LENGTH_INIT(machdep->nr_irqs, irq_desc,
--
2.51.1
5 days, 1 hour
[PATCH 1/2] add cpu_to_nid function
by Huang Shijie
Add cpu_to_nid function which we can use to get the NUMA node id
by the cpu id.
1.) For live mode: use /sys/ files to get the cpu_to_nid.
This method will succeed 100%.
2.) For vmcore mode: use "numa_node" symbol to get the cpu_to_nid.
If CONFIG_USE_PERCPU_NUMA_NODE_ID does not defined in kernel,
this method will fail.
Signed-off-by: Huang Shijie <huangsj(a)hygon.cn>
---
defs.h | 2 ++
kernel.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
main.c | 1 +
3 files changed, 69 insertions(+)
diff --git a/defs.h b/defs.h
index e3027e2..7e1515a 100644
--- a/defs.h
+++ b/defs.h
@@ -6044,6 +6044,8 @@ ulong do_maple_tree(ulong, int, struct list_pair *);
void help_init(void);
void cmd_usage(char *, int);
void display_version(void);
+int cpu_to_nid(int cpu);
+void numa_init(void);
void display_help_screen(char *);
#ifdef ARM
#define dump_machdep_table(X) arm_dump_machdep_table(X)
diff --git a/kernel.c b/kernel.c
index e53038d..32230fb 100644
--- a/kernel.c
+++ b/kernel.c
@@ -12221,3 +12221,69 @@ out:
pc->error_fp = error_fp_save;
}
#endif
+
+static int *cpu_to_nid_map;
+
+int
+cpu_to_nid(int cpu)
+{
+ if (!cpu_to_nid_map || cpu < 0 || cpu >= kt->cpus)
+ return -1;
+ if (vt->numnodes == 1)
+ return 0;
+ return cpu_to_nid_map[cpu];
+}
+
+static void
+cpu_to_nid_init(void)
+{
+ int i, j;
+ int fd;
+ char buf[64];
+
+ cpu_to_nid_map = malloc(kt->cpus * sizeof(int));
+ if (vt->numnodes == 1)
+ return;
+
+ memset(cpu_to_nid_map, -1, kt->cpus * sizeof(int));
+
+ if (ACTIVE()) {
+ for (i = 0; i < kt->cpus; i++) {
+ for (j = 0; j < vt->numnodes; j++) {
+ memset(buf, 0, sizeof(buf));
+ sprintf(buf, "/sys/devices/system/cpu/cpu%d/node%d", i, j);
+
+ fd = open(buf, O_RDONLY);
+ if (fd > 0) {
+ cpu_to_nid_map[i] = j;
+ close(fd);
+ break;
+ }
+ }
+ }
+ } else {
+ int cpu;
+
+ if (symbol_exists("numa_node")) {
+ ulong base = symbol_value("numa_node");
+
+ for (cpu = 0; cpu < kt->cpus; cpu++) {
+ ulong addr = base + kt->__per_cpu_offset[cpu];
+ int nid;
+
+ if (readmem(addr, KVADDR, &nid, sizeof(int),
+ "numa_node", RETURN_ON_ERROR|QUIET))
+ cpu_to_nid_map[cpu] = nid;
+ }
+ } else {
+ error(WARNING,
+ "numa_node symbol not found in vmcore\n");
+ }
+ }
+}
+
+void
+numa_init(void)
+{
+ cpu_to_nid_init();
+}
diff --git a/main.c b/main.c
index d5f8486..a8c77f8 100644
--- a/main.c
+++ b/main.c
@@ -793,6 +793,7 @@ main_loop(void)
kernel_init();
machdep_init(POST_GDB);
vm_init();
+ numa_init();
machdep_init(POST_VM);
module_init();
help_init();
--
2.53.0
5 days, 1 hour
[PATCH v2] riscv64: support leaf PTEs in page table walks
by Wang Yechao
From: yuandong <yuan.dong1(a)zte.com.cn>
The existing RISC-V virtual-to-physical address walkers descend to the
last-level PTE unconditionally. This fails when an upper-level entry is
a leaf mapping, as used for huge pages.
Replace the separate Sv39, Sv48, and Sv57 page-table walkers with a
common n-level implementation. Detect leaf entries at every level,
calculate the physical address using the level-specific page offset,
and reject misaligned leaf entries.
This allows kvtop and uvtop to translate upper-level leaf mappings
while retaining support for normal 4 KiB pages.
Signed-off-by: yuandong <yuan.dong1(a)zte.com.cn>
---
defs.h | 2 +
riscv64.c | 428 +++++++++++++++++++++++++-----------------------------
2 files changed, 201 insertions(+), 229 deletions(-)
diff --git a/defs.h b/defs.h
index e3027e2..ac0b128 100644
--- a/defs.h
+++ b/defs.h
@@ -3801,6 +3801,8 @@ typedef signed int s32;
#define VM_L5_1G (0x200)
#define IRQ_STACKS (0x400)
#define OVERFLOW_STACKS (0x800)
+#define RISCV64_PTE_LEAF(pte) \
+ ((pte) & (_PAGE_READ | _PAGE_WRITE | _PAGE_EXEC))
#define RISCV64_OVERFLOW_STACK_SIZE (1 << 12)
diff --git a/riscv64.c b/riscv64.c
index 4dcecc2..1b6396c 100644
--- a/riscv64.c
+++ b/riscv64.c
@@ -19,12 +19,8 @@
#include <math.h>
static ulong riscv64_get_page_size(void);
-static int riscv64_vtop_3level_4k(ulong *pgd, ulong vaddr,
- physaddr_t *paddr, int verbose);
-static int riscv64_vtop_4level_4k(ulong *pgd, ulong vaddr,
- physaddr_t *paddr, int verbose);
-static int riscv64_vtop_5level_4k(ulong *pgd, ulong vaddr,
- physaddr_t *paddr, int verbose);
+static int riscv64_vtop_nlevel_4k(ulong *pgd, ulong vaddr,
+ physaddr_t *paddr, int verbose, int levels);
static void riscv64_page_type_init(void);
static int riscv64_is_kvaddr(ulong vaddr);
static int riscv64_is_uvaddr(ulong vaddr, struct task_context *tc);
@@ -637,66 +633,208 @@ riscv64_page_type_init(void)
}
}
+static ulong
+riscv64_vtop_index(ulong vaddr, int levels, int level)
+{
+ switch (levels) {
+ case 3:
+ switch (level) {
+ case 2:
+ return pgd_index_l3_4k(vaddr);
+ case 1:
+ return pmd_index_l3_4k(vaddr);
+ case 0:
+ return pte_index_l3_4k(vaddr);
+ }
+ break;
+
+ case 4:
+ switch (level) {
+ case 3:
+ return pgd_index_l4_4k(vaddr);
+ case 2:
+ return pud_index_l4_4k(vaddr);
+ case 1:
+ return pmd_index_l4_4k(vaddr);
+ case 0:
+ return pte_index_l4_4k(vaddr);
+ }
+ break;
+
+ case 5:
+ switch (level) {
+ case 4:
+ return pgd_index_l5_4k(vaddr);
+ case 3:
+ return p4d_index_l5_4k(vaddr);
+ case 2:
+ return pud_index_l5_4k(vaddr);
+ case 1:
+ return pmd_index_l5_4k(vaddr);
+ case 0:
+ return pte_index_l5_4k(vaddr);
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static const char *
+riscv64_level_name(int levels, int level)
+{
+ if (level == levels - 1)
+ return "PGD";
+
+ if (level == 3)
+ return "P4D";
+
+ if (level == 2)
+ return "PUD";
+
+ if (level == 1)
+ return "PMD";
+
+ return "PTE";
+}
+
+static int
+riscv64_pte_page_shift(int level)
+{
+ switch (level) {
+ case 0:
+ return PAGESHIFT();
+ case 1:
+ return PMD_SHIFT;
+ case 2:
+ return PUD_SHIFT;
+ case 3:
+ return P4D_SHIFT;
+ case 4:
+ return PGD_SHIFT_L5;
+ default:
+ return PAGESHIFT();
+ }
+}
+
+static physaddr_t
+riscv64_pte_page_mask(int level)
+{
+ return ((physaddr_t)1 << riscv64_pte_page_shift(level)) - 1;
+}
+
+static physaddr_t
+riscv64_pte_paddr_base(ulong pte)
+{
+ pte &= PTE_PFN_PROT_MASK;
+ return PTOB(pte >> _PAGE_PFN_SHIFT);
+}
+
+static physaddr_t
+riscv64_pte_to_paddr(ulong pte, ulong vaddr, int level)
+{
+ physaddr_t base, mask;
+
+ base = riscv64_pte_paddr_base(pte);
+ mask = riscv64_pte_page_mask(level);
+
+ return base + (vaddr & mask);
+}
+
static int
-riscv64_vtop_3level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr, int verbose)
+riscv64_vtop_nlevel_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr,
+ int verbose, int levels)
{
- ulong *pgd_ptr, pgd_val;
- ulong pmd_base, pmd_addr, pmd_val;
- ulong pte_base, pte_addr, pte_val, pte_pfn;
+ int level;
+ ulong index;
+ ulong entry_addr;
+ ulong pte;
+ ulong table_addr;
+ const char *name;
+
+ if (levels < 3 || levels > 5)
+ return FALSE;
+
+ *paddr = 0;
if (verbose)
fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd);
- /* PGD */
- pgd_ptr = pgd + pgd_index_l3_4k(vaddr);
- FILL_PGD(pgd, KVADDR, PAGESIZE());
- pgd_val = ULONG(machdep->pgd + PAGEOFFSET(pgd_ptr));
- if (verbose)
- fprintf(fp, " PGD: %lx => %lx\n", (ulong)pgd_ptr, pgd_val);
- if (!pgd_val)
- goto no_page;
- pgd_val &= PTE_PFN_PROT_MASK;
- pmd_base = (pgd_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
-
- /* PMD */
- FILL_PMD(PAGEBASE(pmd_base), PHYSADDR, PAGESIZE());
- pmd_addr = pmd_base + sizeof(pmd_t) * pmd_index_l3_4k(vaddr);
- pmd_val = ULONG(machdep->pmd + PAGEOFFSET(pmd_addr));
- if (verbose)
- fprintf(fp, " PMD: %016lx => %016lx\n", pmd_addr, pmd_val);
- if (!pmd_val)
- goto no_page;
- pmd_val &= PTE_PFN_PROT_MASK;
- pte_base = (pmd_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
-
- /* PTE */
- FILL_PTBL(PAGEBASE(pte_base), PHYSADDR, PAGESIZE());
- pte_addr = pte_base + sizeof(pmd_t) * pte_index_l3_4k(vaddr);
- pte_val = ULONG(machdep->ptbl + PAGEOFFSET(pte_addr));
- if (verbose)
- fprintf(fp, " PTE: %lx => %lx\n", pte_addr, pte_val);
- if (!pte_val)
- goto no_page;
- pte_val &= PTE_PFN_PROT_MASK;
- pte_pfn = pte_val >> _PAGE_PFN_SHIFT;
-
- if (!(pte_val & _PAGE_PRESENT)) {
- if (verbose) {
- fprintf(fp, "\n");
- riscv64_translate_pte((ulong)pte_val, 0, 0);
- fprintf(fp, " PAGE: %016lx not present\n\n", PAGEBASE(*paddr));
+ table_addr = (ulong)pgd;
+
+ for (level = levels - 1; level >= 0; level--) {
+ name = riscv64_level_name(levels, level);
+ index = riscv64_vtop_index(vaddr, levels, level);
+
+ if (level == levels - 1) {
+ FILL_PGD((ulong *)PAGEBASE(table_addr), KVADDR,
+ PAGESIZE());
+
+ entry_addr = table_addr + sizeof(ulong) * index;
+ pte = ULONG(machdep->pgd + PAGEOFFSET(entry_addr));
+ } else {
+ FILL_PTBL(PAGEBASE(table_addr), PHYSADDR,
+ PAGESIZE());
+
+ entry_addr = table_addr + sizeof(ulong) * index;
+ pte = ULONG(machdep->ptbl + PAGEOFFSET(entry_addr));
}
- return FALSE;
- }
- *paddr = PTOB(pte_pfn) + PAGEOFFSET(vaddr);
+ if (verbose)
+ fprintf(fp, " %s: %016lx => %016lx\n",
+ name, entry_addr, pte);
+
+ if (!pte)
+ goto no_page;
+
+ pte &= PTE_PFN_PROT_MASK;
+
+ if (!(pte & _PAGE_PRESENT)) {
+ if (verbose) {
+ fprintf(fp, "\n");
+ riscv64_translate_pte(pte, 0, 0);
+ }
+
+ fprintf(fp, " PAGE: %016lx not present\n\n",
+ PAGEBASE(vaddr));
+ return FALSE;
+ }
+
+ if (RISCV64_PTE_LEAF(pte)) {
+ physaddr_t base, mask;
+
+ base = riscv64_pte_paddr_base(pte);
+ mask = riscv64_pte_page_mask(level);
- if (verbose) {
- fprintf(fp, " PAGE: %016lx\n\n", PAGEBASE(*paddr));
- riscv64_translate_pte(pte_val, 0, 0);
+ if (level && (base & mask)) {
+ if (verbose) {
+ fprintf(fp, "\n");
+ riscv64_translate_pte(pte, 0, 0);
+ }
+
+ fprintf(fp,
+ " PAGE: %016lx invalid misaligned leaf pte\n\n",
+ PAGEBASE(vaddr));
+ return FALSE;
+ }
+
+ *paddr = riscv64_pte_to_paddr(pte, vaddr, level);
+
+ if (verbose) {
+ fprintf(fp, " PAGE: %016lx\n\n",
+ (ulong)(*paddr & ~mask));
+ riscv64_translate_pte(pte, 0, 0);
+ }
+
+ return TRUE;
+ }
+
+ if (level == 0)
+ goto no_page;
+
+ table_addr = (ulong)riscv64_pte_paddr_base(pte);
}
- return TRUE;
no_page:
if (verbose)
fprintf(fp, "invalid for %lx address\n", vaddr);
@@ -1218,174 +1356,6 @@ riscv64_get_frame(struct bt_info *bt, ulong *pcp, ulong *spp)
return TRUE;
}
-static int
-riscv64_vtop_4level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr, int verbose)
-{
- ulong *pgd_ptr, pgd_val;
- ulong pud_base, pud_addr, pud_val;
- ulong pmd_base, pmd_addr, pmd_val;
- ulong pte_base, pte_addr, pte_val, pte_pfn;
-
- if (verbose)
- fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd);
-
- /* PGD */
- pgd_ptr = pgd + pgd_index_l4_4k(vaddr);
- FILL_PGD(pgd, KVADDR, PAGESIZE());
- pgd_val = ULONG(machdep->pgd + PAGEOFFSET(pgd_ptr));
- if (verbose)
- fprintf(fp, " PGD: %lx => %lx\n", (ulong)pgd_ptr, pgd_val);
- if (!pgd_val)
- goto no_page;
- pgd_val &= PTE_PFN_PROT_MASK;
- pud_base = (pgd_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
-
- /* PUD */
- FILL_PUD(PAGEBASE(pud_base), PHYSADDR, PAGESIZE());
- pud_addr = pud_base + sizeof(pud_t) * pud_index_l4_4k(vaddr);
- pud_val = ULONG(machdep->pud + PAGEOFFSET(pud_addr));
- if (verbose)
- fprintf(fp, " PUD: %016lx => %016lx\n", pud_addr, pud_val);
- if (!pud_val)
- goto no_page;
- pud_val &= PTE_PFN_PROT_MASK;
- pmd_base = (pud_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
-
- /* PMD */
- FILL_PMD(PAGEBASE(pmd_base), PHYSADDR, PAGESIZE());
- pmd_addr = pmd_base + sizeof(pmd_t) * pmd_index_l4_4k(vaddr);
- pmd_val = ULONG(machdep->pmd + PAGEOFFSET(pmd_addr));
- if (verbose)
- fprintf(fp, " PMD: %016lx => %016lx\n", pmd_addr, pmd_val);
- if (!pmd_val)
- goto no_page;
- pmd_val &= PTE_PFN_PROT_MASK;
- pte_base = (pmd_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
-
- /* PTE */
- FILL_PTBL(PAGEBASE(pte_base), PHYSADDR, PAGESIZE());
- pte_addr = pte_base + sizeof(pte_t) * pte_index_l4_4k(vaddr);
- pte_val = ULONG(machdep->ptbl + PAGEOFFSET(pte_addr));
- if (verbose)
- fprintf(fp, " PTE: %lx => %lx\n", pte_addr, pte_val);
- if (!pte_val)
- goto no_page;
- pte_val &= PTE_PFN_PROT_MASK;
- pte_pfn = pte_val >> _PAGE_PFN_SHIFT;
-
- if (!(pte_val & _PAGE_PRESENT)) {
- if (verbose) {
- fprintf(fp, "\n");
- riscv64_translate_pte((ulong)pte_val, 0, 0);
- fprintf(fp, " PAGE: %016lx not present\n\n", PAGEBASE(*paddr));
- }
- return FALSE;
- }
-
- *paddr = PTOB(pte_pfn) + PAGEOFFSET(vaddr);
-
- if (verbose) {
- fprintf(fp, " PAGE: %016lx\n\n", PAGEBASE(*paddr));
- riscv64_translate_pte(pte_val, 0, 0);
- }
-
- return TRUE;
-no_page:
- if (verbose)
- fprintf(fp, "invalid for %lx address\n", vaddr);
- return FALSE;
-}
-
-static int
-riscv64_vtop_5level_4k(ulong *pgd, ulong vaddr, physaddr_t *paddr, int verbose)
-{
- ulong *pgd_ptr, pgd_val;
- ulong p4d_base, p4d_addr, p4d_val;
- ulong pud_base, pud_addr, pud_val;
- ulong pmd_base, pmd_addr, pmd_val;
- ulong pte_base, pte_addr, pte_val, pte_pfn;
-
- if (verbose)
- fprintf(fp, "PAGE DIRECTORY: %lx\n", (ulong)pgd);
-
- /* PGD */
- pgd_ptr = pgd + pgd_index_l5_4k(vaddr);
- FILL_PGD(pgd, KVADDR, PAGESIZE());
- pgd_val = ULONG(machdep->pgd + PAGEOFFSET(pgd_ptr));
- if (verbose)
- fprintf(fp, " PGD: %lx => %lx\n", (ulong)pgd_ptr, pgd_val);
- if (!pgd_val)
- goto no_page;
- pgd_val &= PTE_PFN_PROT_MASK;
- p4d_base = (pgd_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
-
- /* P4D */
- FILL_P4D(PAGEBASE(p4d_base), PHYSADDR, PAGESIZE());
- p4d_addr = p4d_base + sizeof(p4d_t) * p4d_index_l5_4k(vaddr);
- p4d_val = ULONG(machdep->machspec->p4d + PAGEOFFSET(p4d_addr));
- if (verbose)
- fprintf(fp, " P4D: %016lx => %016lx\n", p4d_addr, p4d_val);
- if (!p4d_val)
- goto no_page;
- p4d_val &= PTE_PFN_PROT_MASK;
- pud_base = (p4d_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
-
- /* PUD */
- FILL_PUD(PAGEBASE(pud_base), PHYSADDR, PAGESIZE());
- pud_addr = pud_base + sizeof(pud_t) * pud_index_l5_4k(vaddr);
- pud_val = ULONG(machdep->pud + PAGEOFFSET(pud_addr));
- if (verbose)
- fprintf(fp, " PUD: %016lx => %016lx\n", pud_addr, pud_val);
- if (!pud_val)
- goto no_page;
- pud_val &= PTE_PFN_PROT_MASK;
- pmd_base = (pud_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
-
- /* PMD */
- FILL_PMD(PAGEBASE(pmd_base), PHYSADDR, PAGESIZE());
- pmd_addr = pmd_base + sizeof(pmd_t) * pmd_index_l5_4k(vaddr);
- pmd_val = ULONG(machdep->pmd + PAGEOFFSET(pmd_addr));
- if (verbose)
- fprintf(fp, " PMD: %016lx => %016lx\n", pmd_addr, pmd_val);
- if (!pmd_val)
- goto no_page;
- pmd_val &= PTE_PFN_PROT_MASK;
- pte_base = (pmd_val >> _PAGE_PFN_SHIFT) << PAGESHIFT();
-
- /* PTE */
- FILL_PTBL(PAGEBASE(pte_base), PHYSADDR, PAGESIZE());
- pte_addr = pte_base + sizeof(pte_t) * pte_index_l5_4k(vaddr);
- pte_val = ULONG(machdep->ptbl + PAGEOFFSET(pte_addr));
- if (verbose)
- fprintf(fp, " PTE: %lx => %lx\n", pte_base, pte_val);
- if (!pte_val)
- goto no_page;
- pte_val &= PTE_PFN_PROT_MASK;
- pte_pfn = pte_val >> _PAGE_PFN_SHIFT;
-
- if (!(pte_val & _PAGE_PRESENT)) {
- if (verbose) {
- fprintf(fp, "\n");
- riscv64_translate_pte((ulong)pte_val, 0, 0);
- fprintf(fp, " PAGE: %016lx not present\n\n", PAGEBASE(*paddr));
- }
- return FALSE;
- }
-
- *paddr = PTOB(pte_pfn) + PAGEOFFSET(vaddr);
-
- if (verbose) {
- fprintf(fp, " PAGE: %016lx\n\n", PAGEBASE(*paddr));
- riscv64_translate_pte(pte_val, 0, 0);
- }
-
- return TRUE;
-no_page:
- if (verbose)
- fprintf(fp, "invalid for %lx address\n", vaddr);
- return FALSE;
-}
-
static int
riscv64_init_active_task_regs(void)
{
@@ -1610,11 +1580,11 @@ riscv64_uvtop(struct task_context *tc, ulong uvaddr, physaddr_t *paddr, int verb
switch (machdep->flags & VM_FLAGS)
{
case VM_L3_4K:
- return riscv64_vtop_3level_4k(pgd, uvaddr, paddr, verbose);
+ return riscv64_vtop_nlevel_4k(pgd, uvaddr, paddr, verbose, 3);
case VM_L4_4K:
- return riscv64_vtop_4level_4k(pgd, uvaddr, paddr, verbose);
+ return riscv64_vtop_nlevel_4k(pgd, uvaddr, paddr, verbose, 4);
case VM_L5_4K:
- return riscv64_vtop_5level_4k(pgd, uvaddr, paddr, verbose);
+ return riscv64_vtop_nlevel_4k(pgd, uvaddr, paddr, verbose, 5);
default:
return FALSE;
}
@@ -1669,11 +1639,11 @@ riscv64_kvtop(struct task_context *tc, ulong kvaddr, physaddr_t *paddr, int verb
switch (machdep->flags & VM_FLAGS)
{
case VM_L3_4K:
- return riscv64_vtop_3level_4k((ulong *)kernel_pgd, kvaddr, paddr, verbose);
+ return riscv64_vtop_nlevel_4k((ulong *)kernel_pgd, kvaddr, paddr, verbose, 3);
case VM_L4_4K:
- return riscv64_vtop_4level_4k((ulong *)kernel_pgd, kvaddr, paddr, verbose);
+ return riscv64_vtop_nlevel_4k((ulong *)kernel_pgd, kvaddr, paddr, verbose, 4);
case VM_L5_4K:
- return riscv64_vtop_5level_4k((ulong *)kernel_pgd, kvaddr, paddr, verbose);
+ return riscv64_vtop_nlevel_4k((ulong *)kernel_pgd, kvaddr, paddr, verbose, 5);
default:
return FALSE;
}
--
2.27.0
5 days, 1 hour
Madagascar Evisa Fee
by a.lexandra.24510.1@gmail.com
<a href="https://evisa-madagascar.it.com/madagascar-evisa-fee-and-processing-time/">Madagascar eVisa Fee</a> refers to the cost required to process an electronic visa application for traveling to Madagascar. The fee may vary depending on the type of eVisa, duration of stay, nationality, and processing requirements. Applicants are generally required to pay the applicable fee online during the application process. The payment confirms the submission and allows authorities to review the visa request. Travelers should ensure that all information provided is accurate before making payment, as fees may be non-refundable in some cases. Before applying, applicants should check the latest Madagascar eVisa fee details, accepted payment methods, and updated visa regulations for a smooth experience.
5 days, 19 hours
[PATCH] loongarch64: fix mem section init failure
by Dave Young
The loongarch64 SECTION_SIZE_BITS does not match the mainline kernel code.
crash test failed with mem_section init failure due to access non exist
memory address for mem sections caused by wrong NR_SECTION_ROOTS
Signed-off-by: Dave Young <yangrr.2009(a)tsinghua.org.cn>
---
defs.h | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
Index: crash/defs.h
===================================================================
--- crash.orig/defs.h 2026-08-18 09:40:48.392309457 +0800
+++ crash/defs.h 2026-08-24 14:46:52.133057387 +0800
@@ -3903,7 +3903,7 @@
#define TIF_SIGPENDING (1)
-#define _SECTION_SIZE_BITS 28
+#define _SECTION_SIZE_BITS 29
#define _MAX_PHYSMEM_BITS 48
#endif /* LOONGARCH64 */
5 days, 22 hours
[PATCH v2] drm/qxl: fix use-after-free and NULL pointer deref
by Jiri Slaby (SUSE)
When allocating a `qxl_release` structure with `kmalloc()`, the underlying
memory contained uninitialized garbage. Specifically, `release->base.flags`
(part of the embedded `dma_fence`) was not cleared.
This garbage in `base.flags` caused helper functions such as
`dma_fence_was_initialized()` to return true even for releases where the
fence was never actually initialized (e.g. via `dma_fence_init()`).
Consequently, during release cleanup in `qxl_release_free()`, the driver
attempted to put/free an uninitialized `dma_fence`, leading to refcount
underflows (`refcount_t: underflow; use-after-free`) and subsequent NULL
pointer dereferences in `dma_fence_signal_timestamp_locked()`.
Fix this by switching from `kmalloc()` to `kzalloc_obj()` in
`qxl_release_alloc()`, ensuring all fields (including embedded fence
flags) are properly zero-initialized upon allocation, and remove
redundant explicit zero-initializations.
The dumps in question:
refcount_t: underflow; use-after-free.
WARNING: lib/refcount.c:28 at refcount_warn_saturate+0x59/0x90, CPU#0: kworker/0:0/1534
Modules linked in: af_packet nft_fib_inet ...
CPU: 0 UID: 0 PID: 1534 Comm: kworker/0:0 Not tainted 7.1.3-1-default #1 PREEMPT(full) openSUSE Tumbleweed b041a6527f6e58424f4cd3de0fade8d408b378fd
...
RIP: 0010:refcount_warn_saturate+0x59/0x90
...
Call Trace:
<TASK>
qxl_release_free+0xee/0xf0 [qxl d93e9381353e619799d56790f5f8dda6cce491f6]
qxl_garbage_collect+0xd1/0x1b0 [qxl d93e9381353e619799d56790f5f8dda6cce491f6]
process_one_work+0x19e/0x3a0
...
And then of course:
BUG: kernel NULL pointer dereference, address: 0000000000000028
...
RIP: 0010:dma_fence_signal_timestamp_locked+0x32/0x120
Signed-off-by: Jiri Slaby (SUSE) <jirislaby(a)kernel.org>
Assisted-by: Gemini <gemini(a)google.com> # only commit log
Fixes: 2bcbc706dfa0 ("dma-buf: add dma_fence_was_initialized function v2")
Closes: https://bugzilla.suse.com/show_bug.cgi?id=1271081
Cc: Christian König <christian.koenig(a)amd.com>
Cc: Tvrtko Ursulin <tvrtko.ursulin(a)igalia.com>
Cc: Dave Airlie <airlied(a)redhat.com>
Cc: Gerd Hoffmann <kraxel(a)redhat.com>
Cc: Maarten Lankhorst <maarten.lankhorst(a)linux.intel.com>
Cc: Maxime Ripard <mripard(a)kernel.org>
Cc: Thomas Zimmermann <tzimmermann(a)suse.de>
Cc: David Airlie <airlied(a)gmail.com>
Cc: Simona Vetter <simona(a)ffwll.ch>
Cc: stable(a)vger.kernel.org
---
Cc: virtualization(a)lists.linux.dev
Cc: spice-devel(a)lists.freedesktop.org
Cc: dri-devel(a)lists.freedesktop.org
[v2] use kzalloc_obj() instead of bare kzalloc()
---
drivers/gpu/drm/qxl/qxl_release.c | 6 +-----
1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/drivers/gpu/drm/qxl/qxl_release.c b/drivers/gpu/drm/qxl/qxl_release.c
index 06979d0e8a9f..07dc6eafe6f7 100644
--- a/drivers/gpu/drm/qxl/qxl_release.c
+++ b/drivers/gpu/drm/qxl/qxl_release.c
@@ -89,17 +89,13 @@ qxl_release_alloc(struct qxl_device *qdev, int type,
{
struct qxl_release *release;
int handle;
- size_t size = sizeof(*release);
- release = kmalloc(size, GFP_KERNEL);
+ release = kzalloc_obj(*release);
if (!release) {
DRM_ERROR("Out of memory\n");
return -ENOMEM;
}
- release->base.ops = NULL;
release->type = type;
- release->release_offset = 0;
- release->surface_release_id = 0;
INIT_LIST_HEAD(&release->bos);
idr_preload(GFP_KERNEL);
--
2.55.0
1 week, 2 days
How Can SaaS Pitch Decks Show a Clear Path to Expansion?
by jamesdavid15161@gmail.com
SaaS startups often begin with a focused product and a specific customer segment before expanding into new markets, industries, or product categories. Investors may want to understand whether the business has realistic opportunities to grow beyond its initial market. SaaS pitch deck experts can help founders structure an expansion story that connects current traction with future opportunities without relying on overly optimistic projections. I'm interested in hearing different perspectives on this topic. What should SaaS founders include when explaining their expansion strategy, and what makes a growth opportunity appear realistic to investors. Visit https://www.slidey.io/sa/saas-pitch-deck-design-agency
1 week, 4 days
[PATCH] loongarch64: enable kaslr offset calculation
by Dave Young
crash failed with mainline kernel with kaslr enabled.
Add the flags which will enable kaslr offset calculating.
Signed-off-by: Dave Young <yangrr.2009(a)tsinghua.org.cn>
---
loongarch64.c | 8 ++++++++
1 file changed, 8 insertions(+)
Index: crash/loongarch64.c
===================================================================
--- crash.orig/loongarch64.c 2026-08-18 08:59:46.731117021 +0800
+++ crash/loongarch64.c 2026-08-24 14:55:57.330529708 +0800
@@ -1891,6 +1891,14 @@
machdep->last_ptbl_read = 0;
machdep->verify_paddr = generic_verify_paddr;
machdep->ptrs_per_pgd = PTRS_PER_PGD;
+
+ /*
+ * Even if CONFIG_RANDOMIZE_BASE is not configured,
+ * derive_kaslr_offset() should work and set
+ * kt->relocate to 0
+ */
+ if (!kt->relocate && !(kt->flags2 & (RELOC_AUTO|KASLR)))
+ kt->flags2 |= (RELOC_AUTO|KASLR);
break;
case PRE_GDB:
1 week, 6 days
[PATCH] memory: handle kmem_cache per-node pointer layout
by Harshvardhan Jha
Linux commit 5ba6bc27b1f9 ("slab: decouple pointer to barn from
kmem_cache_node") moved the kmem_cache_node pointers from
kmem_cache.node[] to kmem_cache.per_node[].node.
Teach the SLUB traversal code to recognize and handle both layouts.
Without this, crash treats the new layout as non-NUMA and kmem -s fails
while looking for the removed kmem_cache.local_node member.
Signed-off-by: Harshvardhan Jha <harshvardhan.j.jha(a)oracle.com>
---
Testing was performed manually on x86_64 VMs:
- Linux 7.1.0-rc7 with the new per_node layout: upstream crash failed
with the kmem_cache_local_node invalid-offset error, while the patched
crash completed kmem -s and displayed the slab-cache table.
- Linux 6.18.0-rc7 with the old node[1024] layout: the patched crash
completed kmem -s and displayed the slab-cache table.
defs.h | 3 +++
memory.c | 39 ++++++++++++++++++++++++++++++---------
symbols.c | 6 ++++++
3 files changed, 39 insertions(+), 9 deletions(-)
diff --git a/defs.h b/defs.h
index e3027e2..ccd84a0 100644
--- a/defs.h
+++ b/defs.h
@@ -1766,6 +1766,8 @@ struct offset_table { /* stash of commonly-used offsets */
long kmem_cache_name;
long kmem_cache_list;
long kmem_cache_node;
+ long kmem_cache_per_node;
+ long kmem_cache_per_node_node;
long kmem_cache_cpu_slab;
long page_inuse;
/* long page_offset; use "old" page->offset */
@@ -2395,6 +2397,7 @@ struct size_table { /* stash of commonly-used sizes */
long rlimit;
long kmem_cache;
long kmem_cache_node;
+ long kmem_cache_per_node_ptrs;
long upid;
long kmem_cache_cpu;
long cfs_rq;
diff --git a/memory.c b/memory.c
index 3f3aa27..cf26bd4 100644
--- a/memory.c
+++ b/memory.c
@@ -306,6 +306,7 @@ static int get_kmem_cache_list(ulong **);
static int get_kmem_cache_root_list(ulong **);
static int get_kmem_cache_child_list(ulong **, ulong);
static int get_kmem_cache_slub_data(long, struct meminfo *);
+static ulong get_kmem_cache_node_ptr(struct meminfo *, int);
static ulong compound_head(ulong);
static long count_partial(ulong, struct meminfo *, ulong *);
static short count_cpu_partial(struct meminfo *, int);
@@ -869,6 +870,9 @@ vm_init(void)
MEMBER_OFFSET_INIT(kmem_cache_inuse, "kmem_cache", "inuse");
MEMBER_OFFSET_INIT(kmem_cache_align, "kmem_cache", "align");
MEMBER_OFFSET_INIT(kmem_cache_node, "kmem_cache", "node");
+ MEMBER_OFFSET_INIT(kmem_cache_per_node, "kmem_cache", "per_node");
+ MEMBER_OFFSET_INIT(kmem_cache_per_node_node,
+ "kmem_cache_per_node_ptrs", "node");
MEMBER_OFFSET_INIT(kmem_cache_cpu_slab, "kmem_cache", "cpu_slab");
MEMBER_OFFSET_INIT(kmem_cache_list, "kmem_cache", "list");
MEMBER_OFFSET_INIT(kmem_cache_red_left_pad, "kmem_cache", "red_left_pad");
@@ -920,9 +924,16 @@ vm_init(void)
if (INVALID_MEMBER(page_objects))
ANON_MEMBER_OFFSET_INIT(page_objects, "slab", "objects");
}
+ STRUCT_SIZE_INIT(kmem_cache_per_node_ptrs,
+ "kmem_cache_per_node_ptrs");
if (VALID_MEMBER(kmem_cache_node)) {
ARRAY_LENGTH_INIT(len, NULL, "kmem_cache.node", NULL, 0);
vt->flags |= CONFIG_NUMA;
+ } else if (VALID_MEMBER(kmem_cache_per_node) &&
+ VALID_MEMBER(kmem_cache_per_node_node) &&
+ VALID_SIZE(kmem_cache_per_node_ptrs)) {
+ ARRAY_LENGTH_INIT(len, NULL, "kmem_cache.per_node", NULL, 0);
+ vt->flags |= CONFIG_NUMA;
}
ARRAY_LENGTH_INIT(len, NULL, "kmem_cache.cpu_slab", NULL, 0);
@@ -19460,6 +19471,22 @@ count_cpu_partial(struct meminfo *si, int cpu)
return free_objects;
}
+static ulong
+get_kmem_cache_node_ptr(struct meminfo *si, int node)
+{
+ char *node_addr;
+
+ if (VALID_MEMBER(kmem_cache_node))
+ node_addr = si->cache_buf + OFFSET(kmem_cache_node) +
+ (sizeof(void *) * node);
+ else
+ node_addr = si->cache_buf + OFFSET(kmem_cache_per_node) +
+ (SIZE(kmem_cache_per_node_ptrs) * node) +
+ OFFSET(kmem_cache_per_node_node);
+
+ return ULONG(node_addr);
+}
+
/*
* Emulate the total count calculation done by the
* slab_objects() sysfs function in slub.c.
@@ -19537,9 +19564,7 @@ get_kmem_cache_slub_data(long cmd, struct meminfo *si)
for (n = 0; n < vt->numnodes; n++) {
if (vt->flags & CONFIG_NUMA) {
nt = &vt->node_table[n];
- node_ptr = ULONG(si->cache_buf +
- OFFSET(kmem_cache_node) +
- (sizeof(void *) * nt->node_id));
+ node_ptr = get_kmem_cache_node_ptr(si, nt->node_id);
} else
node_ptr = si->cache +
OFFSET(kmem_cache_local_node);
@@ -19709,9 +19734,7 @@ do_kmem_cache_slub(struct meminfo *si)
for (n = 0; n < vt->numnodes; n++) {
if (vt->flags & CONFIG_NUMA) {
nt = &vt->node_table[n];
- node_ptr = ULONG(si->cache_buf +
- OFFSET(kmem_cache_node) +
- (sizeof(void *)* nt->node_id));
+ node_ptr = get_kmem_cache_node_ptr(si, nt->node_id);
} else
node_ptr = si->cache +
OFFSET(kmem_cache_local_node);
@@ -20515,9 +20538,7 @@ slab_to_kmem_cache_node(struct meminfo *si, ulong slab_page)
if (vt->flags & CONFIG_NUMA) {
node = page_to_nid(slab_page);
- node_ptr = ULONG(si->cache_buf +
- OFFSET(kmem_cache_node) +
- (sizeof(void *)*node));
+ node_ptr = get_kmem_cache_node_ptr(si, node);
} else
node_ptr = si->cache + OFFSET(kmem_cache_local_node);
diff --git a/symbols.c b/symbols.c
index a7ccdb1..652fce4 100644
--- a/symbols.c
+++ b/symbols.c
@@ -10922,6 +10922,10 @@ dump_offset_table(char *spec, ulong makestruct)
OFFSET(kmem_cache_red_left_pad));
fprintf(fp, " kmem_cache_node: %ld\n",
OFFSET(kmem_cache_node));
+ fprintf(fp, " kmem_cache_per_node: %ld\n",
+ OFFSET(kmem_cache_per_node));
+ fprintf(fp, " kmem_cache_per_node_node: %ld\n",
+ OFFSET(kmem_cache_per_node_node));
fprintf(fp, " kmem_cache_cpu_slab: %ld\n",
OFFSET(kmem_cache_cpu_slab));
fprintf(fp, " kmem_cache_cpu_partial: %ld\n",
@@ -12042,6 +12046,8 @@ dump_offset_table(char *spec, ulong makestruct)
SIZE(kmem_bufctl_t));
fprintf(fp, " kmem_cache: %ld\n", SIZE(kmem_cache));
fprintf(fp, " kmem_cache_node: %ld\n", SIZE(kmem_cache_node));
+ fprintf(fp, " kmem_cache_per_node_ptrs: %ld\n",
+ SIZE(kmem_cache_per_node_ptrs));
fprintf(fp, " kmem_cache_cpu: %ld\n", SIZE(kmem_cache_cpu));
fprintf(fp, " swap_info_struct: %ld\n",
--
2.52.0
2 weeks, 5 days