MCPcopy Create free account
hub / github.com/MemNixFS/MemNixFS / walk_inode_hashtable

Function walk_inode_hashtable

src/os/linux/pagecache.cpp:460–527  ·  view source on GitHub ↗

Walk the GLOBAL inode hashtable. This is the only reliable way to find every cached inode on modern (≥ ~5.x) kernels — `super_block.s_inodes` is frequently sparse because most inodes spend their cached life on the per-sb LRU and aren't kept on s_inodes. inode_hashtable is a `struct hlist_head *` array of `1 << i_hash_shift` entries (8 bytes each — single `first` pointer per bucket). Each cached i

Source from the content-addressed store, hash-verified

458// entries (8 bytes each — single `first` pointer per bucket). Each cached
459// inode is linked into one bucket via `inode.i_hash` (hlist_node @ 0xd0).
460std::vector<VAddr> walk_inode_hashtable(const Engine& eng, const Offsets& o)
461{
462 std::vector<VAddr> inodes;
463 const auto& isf = eng.isf();
464 if (o.in_i_hash == 0) {
465 log::warn("inode_hashtable: ISF has no inode.i_hash field");
466 return inodes;
467 }
468
469 auto* tbl_sym = isf.find_symbol("inode_hashtable");
470 auto* shift_sym = isf.find_symbol("i_hash_shift");
471 if (!tbl_sym || !shift_sym) {
472 log::warn("inode_hashtable: missing inode_hashtable / i_hash_shift "
473 "symbols");
474 return inodes;
475 }
476
477 VAddr table_va = 0;
478 if (!kva_read_pod(eng, tbl_sym->address, table_va) || table_va == 0) {
479 log::warn("inode_hashtable: cannot read table pointer");
480 return inodes;
481 }
482 u32 hash_shift = 0;
483 if (!kva_read_pod(eng, shift_sym->address, hash_shift) ||
484 hash_shift == 0 || hash_shift > 30)
485 {
486 log::warn("inode_hashtable: bad hash_shift = {}", hash_shift);
487 return inodes;
488 }
489 const u64 buckets = 1ULL << hash_shift;
490 log::debug("inode_hashtable @ {:#x} ({} buckets)", table_va, buckets);
491
492 // Read the array in big chunks to amortise PGD walks.
493 constexpr u64 kChunk = 0x10000; // 64 KiB at a time (8192 ptrs)
494 std::vector<VAddr> chunk(kChunk / sizeof(VAddr), 0);
495 constexpr std::size_t kInodeCap = 500'000;
496 std::size_t empty_buckets = 0;
497
498 for (u64 start = 0; start < buckets; start += chunk.size()) {
499 u64 want = std::min<u64>(chunk.size(), buckets - start);
500 std::size_t bytes = want * sizeof(VAddr);
501 std::memset(chunk.data(), 0, bytes);
502 kva_read(eng, table_va + start * sizeof(VAddr), chunk.data(), bytes);
503
504 for (u64 i = 0; i < want; ++i) {
505 VAddr node = chunk[i];
506 if (node == 0) { ++empty_buckets; continue; }
507 // Walk the hlist chain in this bucket.
508 int chain_guard = 0;
509 while (node != 0 && chain_guard++ < 1'000'000 &&
510 inodes.size() < kInodeCap)
511 {
512 VAddr inode_va = node - o.in_i_hash;
513 inodes.push_back(inode_va);
514
515 // Next link is at hlist_node offset 0.
516 VAddr next = 0;
517 if (!kva_read_pod(eng, node, next) || next == node) break;

Callers 1

enumerate_cached_inodesFunction · 0.85

Calls 6

warnFunction · 0.85
kva_read_podFunction · 0.85
debugFunction · 0.85
kva_readFunction · 0.85
find_symbolMethod · 0.80
sizeMethod · 0.45

Tested by

no test coverage detected