Thank you for the update, Kazu.
On Wed, Jul 12, 2023 at 4:55 PM <crash-utility-request@redhat.com> wrote:
Date: Wed, 12 Jul 2023 08:55:29 +0000
From: HAGIO KAZUHITO(?????)  <k-hagio-ab@nec.com>
To: "crash-utility@redhat.com" <crash-utility@redhat.com>
Cc: "lijiang@redhat.com" <lijiang@redhat.com>
Subject: [Crash-utility] [PATCH v2 2/2] Fix "irq [-a|-s]" options on
        Linux 6.5-rc1 and later
Message-ID: <1689152127-28057-1-git-send-email-k-hagio-ab@nec.com>
Content-Type: text/plain; charset="iso-2022-jp"

From: Kazuhito Hagio <k-hagio-ab@nec.com>

Kernel commit 721255b982 ("genirq: Use a maple tree for interrupt
descriptor management"), which is contained in Linux 6.5-rc1 and later
kernels, replaced irq_desc_tree with a maple tree sparse_irqs.

Without the patch, "irq [-a|-s]" options fail with an error, e.g. the
following on x86_64, on kernels configured with CONFIG_SPARSE_IRQ=y.

  crash> irq
  irq: x86_64_dump_irq: irq_desc[] or irq_desc_tree do not exist?

Signed-off-by: Kazuhito Hagio <k-hagio-ab@nec.com>
---
v1 -> v2
- Fixed a typo in the commit log: s/SPARSE_IRQS/SPARSE_IRQ/
- Reverted the unintendedly removed line in x86_64_show_interrupts()
- Added sparse_irqs support to ia64_dump_irq()


The v2 looks good to me. For the patches: Ack
[1] [PATCH 1/2] Exclude NULL entries from do_maple_tree() return value  
[2] [PATCH v2 2/2] Fix "irq [-a|-s]" options on Linux 6.5-rc1 and later  

Thanks.
Lianbo

 defs.h    |  2 ++
 ia64.c    |  3 ++-
 kernel.c  | 51 ++++++++++++++++++++++++++++++++++++++++++++++++---
 symbols.c |  1 +
 x86_64.c  |  9 ++++++---
 5 files changed, 59 insertions(+), 7 deletions(-)

diff --git a/defs.h b/defs.h
index 26afe232cc3e..358f365585cf 100644
--- a/defs.h
+++ b/defs.h
@@ -676,6 +676,7 @@ struct new_utsname {
 #define IRQ_DESC_TREE_XARRAY       (0x80ULL)
 #define KMOD_PAX                  (0x100ULL)
 #define KMOD_MEMORY               (0x200ULL)
+#define IRQ_DESC_TREE_MAPLE       (0x400ULL)

 #define XEN()       (kt->flags & ARCH_XEN)
 #define OPENVZ()    (kt->flags & ARCH_OPENVZ)
@@ -2222,6 +2223,7 @@ struct offset_table {                    /* stash of commonly-used offsets */
        long module_mem;
        long module_memory_base;
        long module_memory_size;
+       long irq_data_irq;
 };

 struct size_table {         /* stash of commonly-used sizes */
diff --git a/ia64.c b/ia64.c
index 2e1d15fe6042..d3e0a3b01869 100644
--- a/ia64.c
+++ b/ia64.c
@@ -791,7 +791,8 @@ ia64_back_trace_cmd(struct bt_info *bt)
 static void
 ia64_dump_irq(int irq)
 {
-        if (symbol_exists("irq_desc") || symbol_exists("_irq_desc") ||
+       if (kernel_symbol_exists("sparse_irqs") ||
+           symbol_exists("irq_desc") || symbol_exists("_irq_desc") ||
            kernel_symbol_exists("irq_desc_ptrs")) {
                 machdep->dump_irq = generic_dump_irq;
                 return(generic_dump_irq(irq));
diff --git a/kernel.c b/kernel.c
index 0fc77c19f12a..546eed95eebd 100644
--- a/kernel.c
+++ b/kernel.c
@@ -541,7 +541,10 @@ kernel_init()
        MEMBER_OFFSET_INIT(irqaction_dev_id, "irqaction", "dev_id");
        MEMBER_OFFSET_INIT(irqaction_next, "irqaction", "next");

-       if (kernel_symbol_exists("irq_desc_tree")) {
+       /* 6.5 and later: CONFIG_SPARSE_IRQ */
+       if (kernel_symbol_exists("sparse_irqs"))
+               kt->flags2 |= IRQ_DESC_TREE_MAPLE;
+       else if (kernel_symbol_exists("irq_desc_tree")) {
                get_symbol_type("irq_desc_tree", NULL, &req);
                if (STREQ(req.type_tag_name, "xarray")) {
                        kt->flags2 |= IRQ_DESC_TREE_XARRAY;
@@ -554,6 +557,7 @@ kernel_init()
        }
        STRUCT_SIZE_INIT(irq_data, "irq_data");
        if (VALID_STRUCT(irq_data)) {
+               MEMBER_OFFSET_INIT(irq_data_irq, "irq_data", "irq");
                MEMBER_OFFSET_INIT(irq_data_chip, "irq_data", "chip");
                MEMBER_OFFSET_INIT(irq_data_affinity, "irq_data", "affinity");
                MEMBER_OFFSET_INIT(irq_desc_irq_data, "irq_desc", "irq_data");
@@ -6180,6 +6184,8 @@ dump_kernel_table(int verbose)
                fprintf(fp, "%sIRQ_DESC_TREE_RADIX", others++ ? "|" : "");
        if (kt->flags2 & IRQ_DESC_TREE_XARRAY)
                fprintf(fp, "%sIRQ_DESC_TREE_XARRAY", others++ ? "|" : "");
+       if (kt->flags2 & IRQ_DESC_TREE_MAPLE)
+               fprintf(fp, "%sIRQ_DESC_TREE_MAPLE", others++ ? "|" : "");
        if (kt->flags2 & KMOD_PAX)
                fprintf(fp, "%sKMOD_PAX", others++ ? "|" : "");
        if (kt->flags2 & KMOD_MEMORY)
@@ -6652,6 +6658,45 @@ get_irq_desc_addr(int irq)
                readmem(ptr, KVADDR, &addr,
                         sizeof(void *), "irq_desc_ptrs entry",
                         FAULT_ON_ERROR);
+       } else if (kt->flags2 & IRQ_DESC_TREE_MAPLE) {
+               unsigned int i;
+
+               if (kt->highest_irq && (irq > kt->highest_irq))
+                       return addr;
+
+               cnt = do_maple_tree(symbol_value("sparse_irqs"), MAPLE_TREE_COUNT, NULL);
+
+               len = sizeof(struct list_pair) * (cnt+1);
+               lp = (struct list_pair *)GETBUF(len);
+               lp[0].index = cnt; /* maxcount */
+
+               cnt = do_maple_tree(symbol_value("sparse_irqs"), MAPLE_TREE_GATHER, lp);
+
+               /*
+                * NOTE: We cannot use lp.index like Radix Tree or XArray because
+                * it's not an absolute index and just counter in Maple Tree.
+                */
+               if (kt->highest_irq == 0) {
+                       readmem((ulong)lp[cnt-1].value +
+                                       OFFSET(irq_desc_irq_data) + OFFSET(irq_data_irq),
+                               KVADDR, &kt->highest_irq, sizeof(int), "irq_data.irq",
+                               FAULT_ON_ERROR);
+               }
+
+               for (c = 0; c < cnt; c++) {
+                       readmem((ulong)lp[c].value +
+                                       OFFSET(irq_desc_irq_data) + OFFSET(irq_data_irq),
+                               KVADDR, &i, sizeof(int), "irq_data.irq", FAULT_ON_ERROR);
+                       if (i == irq) {
+                               if (CRASHDEBUG(1))
+                                       fprintf(fp, "index: %d value: %lx\n",
+                                               i, (ulong)lp[c].value);
+                               addr = (ulong)lp[c].value;
+                               break;
+                       }
+               }
+               FREEBUF(lp);
+
        } else if (kt->flags2 & (IRQ_DESC_TREE_RADIX|IRQ_DESC_TREE_XARRAY)) {
                if (kt->highest_irq && (irq > kt->highest_irq))
                        return addr;
@@ -6700,8 +6745,8 @@ get_irq_desc_addr(int irq)
                FREEBUF(lp);
        } else {
                error(FATAL,
-                   "neither irq_desc, _irq_desc, irq_desc_ptrs "
-                   "or irq_desc_tree symbols exist\n");
+                   "neither irq_desc, _irq_desc, irq_desc_ptrs, "
+                   "irq_desc_tree or sparse_irqs symbols exist\n");
        }

        return addr;
diff --git a/symbols.c b/symbols.c
index 82529a6785c9..876be7aea90e 100644
--- a/symbols.c
+++ b/symbols.c
@@ -10375,6 +10375,7 @@ dump_offset_table(char *spec, ulong makestruct)
                OFFSET(irq_desc_t_kstat_irqs));
        fprintf(fp, "           irq_desc_t_affinity: %ld\n",
                OFFSET(irq_desc_t_affinity));
+       fprintf(fp, "                  irq_data_irq: %ld\n", OFFSET(irq_data_irq));
        fprintf(fp, "                 irq_data_chip: %ld\n",
                OFFSET(irq_data_chip));
        fprintf(fp, "             irq_data_affinity: %ld\n",
diff --git a/x86_64.c b/x86_64.c
index 87e87ae6e1e8..42ade4817ad9 100644
--- a/x86_64.c
+++ b/x86_64.c
@@ -5391,7 +5391,8 @@ get_x86_64_frame(struct bt_info *bt, ulong *getpc, ulong *getsp)
 static void
 x86_64_dump_irq(int irq)
 {
-        if (symbol_exists("irq_desc") ||
+       if (kernel_symbol_exists("sparse_irqs") ||
+           symbol_exists("irq_desc") ||
            kernel_symbol_exists("irq_desc_ptrs") ||
            kernel_symbol_exists("irq_desc_tree")) {
                 machdep->dump_irq = generic_dump_irq;
@@ -5405,7 +5406,8 @@ x86_64_dump_irq(int irq)
 static void
 x86_64_get_irq_affinity(int irq)
 {
-        if (symbol_exists("irq_desc") ||
+       if (kernel_symbol_exists("sparse_irqs") ||
+           symbol_exists("irq_desc") ||
            kernel_symbol_exists("irq_desc_ptrs") ||
            kernel_symbol_exists("irq_desc_tree")) {
                 machdep->get_irq_affinity = generic_get_irq_affinity;
@@ -5419,7 +5421,8 @@ x86_64_get_irq_affinity(int irq)
 static void
 x86_64_show_interrupts(int irq, ulong *cpus)
 {
-        if (symbol_exists("irq_desc") ||
+       if (kernel_symbol_exists("sparse_irqs") ||
+           symbol_exists("irq_desc") ||
            kernel_symbol_exists("irq_desc_ptrs") ||
            kernel_symbol_exists("irq_desc_tree")) {
                 machdep->show_interrupts = generic_show_interrupts;
--
2.31.1