@@ -171,10 +171,6 @@ class IOPriority(enum.IntEnum):
171171svmem = 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' ])
178174# psutil.disk_io_counters()
179175sdiskio = namedtuple (
180176 'sdiskio' , ['read_count' , 'write_count' ,
@@ -419,15 +415,13 @@ def calculate_avail_vmem(mems):
419415 return int (avail )
420416
421417
422- def virtual_memory (include_zfs_arc : bool = False ):
418+ def virtual_memory ():
423419 """Report virtual memory stats.
424420 This implementation mimics procps-ng-3.3.12, aka "free" CLI tool:
425421 https://gitlab.com/procps-ng/procps/blob/
426422 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L778-791
427423 The returned values are supposed to match both "free" and "vmstat -s"
428424 CLI tools.
429- If specifying the `include_zfs_arc` parameter, the implementation mimics
430- "htop" CLI tool instead of "free" CLI tool.
431425 """
432426 missing_fields = []
433427 mems = {}
@@ -529,29 +523,7 @@ def virtual_memory(include_zfs_arc: bool = False):
529523 # 24fd2605c51fccc375ab0287cec33aa767f06718/proc/sysinfo.c#L764
530524 avail = free
531525
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-
543- # When accounting for zfs memory, we need to keep track of "shared"
544- # So "used" memory is more relevant than "available"
545- used += shared
546- cached -= shared
547- if zfs .enabled :
548- shrinkable_size = max (zfs .size - zfs .min , 0 )
549- used -= shrinkable_size
550- cached += shrinkable_size
551- avail += shrinkable_size
552- percent = usage_percent (used , total , round_ = 1 )
553- else :
554- percent = usage_percent ((total - avail ), total , round_ = 1 )
526+ percent = usage_percent ((total - avail ), total , round_ = 1 )
555527
556528 # Warn about missing metrics which are set to 0.
557529 if missing_fields :
@@ -631,19 +603,11 @@ def swap_memory():
631603 return _common .sswap (total , used , free , percent , sin , sout )
632604
633605
634- def zfs_arc_stats ( ):
635- """Return ZFS ARC (Adaptive Replacement Cache) stats."""
636- missing_fields = []
606+ def apply_zfs_arcstats ( vm_stats : svmem ):
607+ """Apply ZFS ARC (Adaptive Replacement Cache) stats to
608+ input virtual memory call results"""
637609 mems = {}
638610
639- def get_if_available (key ):
640- try :
641- return mems [key ]
642- except KeyError :
643- missing_fields .append (key )
644- return 0
645-
646-
647611 with open_binary ('%s/spl/kstat/zfs/arcstats' % get_procfs_path ()) as f :
648612 for line in f :
649613 fields = line .split ()
@@ -653,44 +617,35 @@ def get_if_available(key):
653617 # Not a key: value line
654618 continue
655619
656- zfs_min = get_if_available (b'c_min' )
657- zfs_max = get_if_available (b'c_max' )
658- zfs_compressed = get_if_available (b'compressed_size' )
659- zfs_uncompressed = get_if_available (b'uncompressed_size' )
660- zfs_size = get_if_available (b'size' )
661- zfs_header = get_if_available (b'hdr_size' )
662- dbuf_size = get_if_available (b'dbuf_size' )
663- dnode_size = get_if_available (b'dnode_size' )
664- bonus_size = get_if_available (b'bonus_size' )
665- zfs_anon = get_if_available (b'anon_size' )
666- zfs_mfu = get_if_available (b'mfu_size' )
667- zfs_mru = get_if_available (b'mru_size' )
668-
669- zfs_enabled = zfs_size > 0
670- zfs_other = dbuf_size + dnode_size + bonus_size if all ([dbuf_size , dnode_size , bonus_size ]) else 0
671-
672- # Warn about missing metrics which are set to 0.
673- if missing_fields :
674- msg = "%s memory stats couldn't be determined and %s set to 0" % (
675- ", " .join (missing_fields ),
676- "was" if len (missing_fields ) == 1 else "were" ,
677- )
620+ try :
621+ zfs_min = mems [b'c_min' ]
622+ zfs_size = mems [b'size' ]
623+ except KeyError :
624+ msg = ("ZFS ARC memory stats couldn't be determined, "
625+ "no modification made to virtual memory stats" )
678626 warnings .warn (msg , RuntimeWarning , stacklevel = 2 )
627+ zfs_min = zfs_size = 0
679628
680- return szfsarc (
681- zfs_enabled ,
682- zfs_min ,
683- zfs_max ,
684- zfs_compressed ,
685- zfs_uncompressed ,
686- zfs_size ,
687- zfs_header ,
688- zfs_anon ,
689- zfs_mfu ,
690- zfs_mru ,
691- zfs_other
692- )
629+ # ZFS ARC memory consumption is not reported by /proc/meminfo.
630+ # Running this func will include reclaimable ZFS ARC
631+ # memory in the returned values.
632+ # N.B. this will make psutil match the output of "htop" instead
633+ # of "free" CLI tool.
634+ # See:
635+ # https://www.reddit.com/r/zfs/comments/ha0p7f/understanding_arcstat_and_free/
636+ # https://github.com/openzfs/zfs/issues/10255
693637
638+ # When accounting for zfs memory, we need to keep track of "shared"
639+ # So "used" memory is more relevant than "available"
640+ vm_stats .used += vm_stats .shared
641+ vm_stats .cached -= vm_stats .shared
642+ shrinkable_size = max (zfs_size - zfs_min , 0 )
643+ vm_stats .used -= shrinkable_size
644+ vm_stats .cached += shrinkable_size
645+ vm_stats .available += shrinkable_size
646+ vm_stats .percent = usage_percent (vm_stats .used , vm_stats .total , round_ = 1 )
647+
648+ return vm_stats
694649
695650
696651# =====================================================================
0 commit comments