about summary refs log tree commit diff
path: root/users/multi/pkgs/htop/zfs-arc-stats/0003-ZFS-arcstats-for-Linux.patch
From 070fe90461182743fabb029415fc1bc59be14f3f Mon Sep 17 00:00:00 2001
From: Ross Williams <ross@ross-williams.net>
Date: Sun, 7 Jul 2019 02:37:02 +0000
Subject: [PATCH 3/9] ZFS arcstats for Linux

If no pools are imported (ARC size == 0) or the
ZFS module is not in the kernel (/proc/spl/kstat/zfs/arcstats
does not exist), then the Meter reports "Unavailable".
---
 Makefile.am              |  6 ++--
 linux/LinuxProcessList.c | 66 ++++++++++++++++++++++++++++++++++++++++
 linux/LinuxProcessList.h | 13 ++++++++
 linux/Platform.c         | 19 ++++++++++++
 linux/Platform.h         |  2 ++
 zfs/ZfsArcMeter.c        | 47 +++++++++++++++-------------
 6 files changed, 130 insertions(+), 23 deletions(-)

diff --git a/Makefile.am b/Makefile.am
index ad23d19..b850815 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -52,14 +52,16 @@ linux_platform_headers = \
 	linux/LinuxProcess.h \
 	linux/LinuxProcessList.h \
 	linux/LinuxCRT.h \
-	linux/Battery.h
+	linux/Battery.h \
+	$(zfs_platform_headers)
 
 all_platform_headers += $(linux_platform_headers)
 
 if HTOP_LINUX
 AM_CFLAGS += -rdynamic
 myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \
-linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c
+linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c \
+$(zfs_platform_sources)
 
 myhtopplatheaders = $(linux_platform_headers)
 endif
diff --git a/linux/LinuxProcessList.c b/linux/LinuxProcessList.c
index 5f38540..4d19185 100644
--- a/linux/LinuxProcessList.c
+++ b/linux/LinuxProcessList.c
@@ -94,6 +94,15 @@ typedef struct LinuxProcessList_ {
    struct nl_sock *netlink_socket;
    int netlink_family;
    #endif
+
+   int zfsArcEnabled;
+   unsigned long long int memZfsArc;
+   unsigned long long int zfsArcMax;
+   unsigned long long int zfsArcMFU;
+   unsigned long long int zfsArcMRU;
+   unsigned long long int zfsArcAnon;
+   unsigned long long int zfsArcHeader;
+   unsigned long long int zfsArcOther;
 } LinuxProcessList;
 
 #ifndef PROCDIR
