Thank you for the fix, Kazu.

On Tue, Jun 11, 2024 at 10:42 AM <devel-request@lists.crash-utility.osci.io> wrote:
Date: Tue, 11 Jun 2024 02:40:55 +0000
From: HAGIO KAZUHITO(萩尾 一仁) <k-hagio-ab@nec.com>
Subject: [Crash-utility] [PATCH] Fix "kmem -i" and "swap" commands on
        Linux 6.10-rc1 and later kernels
To: "devel@lists.crash-utility.osci.io"
        <devel@lists.crash-utility.osci.io>
Message-ID: <1718073652-13444-1-git-send-email-k-hagio-ab@nec.com>
Content-Type: text/plain; charset="iso-2022-jp"

Kernel commit 798cb7f9aec3 ("swapon(2)/swapoff(2): don't bother with
block size") removed swap_info_struct.old_block_size member at Linux
6.10-rc1.  The crash-utility has used this to determine whether a swap
is a partition or file and to determine the way to get the swap path.

Withtout the patch, the "kmem -i" and "swap" commands fail with the
following error messsage:

  crash> kmem -i
  ...
     TOTAL HUGE  13179392      50.3 GB         ----
      HUGE FREE  13179392      50.3 GB  100% of TOTAL HUGE

  swap: invalid (optional) structure member offsets: swap_info_struct_swap_device or swap_info_struct_old_block_size
        FILE: memory.c  LINE: 16032  FUNCTION: dump_swap_info()

The swap_file member of recent swap_info_struct is a pointer to a
struct file (once upon a time it was dentry), use this fact directly.

Signed-off-by: Kazuhito Hagio <k-hagio-ab@nec.com>
---
NOTE: This patch can be applied on top of my '[PATCH] Fix "kmem -v"
option on Linux 6.9 and later kernels' patch.  Otherwise, please adjust
the hunk for offset_table.

 defs.h    |  1 +
 filesys.c |  1 +
 memory.c  | 28 +++++++++++++++++++++++-----
 symbols.c |  1 +
 4 files changed, 26 insertions(+), 5 deletions(-)

diff --git a/defs.h b/defs.h
index 95de33188070..64bf785b7a1b 100644
--- a/defs.h
+++ b/defs.h
@@ -2242,6 +2242,7 @@ struct offset_table {                    /* stash of commonly-used offsets */
        long log_caller_id;
        long vmap_node_busy;
        long rb_list_head;
+       long file_f_inode;
 };

 struct size_table {         /* stash of commonly-used sizes */
diff --git a/filesys.c b/filesys.c
index 81fe856699e1..406ebb299780 100644
--- a/filesys.c
+++ b/filesys.c
@@ -2038,6 +2038,7 @@ vfs_init(void)
        MEMBER_OFFSET_INIT(file_f_dentry, "file", "f_dentry");
        MEMBER_OFFSET_INIT(file_f_vfsmnt, "file", "f_vfsmnt");
        MEMBER_OFFSET_INIT(file_f_count, "file", "f_count");
+       MEMBER_OFFSET_INIT(file_f_inode, "file", "f_inode");
        MEMBER_OFFSET_INIT(path_mnt, "path", "mnt");
        MEMBER_OFFSET_INIT(path_dentry, "path", "dentry");
        if (INVALID_MEMBER(file_f_dentry)) {
diff --git a/memory.c b/memory.c
index acb8507cfb75..28f901f16adf 100644
--- a/memory.c
+++ b/memory.c
@@ -16075,6 +16075,8 @@ dump_swap_info(ulong swapflags, ulong *totalswap_pages, ulong *totalused_pages)
        char buf3[BUFSIZE];
        char buf4[BUFSIZE];
        char buf5[BUFSIZE];
+       int swap_file_is_file =
+               STREQ(MEMBER_TYPE_NAME("swap_info_struct", "swap_file"), "file");


This patch looks good, but I still have one question:
The 'swap_file' has been added into the struct swap_info_struct since linux v2.6.12-rc2, and until now it(its type) has been never changed, it indicates that the above checking is always true.

STREQ(MEMBER_TYPE_NAME("swap_info_struct", "swap_file"), "file")

Is that expected behavior? Just want to confirm with you.

Thanks
Lianbo
 
        if (!symbol_exists("nr_swapfiles"))
                error(FATAL, "nr_swapfiles doesn't exist in this kernel!\n");
@@ -16118,9 +16120,21 @@ dump_swap_info(ulong swapflags, ulong *totalswap_pages, ulong *totalused_pages)
                swap_file = ULONG(vt->swap_info_struct +
                        OFFSET(swap_info_struct_swap_file));

-                swap_device = INT(vt->swap_info_struct +
-                        OFFSET_OPTION(swap_info_struct_swap_device,
-                       swap_info_struct_old_block_size));
+               /* Linux 6.10 and later */
+               if (INVALID_MEMBER(swap_info_struct_swap_device) &&
+                   INVALID_MEMBER(swap_info_struct_old_block_size) &&
+                   swap_file_is_file) {
+                       ulong inode;
+                       ushort mode;
+                       readmem(swap_file + OFFSET(file_f_inode), KVADDR, &inode,
+                               sizeof(ulong), "swap_file.f_inode", FAULT_ON_ERROR);
+                       readmem(inode + OFFSET(inode_i_mode), KVADDR, &mode,
+                               sizeof(ushort), "inode.i_mode", FAULT_ON_ERROR);
+                       swap_device = S_ISBLK(mode);
+               } else
+                       swap_device = INT(vt->swap_info_struct +
+                               OFFSET_OPTION(swap_info_struct_swap_device,
+                               swap_info_struct_old_block_size));

                 pages = INT(vt->swap_info_struct +
                         OFFSET(swap_info_struct_pages));
@@ -16161,8 +16175,12 @@ dump_swap_info(ulong swapflags, ulong *totalswap_pages, ulong *totalused_pages)
                                        OFFSET(swap_info_struct_swap_vfsmnt));
                                get_pathname(swap_file, buf, BUFSIZE,
                                        1, vfsmnt);
-                       } else if (VALID_MEMBER
-                               (swap_info_struct_old_block_size)) {
+                       } else if (VALID_MEMBER(swap_info_struct_old_block_size) ||
+                                   swap_file_is_file) {
+                               /*
+                                * Linux 6.10 and later kernels do not have old_block_size,
+                                * but this still should work, if swap_file is file.
+                                */
                                devname = vfsmount_devname(file_to_vfsmnt(swap_file),
                                        buf1, BUFSIZE);
                                get_pathname(file_to_dentry(swap_file),
diff --git a/symbols.c b/symbols.c
index 283b98a8fbfe..0bf050ab62d0 100644
--- a/symbols.c
+++ b/symbols.c
@@ -10522,6 +10522,7 @@ dump_offset_table(char *spec, ulong makestruct)
                OFFSET(file_f_count));
         fprintf(fp, "                   file_f_path: %ld\n",
                OFFSET(file_f_path));
+        fprintf(fp, "                  file_f_inode: %ld\n", OFFSET(file_f_inode));
         fprintf(fp, "                      path_mnt: %ld\n",
                OFFSET(path_mnt));
         fprintf(fp, "                   path_dentry: %ld\n",
--
2.31.1