On Fri, Oct 23, 2026 at 07:51 AM zhen.ni <[email protected]> wrote:
tldr: it's still nack > underlying iteration logic. This shape is also what Documentation/bpf/ > bpf_iterators.rst prescribes: > > When adding a new BPF iterator program, it is expected that similar > functionality will be added as open-coded iterator for maximum > flexibility. It says what to do when an iterator is added. It doesn't say that one is needed. Iterators are for kernel data structures that a prog cannot walk on its own: tasks, cgroups, kmem_caches. pfn is a number. bpf_for() is the iterator. > 1. What range to scan. Where do ctx->start and ctx->nr come from? No > in-tree iterator context provides them, and a prog cannot derive them: > min_low_pfn..max_pfn, memblock holes, and offline sections are all > kernel state. The loop bounds alone require either another kfunc or a > kernel-side walk — which is exactly what the iter target already is. >From user space. It's a syscall prog, like scan_open_coded() in your patch 7. Its ctx is what user space passes in ctx_in. /proc/zoneinfo has start_pfn and spanned for every zone. In holes and offline sections pfn_to_online_page() returns NULL and bpf_page_owner_read() returns -ENOENT. > 2. What makes a page eligible. The alignment test in the sketch is the > only part the kernel agrees is expressible in a prog. Everything before > it reads state a prog doesn’t have a handle to: pfn_valid() over > MAX_ORDER_NR_PAGES areas, pfn_to_online_page() under get_online_mems(), > skip_buddy_pages(), the PAGE_EXT_OWNER / PAGE_EXT_OWNER_ALLOCATED flags > in page_ext, and taking the record snapshot inside the RCU window so the > checked value and the reported value are the same copy. bpf_page_owner_read() in the diff does all of that: pfn_to_online_page(), page_ext_get(), PAGE_EXT_OWNER_ALLOCATED check and the copy of the record before page_ext_put(). PAGE_EXT_OWNER is set together with PAGE_EXT_OWNER_ALLOCATED and it's never cleared. No need to check both. __reset_page_owner() clears PAGE_EXT_OWNER_ALLOCATED, so skip_buddy_pages() and MAX_ORDER_NR_PAGES steps only make the walk faster. They don't change what is eligible. > 3. The walk is deliberately lockless, so exposing struct page to the > prog is not safe. The pfn validity check and the page_ext dereference > must happen under the same kernel-controlled RCU window. Otherwise the The prog doesn't see struct page or page_ext. It passes a pfn and gets a copy of the record. The lookup, the check and the copy are in one RCU section inside the kfunc. offline_page_ext() does synchronize_rcu() before freeing page_ext. page_ext_from_phys() does the same lookup from any context. > 4. One record doesn’t give the prog the useful data. The record holds > only opaque handle/free_handle tokens; stack-depot resolution exists > solely in the kernel. The memcg is neither in the record nor trivially > reachable — the path is built by cgroup_path() walking the kernfs parent > chain. The NUMA node comes from page_owner_get_nid(), which bypasses > page_to_nid()’s poison check. And so on. The handle is the key to filter and aggregate by. page_owner_stacks/show_stacks_handles maps handles to stacks. Same for memcg. bpf_page_cgroup_ino() in the diff returns the id. st_ino of the cgroup directory is that id.
