From 070fe90461182743fabb029415fc1bc59be14f3f Mon Sep 17 00:00:00 2001 From: Ross Williams Date: Sun, 7 Jul 2019 02:37:02 +0000 Subject: 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 ad23d19b..b850815a 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 5f38540c..4d19185c 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 f30b487d..749231e1 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 ab90ca74..4e73c61e 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 @@ -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 b0456e5b..e775181e 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 e12c46e2..ebd80992 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 = { -- cgit v1.2.3