MCPcopy
hub / github.com/tinygrad/tinygrad / PageTableTraverseContext

Class PageTableTraverseContext

tinygrad/runtime/support/memory.py:115–170  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

113class VirtMapping: va_addr:int; size:int; paddrs:list[tuple[int, int]]; aspace:AddrSpace; uncached:bool=False; snooped:bool=False # noqa: E702
114
115class PageTableTraverseContext:
116 def __init__(self, dev, pt, vaddr, create_pts=False, free_pts=False, inspect=False, boot=False):
117 self.dev, self.vaddr, self.create_pts, self.free_pts, self.inspect, self.boot = dev, vaddr - dev.mm.va_base, create_pts, free_pts, inspect, boot
118 self.pt_stack:list[tuple[Any, int, int]] = [(pt, self._pt_pte_idx(pt, self.vaddr), self._pt_pte_size(pt))]
119
120 def _pt_pte_cnt(self, lv): return self.dev.mm.pte_cnt[lv]
121 def _pt_pte_size(self, pt): return self.dev.mm.pte_covers[pt.lv]
122 def _pt_pte_idx(self, pt, va): return (va // self._pt_pte_size(pt)) % self._pt_pte_cnt(pt.lv)
123
124 def level_down(self):
125 pt, pte_idx, _ = self.pt_stack[-1]
126
127 if not pt.valid(pte_idx):
128 assert self.create_pts, "Not allowed to create new page table"
129 pt.set_entry(pte_idx, self.dev.mm.palloc(0x1000, zero=True, boot=self.boot, ptable=True), table=True, valid=True)
130
131 assert not pt.is_page(pte_idx), f"Must be table pt={pt.paddr:#x}, {pt.lv=} {pte_idx=} {pt.entry(pte_idx)=:#x}"
132 child_page_table = self.dev.mm.pt_t(self.dev, pt.address(pte_idx), lv=pt.lv+1)
133
134 self.pt_stack.append((child_page_table, self._pt_pte_idx(child_page_table, self.vaddr), self._pt_pte_size(child_page_table)))
135 return self.pt_stack[-1]
136
137 def _try_free_pt(self) -> bool:
138 pt, _, _ = self.pt_stack[-1]
139 if self.free_pts and pt != self.dev.mm.root_page_table and all(not pt.valid(i) for i in range(self._pt_pte_cnt(self.pt_stack[-1][0].lv))):
140 self.dev.mm.pfree(pt.paddr, ptable=True)
141 parent_pt, parent_pte_idx, _ = self.pt_stack[-2]
142 parent_pt.set_entry(parent_pte_idx, 0x0, valid=False)
143 return True
144 return False
145
146 def level_up(self):
147 while self._try_free_pt() or self.pt_stack[-1][1] == self._pt_pte_cnt(self.pt_stack[-1][0].lv):
148 pt, pt_cnt, _ = self.pt_stack.pop()
149 if pt_cnt == self._pt_pte_cnt(pt.lv): self.pt_stack[-1] = (self.pt_stack[-1][0], self.pt_stack[-1][1] + 1, self.pt_stack[-1][2])
150
151 def next(self, size:int, paddr:int|None=None, off:int=0):
152 while size > 0:
153 pt, pte_idx, pte_covers = self.pt_stack[-1]
154
155 # create_pts goes down until the page covers the request.
156 # free_pts goes down to the table, it assumses all entries are valid on the range (and validates that)
157 # inspect just visits any valid ranges and yields them.
158 if self.create_pts:
159 assert paddr is not None, "paddr must be provided when allocating new page tables"
160 while pte_covers > size or not pt.supports_huge_page(paddr+off) or self.vaddr&(pte_covers-1) != 0: pt, pte_idx, pte_covers = self.level_down()
161 else:
162 while not pt.is_page(pte_idx) and (self.free_pts or pt.valid(pte_idx)): pt, pte_idx, pte_covers = self.level_down()
163
164 entries = max(min(size // pte_covers, self._pt_pte_cnt(pt.lv) - pte_idx), 1 if self.inspect else 0)
165 assert entries > 0, f"Invalid entries {size=:#x}, {pte_covers=:#x}"
166 yield off, pt, pte_idx, entries, pte_covers
167
168 size, off, self.vaddr = size - entries * pte_covers, off + entries * pte_covers, self.vaddr + entries * pte_covers
169 self.pt_stack[-1] = (pt, pte_idx + entries, pte_covers)
170 self.level_up()
171
172class MemoryManager:

Callers 6

test_double_mapMethod · 0.90
test_inspect_modeMethod · 0.90
page_tablesMethod · 0.85
map_rangeMethod · 0.85
unmap_rangeMethod · 0.85

Calls

no outgoing calls

Tested by 3

test_double_mapMethod · 0.72
test_inspect_modeMethod · 0.72

Used in the wild real call sites across dependent graphs

searching dependent graphs…