From 59a3b4b4e797278921f82955d57811a732d84e7d Mon Sep 17 00:00:00 2001
From: chenguanyou <chenguanyou@xiaomi.com>
Date: Mon, 11 Sep 2023 20:59:39 +0800
Subject: [PATCH v3] Fix: move huge compressed obj from page to zspage

when zspage define 'huge', crash-utility zram decompress fail.
we need to be compatible with the previous kernel,
so we can't define 'huge' directly in zspage, just move to the right when calculating.

Before:
crash> mod -s zram zram.ko
     MODULE       NAME                                          BASE           SIZE  OBJECT FILE
ffffffde224db800  zram                                    ffffffde224d2000    57344  zram.ko
crash> mod -s zsmalloc zsmalloc.ko
     MODULE       NAME                                          BASE           SIZE  OBJECT FILE
ffffffde224c5180  zsmalloc                                ffffffde224bf000    40960  zsmalloc.ko
crash> rd 0x13d89fb0
rd: zspage magic incorrect: b0

After:
crash> rd 0x13d89fb0
        13d89fb0:  c2b54f7170883b20                     ;.pqO..

Link: https://lkml.kernel.org/r/20211115185909.3949505-6-minchan@kernel.org

Signed-off-by: chenguanyou <chenguanyou@xiaomi.com>
---
 defs.h     | 13 ++++++++++
 diskdump.c | 69 ++++++++++++++++++++++++++++++++++++++++++++----------
 2 files changed, 70 insertions(+), 12 deletions(-)

diff --git a/defs.h b/defs.h
index 96a7a2a..0af69cc 100644
--- a/defs.h
+++ b/defs.h
@@ -2225,6 +2225,7 @@ struct offset_table {                    /* stash of commonly-used offsets */
 	long module_memory_base;
 	long module_memory_size;
 	long irq_data_irq;
+	long zspage_huge;
 };
 
 struct size_table {         /* stash of commonly-used sizes */
@@ -7221,6 +7222,18 @@ struct zspage {
     unsigned int freeobj;
 };
 
+struct zspage6 {
+    struct {
+        unsigned int huge : 1;
+        unsigned int fullness : 2;
+        unsigned int class : 9;
+        unsigned int isolated : 3;
+        unsigned int magic : 8;
+    };
+    unsigned int inuse;
+    unsigned int freeobj;
+};
+
 /*
  * makedumpfile.c
  */
diff --git a/diskdump.c b/diskdump.c
index 2c284ff..592667f 100644
--- a/diskdump.c
+++ b/diskdump.c
@@ -2754,6 +2754,9 @@ zram_init(void)
 	if (INVALID_MEMBER(zram_table_flag))
 		MEMBER_OFFSET_INIT(zram_table_flag, "zram_table_entry", "value");
 	STRUCT_SIZE_INIT(zram_table_entry, "zram_table_entry");
+    MEMBER_OFFSET_INIT(zspoll_size_class, "zs_pool", "size_class");
+    MEMBER_OFFSET_INIT(size_class_size, "size_class", "size");
+    MEMBER_OFFSET_INIT(zspage_huge, "zspage", "huge");
 }
 
 static unsigned char *
@@ -2761,9 +2764,11 @@ zram_object_addr(ulong pool, ulong handle, unsigned char *zram_buf)
 {
 	ulong obj, off, class, page, zspage;
 	struct zspage zspage_s;
+	struct zspage6 zspage6_s;
 	physaddr_t paddr;
 	unsigned int obj_idx, class_idx, size;
 	ulong pages[2], sizes[2];
+	ulong zs_magic;
 
 	readmem(handle, KVADDR, &obj, sizeof(void *), "zram entry", FAULT_ON_ERROR);
 	obj >>= OBJ_TAG_BITS;
@@ -2772,11 +2777,19 @@ zram_object_addr(ulong pool, ulong handle, unsigned char *zram_buf)
 
 	readmem(page + OFFSET(page_private), KVADDR, &zspage,
 			sizeof(void *), "page_private", FAULT_ON_ERROR);
-	readmem(zspage, KVADDR, &zspage_s, sizeof(struct zspage), "zspage", FAULT_ON_ERROR);
 
-	class_idx = zspage_s.class;
-	if (zspage_s.magic != ZSPAGE_MAGIC)
-		error(FATAL, "zspage magic incorrect: %x\n", zspage_s.magic);
+	if (VALID_MEMBER(zspage_huge)) {
+		readmem(zspage, KVADDR, &zspage6_s, sizeof(struct zspage6), "zspage6", FAULT_ON_ERROR);
+		class_idx = zspage6_s.class;
+		zs_magic = zspage6_s.magic;
+	} else {
+		readmem(zspage, KVADDR, &zspage_s, sizeof(struct zspage), "zspage", FAULT_ON_ERROR);
+		class_idx = zspage_s.class;
+		zs_magic = zspage_s.magic;
+	}
+
+	if (zs_magic != ZSPAGE_MAGIC)
+		error(FATAL, "zspage magic incorrect: %x\n", zs_magic);
 
 	class = pool + OFFSET(zspoll_size_class);
 	class += (class_idx * sizeof(void *));
@@ -2794,8 +2807,13 @@ zram_object_addr(ulong pool, ulong handle, unsigned char *zram_buf)
 	}
 
 	pages[0] = page;
