Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

scripts/gdb: fix interrupts.py after maple tree conversion

In commit 721255b9826b ("genirq: Use a maple tree for interrupt descriptor
management"), the irq_desc_tree was replaced with a sparse_irqs tree using
a maple tree structure. Since the script looked for the irq_desc_tree
symbol which is no longer available, no interrupts would be printed and
the script output would not be useful anymore.

In addition to looking up the correct symbol (sparse_irqs), a new module
(mapletree.py) is added whose mtree_load() implementation is largely
copied after the C version and uses the same variable and intermediate
function names wherever possible to ensure that both the C and Python
version be updated in the future.

This restores the scripts' output to match that of /proc/interrupts.

Link: https://lkml.kernel.org/r/20250625021020.1056930-1-florian.fainelli@broadcom.com
Fixes: 721255b9826b ("genirq: Use a maple tree for interrupt descriptor management")
Signed-off-by: Florian Fainelli <florian.fainelli@broadcom.com>
Cc: Jan Kiszka <jan.kiszka@siemens.com>
Cc: Kieran Bingham <kbingham@kernel.org>
Cc: Shanker Donthineni <sdonthineni@nvidia.com>
Cc: Thomas Gleinxer <tglx@linutronix.de>
Cc: <stable@vger.kernel.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>

authored by

Florian Fainelli and committed by
Andrew Morton
a02b0cde ea9b77f9

+293 -6
+7
scripts/gdb/linux/constants.py.in
··· 20 20 #include <linux/of_fdt.h> 21 21 #include <linux/page_ext.h> 22 22 #include <linux/radix-tree.h> 23 + #include <linux/maple_tree.h> 23 24 #include <linux/slab.h> 24 25 #include <linux/threads.h> 25 26 #include <linux/vmalloc.h> ··· 93 92 LX_GDBPARSED(RADIX_TREE_MAP_SIZE) 94 93 LX_GDBPARSED(RADIX_TREE_MAP_SHIFT) 95 94 LX_GDBPARSED(RADIX_TREE_MAP_MASK) 95 + 96 + /* linux/maple_tree.h */ 97 + LX_VALUE(MAPLE_NODE_SLOTS) 98 + LX_VALUE(MAPLE_RANGE64_SLOTS) 99 + LX_VALUE(MAPLE_ARANGE64_SLOTS) 100 + LX_GDBPARSED(MAPLE_NODE_MASK) 96 101 97 102 /* linux/vmalloc.h */ 98 103 LX_VALUE(VM_IOREMAP)
+6 -6
scripts/gdb/linux/interrupts.py
··· 7 7 from linux import constants 8 8 from linux import cpus 9 9 from linux import utils 10 - from linux import radixtree 10 + from linux import mapletree 11 11 12 12 irq_desc_type = utils.CachedType("struct irq_desc") 13 13 ··· 23 23 def show_irq_desc(prec, irq): 24 24 text = "" 25 25 26 - desc = radixtree.lookup(gdb.parse_and_eval("&irq_desc_tree"), irq) 26 + desc = mapletree.mtree_load(gdb.parse_and_eval("&sparse_irqs"), irq) 27 27 if desc is None: 28 28 return text 29 29 30 - desc = desc.cast(irq_desc_type.get_type()) 31 - if desc is None: 30 + desc = desc.cast(irq_desc_type.get_type().pointer()) 31 + if desc == 0: 32 32 return text 33 33 34 34 if irq_settings_is_hidden(desc): ··· 221 221 gdb.write("CPU%-8d" % cpu) 222 222 gdb.write("\n") 223 223 224 - if utils.gdb_eval_or_none("&irq_desc_tree") is None: 225 - return 224 + if utils.gdb_eval_or_none("&sparse_irqs") is None: 225 + raise gdb.GdbError("Unable to find the sparse IRQ tree, is CONFIG_SPARSE_IRQ enabled?") 226 226 227 227 for irq in range(nr_irqs): 228 228 gdb.write(show_irq_desc(prec, irq))
+252
scripts/gdb/linux/mapletree.py
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + # 3 + # Maple tree helpers 4 + # 5 + # Copyright (c) 2025 Broadcom 6 + # 7 + # Authors: 8 + # Florian Fainelli <florian.fainelli@broadcom.com> 9 + 10 + import gdb 11 + 12 + from linux import utils 13 + from linux import constants 14 + from linux import xarray 15 + 16 + maple_tree_root_type = utils.CachedType("struct maple_tree") 17 + maple_node_type = utils.CachedType("struct maple_node") 18 + maple_enode_type = utils.CachedType("void") 19 + 20 + maple_dense = 0 21 + maple_leaf_64 = 1 22 + maple_range_64 = 2 23 + maple_arange_64 = 3 24 + 25 + class Mas(object): 26 + ma_active = 0 27 + ma_start = 1 28 + ma_root = 2 29 + ma_none = 3 30 + ma_pause = 4 31 + ma_overflow = 5 32 + ma_underflow = 6 33 + ma_error = 7 34 + 35 + def __init__(self, mt, first, end): 36 + if mt.type == maple_tree_root_type.get_type().pointer(): 37 + self.tree = mt.dereference() 38 + elif mt.type != maple_tree_root_type.get_type(): 39 + raise gdb.GdbError("must be {} not {}" 40 + .format(maple_tree_root_type.get_type().pointer(), mt.type)) 41 + self.tree = mt 42 + self.index = first 43 + self.last = end 44 + self.node = None 45 + self.status = self.ma_start 46 + self.min = 0 47 + self.max = -1 48 + 49 + def is_start(self): 50 + # mas_is_start() 51 + return self.status == self.ma_start 52 + 53 + def is_ptr(self): 54 + # mas_is_ptr() 55 + return self.status == self.ma_root 56 + 57 + def is_none(self): 58 + # mas_is_none() 59 + return self.status == self.ma_none 60 + 61 + def root(self): 62 + # mas_root() 63 + return self.tree['ma_root'].cast(maple_enode_type.get_type().pointer()) 64 + 65 + def start(self): 66 + # mas_start() 67 + if self.is_start() is False: 68 + return None 69 + 70 + self.min = 0 71 + self.max = ~0 72 + 73 + while True: 74 + self.depth = 0 75 + root = self.root() 76 + if xarray.xa_is_node(root): 77 + self.depth = 0 78 + self.status = self.ma_active 79 + self.node = mte_safe_root(root) 80 + self.offset = 0 81 + if mte_dead_node(self.node) is True: 82 + continue 83 + 84 + return None 85 + 86 + self.node = None 87 + # Empty tree 88 + if root is None: 89 + self.status = self.ma_none 90 + self.offset = constants.LX_MAPLE_NODE_SLOTS 91 + return None 92 + 93 + # Single entry tree 94 + self.status = self.ma_root 95 + self.offset = constants.LX_MAPLE_NODE_SLOTS 96 + 97 + if self.index != 0: 98 + return None 99 + 100 + return root 101 + 102 + return None 103 + 104 + def reset(self): 105 + # mas_reset() 106 + self.status = self.ma_start 107 + self.node = None 108 + 109 + def mte_safe_root(node): 110 + if node.type != maple_enode_type.get_type().pointer(): 111 + raise gdb.GdbError("{} must be {} not {}" 112 + .format(mte_safe_root.__name__, maple_enode_type.get_type().pointer(), node.type)) 113 + ulong_type = utils.get_ulong_type() 114 + indirect_ptr = node.cast(ulong_type) & ~0x2 115 + val = indirect_ptr.cast(maple_enode_type.get_type().pointer()) 116 + return val 117 + 118 + def mte_node_type(entry): 119 + ulong_type = utils.get_ulong_type() 120 + val = None 121 + if entry.type == maple_enode_type.get_type().pointer(): 122 + val = entry.cast(ulong_type) 123 + elif entry.type == ulong_type: 124 + val = entry 125 + else: 126 + raise gdb.GdbError("{} must be {} not {}" 127 + .format(mte_node_type.__name__, maple_enode_type.get_type().pointer(), entry.type)) 128 + return (val >> 0x3) & 0xf 129 + 130 + def ma_dead_node(node): 131 + if node.type != maple_node_type.get_type().pointer(): 132 + raise gdb.GdbError("{} must be {} not {}" 133 + .format(ma_dead_node.__name__, maple_node_type.get_type().pointer(), node.type)) 134 + ulong_type = utils.get_ulong_type() 135 + parent = node['parent'] 136 + indirect_ptr = node['parent'].cast(ulong_type) & ~constants.LX_MAPLE_NODE_MASK 137 + return indirect_ptr == node 138 + 139 + def mte_to_node(enode): 140 + ulong_type = utils.get_ulong_type() 141 + if enode.type == maple_enode_type.get_type().pointer(): 142 + indirect_ptr = enode.cast(ulong_type) 143 + elif enode.type == ulong_type: 144 + indirect_ptr = enode 145 + else: 146 + raise gdb.GdbError("{} must be {} not {}" 147 + .format(mte_to_node.__name__, maple_enode_type.get_type().pointer(), enode.type)) 148 + indirect_ptr = indirect_ptr & ~constants.LX_MAPLE_NODE_MASK 149 + return indirect_ptr.cast(maple_node_type.get_type().pointer()) 150 + 151 + def mte_dead_node(enode): 152 + if enode.type != maple_enode_type.get_type().pointer(): 153 + raise gdb.GdbError("{} must be {} not {}" 154 + .format(mte_dead_node.__name__, maple_enode_type.get_type().pointer(), enode.type)) 155 + node = mte_to_node(enode) 156 + return ma_dead_node(node) 157 + 158 + def ma_is_leaf(tp): 159 + result = tp < maple_range_64 160 + return tp < maple_range_64 161 + 162 + def mt_pivots(t): 163 + if t == maple_dense: 164 + return 0 165 + elif t == maple_leaf_64 or t == maple_range_64: 166 + return constants.LX_MAPLE_RANGE64_SLOTS - 1 167 + elif t == maple_arange_64: 168 + return constants.LX_MAPLE_ARANGE64_SLOTS - 1 169 + 170 + def ma_pivots(node, t): 171 + if node.type != maple_node_type.get_type().pointer(): 172 + raise gdb.GdbError("{}: must be {} not {}" 173 + .format(ma_pivots.__name__, maple_node_type.get_type().pointer(), node.type)) 174 + if t == maple_arange_64: 175 + return node['ma64']['pivot'] 176 + elif t == maple_leaf_64 or t == maple_range_64: 177 + return node['mr64']['pivot'] 178 + else: 179 + return None 180 + 181 + def ma_slots(node, tp): 182 + if node.type != maple_node_type.get_type().pointer(): 183 + raise gdb.GdbError("{}: must be {} not {}" 184 + .format(ma_slots.__name__, maple_node_type.get_type().pointer(), node.type)) 185 + if tp == maple_arange_64: 186 + return node['ma64']['slot'] 187 + elif tp == maple_range_64 or tp == maple_leaf_64: 188 + return node['mr64']['slot'] 189 + elif tp == maple_dense: 190 + return node['slot'] 191 + else: 192 + return None 193 + 194 + def mt_slot(mt, slots, offset): 195 + ulong_type = utils.get_ulong_type() 196 + return slots[offset].cast(ulong_type) 197 + 198 + def mtree_lookup_walk(mas): 199 + ulong_type = utils.get_ulong_type() 200 + n = mas.node 201 + 202 + while True: 203 + node = mte_to_node(n) 204 + tp = mte_node_type(n) 205 + pivots = ma_pivots(node, tp) 206 + end = mt_pivots(tp) 207 + offset = 0 208 + while True: 209 + if pivots[offset] >= mas.index: 210 + break 211 + if offset >= end: 212 + break 213 + offset += 1 214 + 215 + slots = ma_slots(node, tp) 216 + n = mt_slot(mas.tree, slots, offset) 217 + if ma_dead_node(node) is True: 218 + mas.reset() 219 + return None 220 + break 221 + 222 + if ma_is_leaf(tp) is True: 223 + break 224 + 225 + return n 226 + 227 + def mtree_load(mt, index): 228 + ulong_type = utils.get_ulong_type() 229 + # MT_STATE(...) 230 + mas = Mas(mt, index, index) 231 + entry = None 232 + 233 + while True: 234 + entry = mas.start() 235 + if mas.is_none(): 236 + return None 237 + 238 + if mas.is_ptr(): 239 + if index != 0: 240 + entry = None 241 + return entry 242 + 243 + entry = mtree_lookup_walk(mas) 244 + if entry is None and mas.is_start(): 245 + continue 246 + else: 247 + break 248 + 249 + if xarray.xa_is_zero(entry): 250 + return None 251 + 252 + return entry
+28
scripts/gdb/linux/xarray.py
··· 1 + # SPDX-License-Identifier: GPL-2.0 2 + # 3 + # Xarray helpers 4 + # 5 + # Copyright (c) 2025 Broadcom 6 + # 7 + # Authors: 8 + # Florian Fainelli <florian.fainelli@broadcom.com> 9 + 10 + import gdb 11 + 12 + from linux import utils 13 + from linux import constants 14 + 15 + def xa_is_internal(entry): 16 + ulong_type = utils.get_ulong_type() 17 + return ((entry.cast(ulong_type) & 3) == 2) 18 + 19 + def xa_mk_internal(v): 20 + return ((v << 2) | 2) 21 + 22 + def xa_is_zero(entry): 23 + ulong_type = utils.get_ulong_type() 24 + return entry.cast(ulong_type) == xa_mk_internal(257) 25 + 26 + def xa_is_node(entry): 27 + ulong_type = utils.get_ulong_type() 28 + return xa_is_internal(entry) and (entry.cast(ulong_type) > 4096)