70 lines
2.6 KiB
Python
70 lines
2.6 KiB
Python
|
|
#!/usr/bin/env python
|
||
|
|
"""Hand the archive's file handles back to the host.
|
||
|
|
|
||
|
|
The archive is on a virtiofs share whose daemon holds a host file descriptor per
|
||
|
|
inode the guest has looked up. Anything that reads a lot of the archive leaves those
|
||
|
|
inodes cached, and the handles with them, until something forces the guest to evict
|
||
|
|
them -- at which point the daemon can run out and the mount starts refusing *every*
|
||
|
|
open, which surfaces across the whole machine as "too many open files in system".
|
||
|
|
|
||
|
|
The pipeline's own bulk jobs now reclaim as they go and again when they finish, so
|
||
|
|
this should rarely be needed. It exists for when something else has loaded the cache,
|
||
|
|
or to check where things stand:
|
||
|
|
|
||
|
|
reclaim.py # report, then reclaim if needed
|
||
|
|
reclaim.py --check # report only, change nothing
|
||
|
|
sudo reclaim.py # uses drop_caches: instant, and far gentler
|
||
|
|
|
||
|
|
Without root the only lever available is memory pressure, which means briefly
|
||
|
|
allocating several GiB. Running under sudo avoids that entirely.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import argparse
|
||
|
|
import os
|
||
|
|
import sys
|
||
|
|
|
||
|
|
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||
|
|
|
||
|
|
from suvi import vfs
|
||
|
|
|
||
|
|
#: Roughly the slab size at which the daemon is likely near a 1M descriptor limit.
|
||
|
|
#: A cached dentry plus inode is on the order of a kilobyte, so a gigabyte of
|
||
|
|
#: reclaimable slab is on the order of a million inodes.
|
||
|
|
CONCERN_GIB = 1.0
|
||
|
|
|
||
|
|
|
||
|
|
def report(prefix):
|
||
|
|
slab = vfs.reclaimable_kb() / (1024 * 1024)
|
||
|
|
print(f"{prefix:>8}: {slab:.2f} GiB reclaimable slab "
|
||
|
|
f"(~{slab:.1f}M cached inodes), {vfs.available_gib():.1f} GiB available")
|
||
|
|
return slab
|
||
|
|
|
||
|
|
|
||
|
|
def main(argv=None):
|
||
|
|
parser = argparse.ArgumentParser(description=__doc__,
|
||
|
|
formatter_class=argparse.RawDescriptionHelpFormatter)
|
||
|
|
parser.add_argument("--check", action="store_true", help="report only, reclaim nothing")
|
||
|
|
parser.add_argument("--force", action="store_true", help="reclaim even if it looks fine")
|
||
|
|
args = parser.parse_args(argv)
|
||
|
|
|
||
|
|
before = report("before")
|
||
|
|
if args.check:
|
||
|
|
print(" (check only; nothing reclaimed)")
|
||
|
|
return 0 if before < CONCERN_GIB else 1
|
||
|
|
|
||
|
|
if before < CONCERN_GIB and not args.force:
|
||
|
|
print(f" below {CONCERN_GIB} GiB; nothing to do. Use --force to reclaim anyway.")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
if os.geteuid() != 0:
|
||
|
|
print(" not root, so using memory pressure; run under sudo for the direct path")
|
||
|
|
freed = vfs.release_handles()
|
||
|
|
after = report("after")
|
||
|
|
print(f" {'reclaimed' if freed else 'no change'}: "
|
||
|
|
f"{before - after:+.2f} GiB")
|
||
|
|
return 0
|
||
|
|
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
sys.exit(main())
|