Skip to content

Commit 0857e40

Browse files
author
Hudson Gerwing
committed
feat: add compatibility for zfs arc architectures, (matches htop source code)
1 parent f51f62b commit 0857e40

File tree

1 file changed

+82
-2
lines changed

1 file changed

+82
-2
lines changed

psutil/_pslinux.py

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,10 @@ class IOPriority(enum.IntEnum):
171171
svmem = namedtuple(
172172
'svmem', ['total', 'available', 'percent', 'used', 'free',
173173
'active', 'inactive', 'buffers', 'cached', 'shared', 'slab'])
174+
# psutil.zfs_arc_stats()
175+
szfsarc = namedtuple(
176+
'szfsarc', ['enabled', 'min', 'max', 'compressed', 'uncompressed',
177+
'size', 'header', 'anon', 'mfu', 'mru', 'other'])
174178
# psutil.disk_io_counters()
175179
sdiskio = namedtuple(
176180
'sdiskio', ['read_count', 'write_count',
@@ -415,13 +419,15 @@ def calculate_avail_vmem(mems):
415419
return int(avail)
416420

417421

418-
def virtual_memory():
422+
def virtual_memory(bool:include_zfs_arc=False):
419423
"""Report virtual memory stats.
420-
This implementation mimicks procps-ng-3.3.12, aka "free" CLI tool:
424+
This implementation mimics procps-ng-3.3.12, aka "free" CLI tool:
421425
https://gitlab.com/procps-ng/procps/blob/
422426
24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L778-791
423427
The returned values are supposed to match both "free" and "vmstat -s"
424428
CLI tools.
429+
If specifying the `include_zfs_arc` parameter, the implementation mimics
430+
"htop" CLI tool instead of "free" CLI tool.
425431
"""
426432
missing_fields = []
427433
mems = {}
@@ -523,6 +529,22 @@ def virtual_memory():
523529
# 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L764
524530
avail = free
525531

532+
# ZFS ARC memory consumption is not reported by /proc/meminfo.
533+
# Specifying `include_zfs_arc` will include reclaimable ZFS ARC
534+
# memory in the returned values.
535+
# N.B. this will make psutil match the output of "htop" instead
536+
# of "free" CLI tool.
537+
# See:
538+
# https://www.reddit.com/r/zfs/comments/ha0p7f/understanding_arcstat_and_free/
539+
# https://github.com/openzfs/zfs/issues/10255
540+
if include_zfs_arc:
541+
zfs = zfs_arc_stats()
542+
if zfs.enabled:
543+
shrinkable_size = max(zfs.size - zfs.min, 0)
544+
used -= shrinkable_size
545+
cached += shrinkable_size
546+
avail += shrinkable_size
547+
526548
percent = usage_percent((total - avail), total, round_=1)
527549

528550
# Warn about missing metrics which are set to 0.
@@ -603,6 +625,64 @@ def swap_memory():
603625
return _common.sswap(total, used, free, percent, sin, sout)
604626

605627

628+
def zfs_arc_stats():
629+
"""Return ZFS ARC (Adaptive Replacement Cache) stats."""
630+
missing_fields = []
631+
mems = {}
632+
633+
def get_if_available(key):
634+
try:
635+
return mems[key] / 1024
636+
except KeyError:
637+
missing_fields.append(key)
638+
return 0
639+
640+
641+
with open_binary('%s/spl/kstat/zfs/arcstats' % get_procfs_path()) as f:
642+
for line in f:
643+
fields = line.split()
644+
mems[fields[0]] = int(fields[1])
645+
646+
zfs_min = get_if_available(b'c_min')
647+
zfs_max = get_if_available(b'c_max')
648+
zfs_compressed = get_if_available(b'compressed_size')
649+
zfs_uncompressed = get_if_available(b'uncompressed_size')
650+
zfs_size = get_if_available(b'size')
651+
zfs_header = get_if_available(b'hdr_size')
652+
dbuf_size = get_if_available(b'dbuf_size')
653+
dnode_size = get_if_available(b'dnode_size')
654+
bonus_size = get_if_available(b'bonus_size')
655+
zfs_anon = get_if_available(b'anon_size')
656+
zfs_mfu = get_if_available(b'mfu_size')
657+
zfs_mru = get_if_available(b'mru_size')
658+
659+
zfs_enabled = zfs.size > 0
660+
zfs_other = dbuf_size + dnode_size + bonus_size if all([dbuf_size, dnode_size, bonus_size]) else 0
661+
662+
# Warn about missing metrics which are set to 0.
663+
if missing_fields:
664+
msg = "%s memory stats couldn't be determined and %s set to 0" % (
665+
", ".join(missing_fields),
666+
"was" if len(missing_fields) == 1 else "were",
667+
)
668+
warnings.warn(msg, RuntimeWarning, stacklevel=2)
669+
670+
return szfsarc(
671+
zfs_enabled,
672+
zfs_min,
673+
zfs_max,
674+
zfs_compressed,
675+
zfs_uncompressed,
676+
zfs_size,
677+
zfs_header,
678+
zfs_anon,
679+
zfs_mfu,
680+
zfs_mru,
681+
zfs_other
682+
)
683+
684+
685+
606686
# =====================================================================
607687
# --- CPU
608688
# =====================================================================

0 commit comments

Comments
 (0)