Hello, all.

This patch would let slstatus report on disk IO on Linux systems: reads
from disk, writes to disk and IO percentage.

io_in and io_out reference io_get, which is a light modification of code
from swap.c, so the first part of iocheck.c is very similar to that.
io_perc gets IO wait tick statistics from /sys/block, excluding virtual
devices to make a proper calculation. (As a note for testing purposes,
htop currently double-counts IO involving nvme drives.)

Performance-wise, running all three functions at the same time seems to 
have about the same CPU load as wifi_perc, netspeed_rx and netspeed_tx
together.

If there's any feedback (positive or negative), needed changes, etc.,
I'd be very happy to hear about it!

Thanks very much,

Gene Carlson

---
 Makefile             |   1 +
 README               |   1 +
 components/iocheck.c | 170 +++++++++++++++++++++++++++++++++++++++++++
 config.def.h         |   3 +
 slstatus.h           |   5 ++
 5 files changed, 180 insertions(+)
 create mode 100644 components/iocheck.c

diff --git a/Makefile b/Makefile
index 2f93b87..fd85ad7 100644
--- a/Makefile
+++ b/Makefile
@@ -12,6 +12,7 @@ COM =\
        components/disk\
        components/entropy\
        components/hostname\
+       components/iocheck\
        components/ip\
        components/kernel_release\
        components/keyboard_indicators\
diff --git a/README b/README
index 86fe988..996c22e 100644
--- a/README
+++ b/README
@@ -15,6 +15,7 @@ Features
 - Available entropy
 - Username/GID/UID
 - Hostname
+- Disk IO (read, write and percentage) (Linux only)
 - IP address (IPv4 and IPv6)
 - Kernel version
 - Keyboard indicators
diff --git a/components/iocheck.c b/components/iocheck.c
new file mode 100644
index 0000000..d3ff9d8
--- /dev/null
+++ b/components/iocheck.c
@@ -0,0 +1,170 @@
+/* See LICENSE file for copyright and license details. */
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <unistd.h>
+
+#include "../util.h"
+
+#if defined(__linux__)
+       static int
+       get_io(uintmax_t *s_in, uintmax_t *s_out)
+       {
+               FILE *fp;
+               struct {
+                       const char *name;
+                       const size_t len;
+                       uintmax_t *var;
+               } ent[] = {
+                       { "pgpgin",  sizeof("pgpgin") - 1,  s_in  },
+                       { "pgpgout", sizeof("pgpgout") - 1, s_out },
+               };
+               size_t line_len = 0, i, left;
+               char *line = NULL;
+                       
+               /* get number of fields we want to extract */
+               for (i = 0, left = 0; i < LEN(ent); i++) {
+                       if (ent[i].var) {
+                               left++;
+                       }
+               }
+                
+               if (!(fp = fopen("/proc/vmstat", "r"))) {
+                       warn("fopen '/proc/vmstat':");
+                       return 1;
+               }
+
+               /* read file line by line and extract field information */
+               while (left > 0 && getline(&line, &line_len, fp) >= 0) {
+                       for (i = 0; i < LEN(ent); i++) {
+                               if (ent[i].var &&
+                               !strncmp(line,ent[i].name, ent[i].len)) {
+                                       sscanf(line + ent[i].len + 1,
+                                               "%ju\n", ent[i].var);
+                                       left--;
+                                       break;
+                               }
+                       }
+               }
+               free(line);
+               if(ferror(fp)) {
+                       warn("getline '/proc/vmstat':");
+                       return 1;
+               }
+               
+               fclose(fp);
+               return 0;
+       }       
+               
+       const char *
+       io_in(void)
+       {
+               uintmax_t oldin;
+               static uintmax_t newin;
+
+               oldin = newin;
+
+               if (get_io(&newin, NULL)) {
+                       return NULL;
+               }
+               if (oldin == 0) {
+                       return NULL;
+               }
+               
+               return fmt_human((newin-oldin) * 1024, 1024);
+       }
+
+       const char *
+       io_out(void)
+       {
+               uintmax_t oldout;
+               static uintmax_t newout;
+
+               oldout = newout;
+
+               if (get_io(NULL, &newout)) {
+                       return NULL;
+               }
+               if (oldout == 0) {
+                       return NULL;
+               }
+               
+               return fmt_human((newout - oldout) * 1024, 1024);
+       }
+
+       const char *
+       io_perc(void)
+       {
+               struct dirent *dp;
+               DIR *bd;
+               uintmax_t oldwait;
+               static uintmax_t newwait;
+               extern const unsigned int interval;
+
+               oldwait = newwait;
+               
+               if (!(bd = opendir("/sys/block"))) {
+                       warn("opendir '%s':", "/sys/block");
+                       return NULL;
+               } 
+
+               newwait = 0;
+               /* get IO wait stats from the /sys/block directories */
+               while ((dp = readdir(bd))) {
+                       if (strstr(dp->d_name, "loop") ||
+                           strstr(dp->d_name, "ram")) {
+                               continue;
+                       }
+                       if (!strcmp(dp->d_name, ".") ||
+                           !strcmp(dp->d_name, "..")) {
+                               continue;
+                       }
+                       /* virtual devices don't count */
+                       char virtpath[50];
+                       strcpy(virtpath, "/sys/devices/virtual/block/");
+                       strcat(virtpath, dp->d_name);
+                       if (access(virtpath, R_OK) == 0) {
+                               continue;
+                       }
+                       char statpath[34];
+                       strcpy(statpath, "/sys/block/");
+                       strcat(statpath, dp->d_name);
+                       strcat(statpath, "/stat");
+                       uintmax_t partwait;
+                       if (pscanf(statpath,
+                               "%*d %*d %*d %*d %*d %*d %*d %*d %*d %ju %*d",
+                               &partwait) != 1) {
+                               continue;
+                       }
+                       newwait += partwait;
+               }
+               closedir(bd);
+               if (oldwait == 0 || newwait < oldwait) {
+                       return NULL;
+               }
+               
+               return bprintf("%0.1f", 100 *
+                      (newwait - oldwait) / (float)interval);
+       }
+
+#else
+       const char *
+       io_in(void)
+       {
+               return NULL;
+       }
+
+       const char *
+       io_out(void)
+       {
+               return NULL;
+       }
+
+       const char *
+       io_perc(void)
+       {
+               return NULL;
+       }
+#endif
diff --git a/config.def.h b/config.def.h
index 93a875a..c321c50 100644
--- a/config.def.h
+++ b/config.def.h
@@ -28,6 +28,9 @@ static const char unknown_str[] = "n/a";
  * entropy             available entropy               NULL
  * gid                 GID of current user             NULL
  * hostname            hostname                        NULL
+ * io_in               disk IO (read)                  NULL
+ * io_out              disk IO (write)                 NULL
+ * io_perc             disk IO (percentage)            NULL
  * ipv4                IPv4 address                    interface name (eth0)
  * ipv6                IPv6 address                    interface name (eth0)
  * kernel_release      `uname -r`                      NULL
diff --git a/slstatus.h b/slstatus.h
index b0f2564..3464f5b 100644
--- a/slstatus.h
+++ b/slstatus.h
@@ -24,6 +24,11 @@ const char *entropy(void);
 /* hostname */
 const char *hostname(void);
 
+/* iocheck */
+const char *io_in(void);
+const char *io_out(void);
+const char *io_perc(void);
+
 /* ip */
 const char *ipv4(const char *interface);
 const char *ipv6(const char *interface);
-- 
2.33.0


Reply via email to