Commit a02b0cde authored by Florian Fainelli's avatar Florian Fainelli Committed by Andrew Morton
Browse files

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

In commit 721255b9 ("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: 721255b9 ("genirq: Use a maple tree for interrupt descriptor management")
Signed-off-by: default avatarFlorian 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: default avatarAndrew Morton <akpm@linux-foundation.org>
parent ea9b77f9
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@
#include <linux/of_fdt.h>
#include <linux/page_ext.h>
#include <linux/radix-tree.h>
#include <linux/maple_tree.h>
#include <linux/slab.h>
#include <linux/threads.h>
#include <linux/vmalloc.h>
@@ -93,6 +94,12 @@ LX_GDBPARSED(RADIX_TREE_MAP_SIZE)
LX_GDBPARSED(RADIX_TREE_MAP_SHIFT)
LX_GDBPARSED(RADIX_TREE_MAP_MASK)

/* linux/maple_tree.h */
LX_VALUE(MAPLE_NODE_SLOTS)
LX_VALUE(MAPLE_RANGE64_SLOTS)
LX_VALUE(MAPLE_ARANGE64_SLOTS)
LX_GDBPARSED(MAPLE_NODE_MASK)

/* linux/vmalloc.h */
LX_VALUE(VM_IOREMAP)
LX_VALUE(VM_ALLOC)
+6 −6
Original line number Diff line number Diff line
@@ -7,7 +7,7 @@ import gdb
from linux import constants
from linux import cpus
from linux import utils
from linux import radixtree
from linux import mapletree

irq_desc_type = utils.CachedType("struct irq_desc")

@@ -23,12 +23,12 @@ def irqd_is_level(desc):
def show_irq_desc(prec, irq):
    text = ""

    desc = radixtree.lookup(gdb.parse_and_eval("&irq_desc_tree"), irq)
    desc = mapletree.mtree_load(gdb.parse_and_eval("&sparse_irqs"), irq)
    if desc is None:
        return text

    desc = desc.cast(irq_desc_type.get_type())
    if desc is None:
    desc = desc.cast(irq_desc_type.get_type().pointer())
    if desc == 0:
        return text

    if irq_settings_is_hidden(desc):
@@ -221,8 +221,8 @@ class LxInterruptList(gdb.Command):
            gdb.write("CPU%-8d" % cpu)
        gdb.write("\n")

        if utils.gdb_eval_or_none("&irq_desc_tree") is None:
            return
        if utils.gdb_eval_or_none("&sparse_irqs") is None:
            raise gdb.GdbError("Unable to find the sparse IRQ tree, is CONFIG_SPARSE_IRQ enabled?")

        for irq in range(nr_irqs):
            gdb.write(show_irq_desc(prec, irq))
+252 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0
#
#  Maple tree helpers
#
# Copyright (c) 2025 Broadcom
#
# Authors:
#  Florian Fainelli <florian.fainelli@broadcom.com>

import gdb

from linux import utils
from linux import constants
from linux import xarray

maple_tree_root_type = utils.CachedType("struct maple_tree")
maple_node_type = utils.CachedType("struct maple_node")
maple_enode_type = utils.CachedType("void")

maple_dense = 0
maple_leaf_64 = 1
maple_range_64 = 2
maple_arange_64 = 3

class Mas(object):
    ma_active = 0
    ma_start = 1
    ma_root = 2
    ma_none = 3
    ma_pause = 4
    ma_overflow = 5
    ma_underflow = 6
    ma_error = 7

    def __init__(self, mt, first, end):
        if mt.type == maple_tree_root_type.get_type().pointer():
            self.tree = mt.dereference()
        elif mt.type != maple_tree_root_type.get_type():
            raise gdb.GdbError("must be {} not {}"
                               .format(maple_tree_root_type.get_type().pointer(), mt.type))
        self.tree = mt
        self.index = first
        self.last = end
        self.node = None
        self.status = self.ma_start
        self.min = 0
        self.max = -1

    def is_start(self):
        # mas_is_start()
        return self.status == self.ma_start

    def is_ptr(self):
        # mas_is_ptr()
        return self.status == self.ma_root

    def is_none(self):
        # mas_is_none()
        return self.status == self.ma_none

    def root(self):
        # mas_root()
        return self.tree['ma_root'].cast(maple_enode_type.get_type().pointer())

    def start(self):
        # mas_start()
        if self.is_start() is False:
            return None

        self.min = 0
        self.max = ~0

        while True:
            self.depth = 0
            root = self.root()
            if xarray.xa_is_node(root):
                self.depth = 0
                self.status = self.ma_active
                self.node = mte_safe_root(root)
                self.offset = 0
                if mte_dead_node(self.node) is True:
                    continue

                return None

            self.node = None
            # Empty tree
            if root is None:
                self.status = self.ma_none
                self.offset = constants.LX_MAPLE_NODE_SLOTS
                return None

            # Single entry tree
            self.status = self.ma_root
            self.offset = constants.LX_MAPLE_NODE_SLOTS

            if self.index != 0:
                return None

            return root

        return None

    def reset(self):
        # mas_reset()
        self.status = self.ma_start
        self.node = None

def mte_safe_root(node):
    if node.type != maple_enode_type.get_type().pointer():
        raise gdb.GdbError("{} must be {} not {}"
                           .format(mte_safe_root.__name__, maple_enode_type.get_type().pointer(), node.type))
    ulong_type = utils.get_ulong_type()
    indirect_ptr = node.cast(ulong_type) & ~0x2
    val = indirect_ptr.cast(maple_enode_type.get_type().pointer())
    return val

def mte_node_type(entry):
    ulong_type = utils.get_ulong_type()
    val = None
    if entry.type == maple_enode_type.get_type().pointer():
        val = entry.cast(ulong_type)
    elif entry.type == ulong_type:
        val = entry
    else:
        raise gdb.GdbError("{} must be {} not {}"
                           .format(mte_node_type.__name__, maple_enode_type.get_type().pointer(), entry.type))
    return (val >> 0x3) & 0xf

def ma_dead_node(node):
    if node.type != maple_node_type.get_type().pointer():
        raise gdb.GdbError("{} must be {} not {}"
                           .format(ma_dead_node.__name__, maple_node_type.get_type().pointer(), node.type))
    ulong_type = utils.get_ulong_type()
    parent = node['parent']
    indirect_ptr = node['parent'].cast(ulong_type) & ~constants.LX_MAPLE_NODE_MASK
    return indirect_ptr == node

def mte_to_node(enode):
    ulong_type = utils.get_ulong_type()
    if enode.type == maple_enode_type.get_type().pointer():
        indirect_ptr = enode.cast(ulong_type)
    elif enode.type == ulong_type:
        indirect_ptr = enode
    else:
        raise gdb.GdbError("{} must be {} not {}"
                           .format(mte_to_node.__name__, maple_enode_type.get_type().pointer(), enode.type))
    indirect_ptr = indirect_ptr & ~constants.LX_MAPLE_NODE_MASK
    return indirect_ptr.cast(maple_node_type.get_type().pointer())

def mte_dead_node(enode):
    if enode.type != maple_enode_type.get_type().pointer():
        raise gdb.GdbError("{} must be {} not {}"
                           .format(mte_dead_node.__name__, maple_enode_type.get_type().pointer(), enode.type))
    node = mte_to_node(enode)
    return ma_dead_node(node)

def ma_is_leaf(tp):
    result = tp < maple_range_64
    return tp < maple_range_64

def mt_pivots(t):
    if t == maple_dense:
        return 0
    elif t == maple_leaf_64 or t == maple_range_64:
        return constants.LX_MAPLE_RANGE64_SLOTS - 1
    elif t == maple_arange_64:
        return constants.LX_MAPLE_ARANGE64_SLOTS - 1

def ma_pivots(node, t):
    if node.type != maple_node_type.get_type().pointer():
        raise gdb.GdbError("{}: must be {} not {}"
                           .format(ma_pivots.__name__, maple_node_type.get_type().pointer(), node.type))
    if t == maple_arange_64:
        return node['ma64']['pivot']
    elif t == maple_leaf_64 or t == maple_range_64:
        return node['mr64']['pivot']
    else:
        return None

def ma_slots(node, tp):
    if node.type != maple_node_type.get_type().pointer():
        raise gdb.GdbError("{}: must be {} not {}"
                           .format(ma_slots.__name__, maple_node_type.get_type().pointer(), node.type))
    if tp == maple_arange_64:
        return node['ma64']['slot']
    elif tp == maple_range_64 or tp == maple_leaf_64:
        return node['mr64']['slot']
    elif tp == maple_dense:
        return node['slot']
    else:
        return None

def mt_slot(mt, slots, offset):
    ulong_type = utils.get_ulong_type()
    return slots[offset].cast(ulong_type)

def mtree_lookup_walk(mas):
    ulong_type = utils.get_ulong_type()
    n = mas.node

    while True:
        node = mte_to_node(n)
        tp = mte_node_type(n)
        pivots = ma_pivots(node, tp)
        end = mt_pivots(tp)
        offset = 0
        while True:
            if pivots[offset] >= mas.index:
                break
            if offset >= end:
                break
            offset += 1

        slots = ma_slots(node, tp)
        n = mt_slot(mas.tree, slots, offset)
        if ma_dead_node(node) is True:
            mas.reset()
            return None
            break

        if ma_is_leaf(tp) is True:
            break

    return n

def mtree_load(mt, index):
    ulong_type = utils.get_ulong_type()
    # MT_STATE(...)
    mas = Mas(mt, index, index)
    entry = None

    while True:
        entry = mas.start()
        if mas.is_none():
            return None

        if mas.is_ptr():
            if index != 0:
                entry = None
            return entry

        entry = mtree_lookup_walk(mas)
        if entry is None and mas.is_start():
            continue
        else:
            break

    if xarray.xa_is_zero(entry):
        return None

    return entry
+28 −0
Original line number Diff line number Diff line
# SPDX-License-Identifier: GPL-2.0
#
#  Xarray helpers
#
# Copyright (c) 2025 Broadcom
#
# Authors:
#  Florian Fainelli <florian.fainelli@broadcom.com>

import gdb

from linux import utils
from linux import constants

def xa_is_internal(entry):
    ulong_type = utils.get_ulong_type()
    return ((entry.cast(ulong_type) & 3) == 2)

def xa_mk_internal(v):
    return ((v << 2) | 2)

def xa_is_zero(entry):
    ulong_type = utils.get_ulong_type()
    return entry.cast(ulong_type) == xa_mk_internal(257)

def xa_is_node(entry):
    ulong_type = utils.get_ulong_type()
    return xa_is_internal(entry) and (entry.cast(ulong_type) > 4096)