@@ -108,6 +117,10 @@ typedef struct LinuxProcessList_ {
 #define PROCMEMINFOFILE PROCDIR "/meminfo"
 #endif
 
+#ifndef PROCARCSTATSFILE
+#define PROCARCSTATSFILE PROCDIR "/spl/kstat/zfs/arcstats"
+#endif
+
 #ifndef PROCTTYDRIVERSFILE
 #define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers"
 #endif
@@ -964,6 +977,58 @@ static inline void LinuxProcessList_scanMemoryInfo(ProcessList* this) {
    fclose(file);
 }
 
+static inline void LinuxProcessList_scanZfsArcstats(LinuxProcessList* lpl) {
+   unsigned long long int dbufSize;
+   unsigned long long int dnodeSize;
+   unsigned long long int bonusSize;
+
+   FILE* file = fopen(PROCARCSTATSFILE, "r");
+   if (file == NULL) {
+      lpl->zfsArcEnabled = 0;
+      return;
+   }
+   char buffer[128];
+   while (fgets(buffer, 128, file)) {
+      #define tryRead(label, variable) do { if (String_startsWith(buffer, label) && sscanf(buffer + strlen(label), " %*2u %32llu", variable)) { break; } } while(0)
+      switch (buffer[0]) {
+      case 'c':
+         tryRead("c_max", &lpl->zfsArcMax);
+         break;
+      case 's':
+         tryRead("size", &lpl->memZfsArc);
+         break;
+      case 'h':
+         tryRead("hdr_size", &lpl->zfsArcHeader);
+         break;
+      case 'd':
+         tryRead("dbuf_size", &dbufSize);
+         tryRead("dnode_size", &dnodeSize);
+         break;
+      case 'b':
+         tryRead("bonus_size", &bonusSize);
+         break;
+      case 'a':
+         tryRead("anon_size", &lpl->zfsArcAnon);
+         break;
+      case 'm':
+         tryRead("mfu_size", &lpl->zfsArcMFU);
+         tryRead("mru_size", &lpl->zfsArcMRU);
+         break;
+      }
+      #undef tryRead
+   }
+   fclose(file);
+
+   lpl->zfsArcEnabled = (lpl->memZfsArc > 0 ? 1 : 0);
+   lpl->memZfsArc    /= 1024;
+   lpl->zfsArcMax    /= 1024;
+   lpl->zfsArcMFU    /= 1024;
+   lpl->zfsArcMRU    /= 1024;
+   lpl->zfsArcAnon   /= 1024;
+   lpl->zfsArcHeader /= 1024;
+   lpl->zfsArcOther   = (dbufSize + dnodeSize + bonusSize) / 1024;
+}
+
 static inline double LinuxProcessList_scanCPUTime(LinuxProcessList* this) {
 
    FILE* file = fopen(PROCSTATFILE, "r");
@@ -1038,6 +1103,7 @@ void ProcessList_goThroughEntries(ProcessList* super) {
    LinuxProcessList* this = (LinuxProcessList*) super;
 
    LinuxProcessList_scanMemoryInfo(super);
+   LinuxProcessList_scanZfsArcstats(this);
    double period = LinuxProcessList_scanCPUTime(this);
 
    struct timeval tv;
diff --git a/linux/LinuxProcessList.h b/linux/LinuxProcessList.h
index f30b487..749231e 100644
--- a/linux/LinuxProcessList.h
+++ b/linux/LinuxProcessList.h
@@ -67,6 +67,15 @@ typedef struct LinuxProcessList_ {
    struct nl_sock *netlink_socket;
    int netlink_family;
    #endif
+
+   int zfsArcEnabled;
+   unsigned long long int memZfsArc;
+   unsigned long long int zfsArcMax;
+   unsigned long long int zfsArcMFU;
+   unsigned long long int zfsArcMRU;
+   unsigned long long int zfsArcAnon;
+   unsigned long long int zfsArcHeader;
+   unsigned long long int zfsArcOther;
 } LinuxProcessList;
 
 #ifndef PROCDIR
@@ -81,6 +90,10 @@ typedef struct LinuxProcessList_ {
 #define PROCMEMINFOFILE PROCDIR "/meminfo"
 #endif
 
+#ifndef PROCARCSTATSFILE
+#define PROCARCSTATSFILE PROCDIR "/spl/kstat/zfs/arcstats"
+#endif
+
 #ifndef PROCTTYDRIVERSFILE
 #define PROCTTYDRIVERSFILE PROCDIR "/tty/drivers"
 #endif
diff --git a/linux/Platform.c b/linux/Platform.c
index ab90ca7..4e73c61 100644
--- a/linux/Platform.c
+++ b/linux/Platform.c
@@ -21,6 +21,7 @@ in the source distribution for its full text.
 #include "UptimeMeter.h"
 #include "ClockMeter.h"
 #include "HostnameMeter.h"
+#include "zfs/ZfsArcMeter.h"
 #include "LinuxProcess.h"
 
 #include <math.h>
@@ -126,6 +127,7 @@ MeterClass* Platform_meterTypes[] = {
    &LeftCPUs2Meter_class,
    &RightCPUs2Meter_class,
    &BlankMeter_class,
+   &ZfsArcMeter_class,
    NULL
 };
 
@@ -213,6 +215,23 @@ void Platform_setSwapValues(Meter* this) {
    this->values[0] = pl->usedSwap;
 }
 
+void Platform_setZfsArcValues(Meter* this) {
+   LinuxProcessList* lpl = (LinuxProcessList*) this->pl;
+
+   this->total = lpl->zfsArcMax;
+   this->values[0] = lpl->zfsArcMFU;
+   this->values[1] = lpl->zfsArcMRU;
+   this->values[2] = lpl->zfsArcAnon;
+   this->values[3] = lpl->zfsArcHeader;
+   this->values[4] = lpl->zfsArcOther;
+
+   // "Hide" the last value so it can
+   // only be accessed by index and is not
+   // displayed by the Bar or Graph style
+   Meter_setItems(this, 5);
+   this->values[5] = lpl->memZfsArc;
+}
+
 char* Platform_getProcessEnv(pid_t pid) {
    char procname[32+1];
    xSnprintf(procname, 32, "/proc/%d/environ", pid);
diff --git a/linux/Platform.h b/linux/Platform.h
index b0456e5..e775181 100644
--- a/linux/Platform.h
+++ b/linux/Platform.h
@@ -43,6 +43,8 @@ void Platform_setMemoryValues(Meter* this);
 
 void Platform_setSwapValues(Meter* this);
 
+void Platform_setZfsArcValues(Meter* this);
+
 char* Platform_getProcessEnv(pid_t pid);
 
 #endif
diff --git a/zfs/ZfsArcMeter.c b/zfs/ZfsArcMeter.c
index e12c46e..ebd8099 100644
--- a/zfs/ZfsArcMeter.c
+++ b/zfs/ZfsArcMeter.c
@@ -41,27 +41,32 @@ static void ZfsArcMeter_display(Object* cast, RichString* out) {
    char buffer[50];
    Meter* this = (Meter*)cast;
 
-   RichString_write(out, CRT_colors[METER_TEXT], ":");
-   Meter_humanUnit(buffer, this->total, 50);
-   RichString_append(out, CRT_colors[METER_VALUE], buffer);
-   Meter_humanUnit(buffer, this->values[5], 50);
-   RichString_append(out, CRT_colors[METER_TEXT], " Used:");
-   RichString_append(out, CRT_colors[METER_VALUE], buffer);
-   Meter_humanUnit(buffer, this->values[0], 50);
-   RichString_append(out, CRT_colors[METER_TEXT], " MFU:");
-   RichString_append(out, CRT_colors[ZFS_MFU], buffer);
-   Meter_humanUnit(buffer, this->values[1], 50);
-   RichString_append(out, CRT_colors[METER_TEXT], " MRU:");
-   RichString_append(out, CRT_colors[ZFS_MRU], buffer);
-   Meter_humanUnit(buffer, this->values[2], 50);
-   RichString_append(out, CRT_colors[METER_TEXT], " Anon:");
-   RichString_append(out, CRT_colors[ZFS_ANON], buffer);
-   Meter_humanUnit(buffer, this->values[3], 50);
-   RichString_append(out, CRT_colors[METER_TEXT], " Hdr:");
-   RichString_append(out, CRT_colors[ZFS_HEADER], buffer);
-   Meter_humanUnit(buffer, this->values[4], 50);
-   RichString_append(out, CRT_colors[METER_TEXT], " Oth:");
-   RichString_append(out, CRT_colors[ZFS_OTHER], buffer);
+   if (this->values[5] > 0) {
+      RichString_write(out, CRT_colors[METER_TEXT], ":");
+      Meter_humanUnit(buffer, this->total, 50);
+      RichString_append(out, CRT_colors[METER_VALUE], buffer);
+      Meter_humanUnit(buffer, this->values[5], 50);
+      RichString_append(out, CRT_colors[METER_TEXT], " Used:");
+      RichString_append(out, CRT_colors[METER_VALUE], buffer);
+      Meter_humanUnit(buffer, this->values[0], 50);
+      RichString_append(out, CRT_colors[METER_TEXT], " MFU:");
+      RichString_append(out, CRT_colors[ZFS_MFU], buffer);
+      Meter_humanUnit(buffer, this->values[1], 50);
+      RichString_append(out, CRT_colors[METER_TEXT], " MRU:");
+      RichString_append(out, CRT_colors[ZFS_MRU], buffer);
+      Meter_humanUnit(buffer, this->values[2], 50);
+      RichString_append(out, CRT_colors[METER_TEXT], " Anon:");
+      RichString_append(out, CRT_colors[ZFS_ANON], buffer);
+      Meter_humanUnit(buffer, this->values[3], 50);
+      RichString_append(out, CRT_colors[METER_TEXT], " Hdr:");
+      RichString_append(out, CRT_colors[ZFS_HEADER], buffer);
+      Meter_humanUnit(buffer, this->values[4], 50);
+      RichString_append(out, CRT_colors[METER_TEXT], " Oth:");
+      RichString_append(out, CRT_colors[ZFS_OTHER], buffer);
+   } else {
+      RichString_write(out, CRT_colors[METER_TEXT], " ");
+      RichString_append(out, CRT_colors[FAILED_SEARCH], "Unavailable");
+   }
 }
 
 MeterClass ZfsArcMeter_class = {
-- 
2.20.1