-	readmem(page + OFFSET(page_freelist), KVADDR, &pages[1],
-			sizeof(void *), "page_freelist", FAULT_ON_ERROR);
+	if (VALID_MEMBER(page_freelist)) {
+		readmem(page + OFFSET(page_freelist), KVADDR, &pages[1],
+				sizeof(void *), "page_freelist", FAULT_ON_ERROR);
+	} else {
+		readmem(page + OFFSET(page_index), KVADDR, &pages[1],
+				sizeof(void *), "page_index", FAULT_ON_ERROR);
+	}
 	sizes[0] = PAGESIZE() - off;
 	sizes[1] = size - sizes[0];
 	if (!is_page_ptr(pages[0], &paddr)) {
@@ -2812,9 +2830,15 @@ zram_object_addr(ulong pool, ulong handle, unsigned char *zram_buf)
 	readmem(paddr, PHYSADDR, zram_buf + sizes[0], sizes[1], "zram buffer[1]", FAULT_ON_ERROR);
 
 out:
-	readmem(page, KVADDR, &obj, sizeof(void *), "page flags", FAULT_ON_ERROR);
-	if (!(obj & (1<<10))) { //PG_OwnerPriv1 flag
-		return (zram_buf + ZS_HANDLE_SIZE);
+	if (!VALID_MEMBER(zspage_huge)) {
+		readmem(page, KVADDR, &obj, sizeof(void *), "page flags", FAULT_ON_ERROR);
+		if (!(obj & (1<<10))) { //PG_OwnerPriv1 flag
+			return (zram_buf + ZS_HANDLE_SIZE);
+		}
+	} else {
+		if (!zspage6_s.huge) {
+			return (zram_buf + ZS_HANDLE_SIZE);
+		}
 	}
 
 	return zram_buf;
@@ -2929,6 +2953,8 @@ try_zram_decompress(ulonglong pte_val, unsigned char *buf, ulong len, ulonglong
 	unsigned char *outbuf = NULL;
 	ulong zram, zram_table_entry, sector, index, entry, flags, size,
 		outsize, off;
+	unsigned long *same_buf = NULL;
+	bool is_same = false;
 
 	if (INVALID_MEMBER(zram_compressor)) {
 		zram_init();
@@ -2992,11 +3018,30 @@ try_zram_decompress(ulonglong pte_val, unsigned char *buf, ulong len, ulonglong
 		sizeof(void *), "entry of table", FAULT_ON_ERROR);
 	readmem(zram_table_entry + OFFSET(zram_table_flag), KVADDR, &flags,
 		sizeof(void *), "zram_table_flag", FAULT_ON_ERROR);
-	if (!entry || (flags & ZRAM_FLAG_SAME_BIT)) {
-		memset(buf, entry, len);
+
+	if (THIS_KERNEL_VERSION >= LINUX(5,1,0)) {
+		if (!entry || flags & (1 << (PAGESHIFT() + 1 + 1)))
+			is_same = true;
+	} else {
+		if (!entry || (flags & ZRAM_FLAG_SAME_BIT))
+			is_same = true;
+	}
+
+	if (is_same) {
+		same_buf = (unsigned long *)GETBUF(PAGESIZE());
+		for (int count = 0; count < PAGESIZE() / sizeof(unsigned long); count++) {
+			same_buf[count] = entry;
+		}
+		memcpy(buf, same_buf + off, PAGESIZE());
+		FREEBUF(same_buf);
 		goto out;
 	}
-	size = flags & (ZRAM_FLAG_SHIFT -1);
+
+	if (THIS_KERNEL_VERSION >= LINUX(5,1,0)) {
+		size = flags & ((1 << (PAGESHIFT() + 1)) - 1);
+	} else {
+		size = flags & (ZRAM_FLAG_SHIFT -1);
+	}
 	if (size == 0) {
 		len = 0;
 		goto out;
-- 
2.39.0

