radixtree.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. # SPDX-License-Identifier: GPL-2.0
  2. #
  3. # Radix Tree Parser
  4. #
  5. # Copyright (c) 2016 Linaro Ltd
  6. # Copyright (c) 2023 Broadcom
  7. #
  8. # Authors:
  9. # Kieran Bingham <kieran.bingham@linaro.org>
  10. # Florian Fainelli <f.fainelli@gmail.com>
  11. import gdb
  12. from linux import utils
  13. from linux import constants
  14. radix_tree_root_type = utils.CachedType("struct xarray")
  15. radix_tree_node_type = utils.CachedType("struct xa_node")
  16. def is_internal_node(node):
  17. long_type = utils.get_long_type()
  18. return ((node.cast(long_type) & constants.LX_RADIX_TREE_ENTRY_MASK) == constants.LX_RADIX_TREE_INTERNAL_NODE)
  19. def entry_to_node(node):
  20. long_type = utils.get_long_type()
  21. node_type = node.type
  22. indirect_ptr = node.cast(long_type) & ~constants.LX_RADIX_TREE_INTERNAL_NODE
  23. return indirect_ptr.cast(radix_tree_node_type.get_type().pointer())
  24. def node_maxindex(node):
  25. return (constants.LX_RADIX_TREE_MAP_SIZE << node['shift']) - 1
  26. def resolve_root(root):
  27. if root.type == radix_tree_root_type.get_type():
  28. return root
  29. if root.type == radix_tree_root_type.get_type().pointer():
  30. return root.dereference()
  31. raise gdb.GdbError("must be {} not {}"
  32. .format(radix_tree_root_type.get_type(), root.type))
  33. def lookup(root, index):
  34. root = resolve_root(root)
  35. node = root['xa_head']
  36. if node == 0:
  37. return None
  38. if not (is_internal_node(node)):
  39. if (index > 0):
  40. return None
  41. return node
  42. node = entry_to_node(node)
  43. maxindex = node_maxindex(node)
  44. if (index > maxindex):
  45. return None
  46. shift = node['shift'] + constants.LX_RADIX_TREE_MAP_SHIFT
  47. while True:
  48. offset = (index >> node['shift']) & constants.LX_RADIX_TREE_MAP_MASK
  49. slot = node['slots'][offset]
  50. if slot == 0:
  51. return None
  52. node = slot.cast(node.type.pointer()).dereference()
  53. if node == 0:
  54. return None
  55. shift -= constants.LX_RADIX_TREE_MAP_SHIFT
  56. if (shift <= 0):
  57. break
  58. return node
  59. def descend(parent, index):
  60. offset = (index >> int(parent["shift"])) & constants.LX_RADIX_TREE_MAP_MASK
  61. return offset, parent["slots"][offset]
  62. def load_root(root):
  63. node = root["xa_head"]
  64. nodep = node
  65. if is_internal_node(node):
  66. node = entry_to_node(node)
  67. maxindex = node_maxindex(node)
  68. return int(node["shift"]) + constants.LX_RADIX_TREE_MAP_SHIFT, \
  69. nodep, maxindex
  70. return 0, nodep, 0
  71. class RadixTreeIter:
  72. def __init__(self, start):
  73. self.index = 0
  74. self.next_index = start
  75. self.node = None
  76. def xa_mk_internal(v):
  77. return (v << 2) | 2
  78. LX_XA_RETRY_ENTRY = xa_mk_internal(256)
  79. LX_RADIX_TREE_RETRY = LX_XA_RETRY_ENTRY
  80. def next_chunk(root, iter):
  81. mask = (1 << (utils.get_ulong_type().sizeof * 8)) - 1
  82. index = iter.next_index
  83. if index == 0 and iter.index != 0:
  84. return None
  85. restart = True
  86. while restart:
  87. restart = False
  88. _, child, maxindex = load_root(root)
  89. if index > maxindex:
  90. return None
  91. if not child:
  92. return None
  93. if not is_internal_node(child):
  94. iter.index = index
  95. iter.next_index = (maxindex + 1) & mask
  96. iter.node = None
  97. return root["xa_head"].address
  98. while True:
  99. node = entry_to_node(child)
  100. offset, child = descend(node, index)
  101. if not child:
  102. while True:
  103. offset += 1
  104. if offset >= constants.LX_RADIX_TREE_MAP_SIZE:
  105. break
  106. slot = node["slots"][offset]
  107. if slot:
  108. break
  109. index &= ~node_maxindex(node)
  110. index = (index + (offset << int(node["shift"]))) & mask
  111. if index == 0:
  112. return None
  113. if offset == constants.LX_RADIX_TREE_MAP_SIZE:
  114. restart = True
  115. break
  116. child = node["slots"][offset]
  117. if not child:
  118. restart = True
  119. break
  120. if child == LX_XA_RETRY_ENTRY:
  121. break
  122. if not node["shift"] or not is_internal_node(child):
  123. break
  124. iter.index = (index & ~node_maxindex(node)) | offset
  125. iter.next_index = ((index | node_maxindex(node)) + 1) & mask
  126. iter.node = node
  127. return node["slots"][offset].address
  128. def next_slot(slot, iter):
  129. mask = (1 << (utils.get_ulong_type().sizeof * 8)) - 1
  130. for _ in range(iter.next_index - iter.index - 1):
  131. slot += 1
  132. iter.index = (iter.index + 1) & mask
  133. if slot.dereference():
  134. return slot
  135. return None
  136. def for_each_slot(root, start=0):
  137. iter = RadixTreeIter(start)
  138. slot = None
  139. while True:
  140. if not slot:
  141. slot = next_chunk(root, iter)
  142. if not slot:
  143. break
  144. yield iter.index, slot
  145. slot = next_slot(slot, iter)
  146. class LxRadixTreeLookup(gdb.Function):
  147. """ Lookup and return a node from a RadixTree.
  148. $lx_radix_tree_lookup(root_node [, index]): Return the node at the given index.
  149. If index is omitted, the root node is dereference and returned."""
  150. def __init__(self):
  151. super(LxRadixTreeLookup, self).__init__("lx_radix_tree_lookup")
  152. def invoke(self, root, index=0):
  153. result = lookup(root, index)
  154. if result is None:
  155. raise gdb.GdbError("No entry in tree at index {}".format(index))
  156. return result
  157. class LxRadixTree(gdb.Command):
  158. """Show all values stored in a RadixTree."""
  159. def __init__(self):
  160. super(LxRadixTree, self).__init__("lx-radix-tree", gdb.COMMAND_DATA,
  161. gdb.COMPLETE_NONE)
  162. def invoke(self, argument, from_tty):
  163. args = gdb.string_to_argv(argument)
  164. if len(args) != 1:
  165. raise gdb.GdbError("Usage: lx-radix-tree ROOT")
  166. root = gdb.parse_and_eval(args[0])
  167. for index, slot in for_each_slot(root):
  168. gdb.write("[{}] = {}\n".format(index, slot.dereference()))
  169. LxRadixTree()
  170. LxRadixTreeLookup()