]> granicus.if.org Git - procps-ng/commitdiff
library: normalize/standardize an i/f, <DISKSTATS> api
authorJim Warner <james.warner@comcast.net>
Wed, 20 Jul 2016 05:00:00 +0000 (00:00 -0500)
committerCraig Small <csmall@enc.com.au>
Tue, 26 Jul 2016 10:46:08 +0000 (20:46 +1000)
This patch will bring this interface up to our 3rd gen
standards. The following summarizes the major changes.

* New delta provisions have been added to most fields.

There are, of course, some fields for which a delta is
inappropriate. They include the identifying items such
as name, type, major and minor. Plus the io_inprogress
field which already acts, in effect, as a delta value.

* To provide delta support, dev_node historical values
have become persistent. By the same token, the library
must provide for future removal of disks/partitions. A
timestamp is used to detect 'stale' data which will be
deleted so as not to satisfy some get, select or reap.

* Such persistent support is provided by a linked list
which, by default, grows from the bottom down so as to
maintain compatibility with the /proc/diskstats order.

Initially, I was tempted to use the GNU tsearch (tree)
provisions until I discovered the overhead of building
that tree plus costs of a subsequent 'twalk'. Besides,
walking such a tree means retrieval order would differ
from an order required/expected by the vmstat program.

* The '/sys/block' directory is no longer scanned with
every refresh cycle. Rather, it's only accessed when a
node is first encountered. Then, that node's 'type' is
persistent for its lifetime like several other fields.

* A sort provision was included, at virtually no cost,
even though such a provision was not currently needed.

Signed-off-by: Jim Warner <james.warner@comcast.net>
proc/diskstats.c
proc/diskstats.h
proc/libprocps.sym

index cf8a117eea7d7f0f433564fa98309616cb4d1159..3a491612023ae7fa1b7197eb1786c05d2d5edf3f 100644 (file)
@@ -4,6 +4,7 @@
  * Copyright (C) 2003 Fabian Frederick
  * Copyright (C) 2003 Albert Cahalan
  * Copyright (C) 2015 Craig Small <csmall@enc.com.au>
+ * Copyright (C) 2016 Jim Warner <james.warner@comcast.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  */
 
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <time.h>
 #include <unistd.h>
-#include <sys/types.h>
+
 #include <sys/stat.h>
-#include <fcntl.h>
-#include <dirent.h>
-#include <errno.h>
+#include <sys/types.h>
 
-#include <proc/diskstats.h>
 #include "procps-private.h"
+#include <proc/diskstats.h>
 
-#define DISKSTAT_LINE_LEN 1024
-#define DISKSTAT_NAME_LEN 34
-#define DISKSTAT_FILE "/proc/diskstats"
-#define SYSBLOCK_DIR "/sys/block"
+/* The following define will cause the 'node_add' function to maintain our |
+   nodes list in ascending alphabetical order which could be used to avoid |
+   a sort on name. Without it, we default to a 'pull-up' stack at slightly |
+   more effort than a simple 'push-down' list to duplicate prior behavior. | */
+//#define ALPHABETIC_NODES
 
-struct procps_diskstat_dev {
-    char name[DISKSTAT_NAME_LEN+1];
-    int is_disk;
+#define DISKSTATS_LINE_LEN  1024
+#define DISKSTATS_NAME_LEN  34
+#define DISKSTATS_FILE      "/proc/diskstats"
+#define SYSBLOCK_DIR        "/sys/block"
+
+#define STACKS_INCR         64
+#define STR_COMPARE         strverscmp
+
+struct dev_data {
     unsigned long reads;
     unsigned long reads_merged;
     unsigned long read_sectors;
@@ -54,298 +64,912 @@ struct procps_diskstat_dev {
     unsigned long io_wtime;
 };
 
-struct procps_diskstat {
+struct dev_node {
+    char name[DISKSTATS_NAME_LEN+1];
+    int type;
+    int major;
+    int minor;
+    time_t stamped;
+    struct dev_data new;
+    struct dev_data old;
+    struct dev_node *next;
+};
+
+struct stacks_extent {
+    int ext_numstacks;
+    struct stacks_extent *next;
+    struct diskstats_stack **stacks;
+};
+
+struct ext_support {
+    int numitems;                      // includes 'logical_end' delimiter
+    enum diskstats_item *items;        // includes 'logical_end' delimiter
+    struct stacks_extent *extents;     // anchor for these extents
+    int dirty_stacks;
+};
+
+struct fetch_support {
+    struct diskstats_stack **anchor;   // fetch consolidated extents
+    int n_alloc;                       // number of above pointers allocated
+    int n_inuse;                       // number of above pointers occupied
+    int n_alloc_save;                  // last known reap.stacks allocation
+    struct diskstats_reap results;     // count + stacks for return to caller
+};
+
+struct procps_diskstats {
     int refcount;
-    FILE *diskstat_fp;
-    struct procps_diskstat_dev *devs;
-    int devs_alloc;
-    int devs_used;
+    FILE *diskstats_fp;
+    time_t old_stamp;                  /* previous read seconds */
+    time_t new_stamp;                  /* current read seconds */
+    struct dev_node *nodes;            /* dev nodes anchor */
+    struct ext_support select_ext;     /* supports concurrent select/reap */
+    struct ext_support fetch_ext;      /* supports concurrent select/reap */
+    struct fetch_support fetch;        /* support for procps_diskstats_reap */
+    struct diskstats_result get_this;  /* used by procps_diskstats_get */
 };
 
-/*
- * scan_for_disks:
- *
- * All disks start off as partitions. This function
- * scans /sys/block and changes all devices found there
- * into disks. If /sys/block cannot have the directory
- * read, all devices are disks
- */
-static int scan_for_disks(struct procps_diskstat *info)
+
+// ___ Results 'Set' Support ||||||||||||||||||||||||||||||||||||||||||||||||||
+
+#define setNAME(e) set_results_ ## e
+#define setDECL(e) static void setNAME(e) \
+    (struct diskstats_result *R, struct dev_node *N)
+
+// regular assignment
+#define DEV_set(e,t,x) setDECL(e) { R->result. t = N-> x; }
+#define REG_set(e,t,x) setDECL(e) { R->result. t = N->new . x; }
+// delta assignment
+#define HST_set(e,t,x) setDECL(e) { R->result. t = ( N->new . x - N->old. x ); }
+
+setDECL(noop)  { (void)R; (void)N; }
+setDECL(extra) { (void)R; (void)N; }
+
+DEV_set(DISKSTATS_NAME,                 str,     name)
+DEV_set(DISKSTATS_TYPE,                 s_int,   type)
+DEV_set(DISKSTATS_MAJOR,                s_int,   major)
+DEV_set(DISKSTATS_MINOR,                s_int,   minor)
+
+REG_set(DISKSTATS_READS,                ul_int,  reads)
+REG_set(DISKSTATS_READS_MERGED,         ul_int,  reads_merged)
+REG_set(DISKSTATS_READ_SECTORS,         ul_int,  read_sectors)
+REG_set(DISKSTATS_READ_TIME,            ul_int,  read_time)
+REG_set(DISKSTATS_WRITES,               ul_int,  writes)
+REG_set(DISKSTATS_WRITES_MERGED,        ul_int,  writes_merged)
+REG_set(DISKSTATS_WRITE_SECTORS,        ul_int,  write_sectors)
+REG_set(DISKSTATS_WRITE_TIME,           ul_int,  write_time)
+REG_set(DISKSTATS_IO_TIME,              ul_int,  io_time)
+REG_set(DISKSTATS_IO_WTIME,             ul_int,  io_wtime)
+
+REG_set(DISKSTATS_IO_INPROGRESS,        s_int,   io_inprogress)
+
+HST_set(DISKSTATS_DELTA_READS,          s_int,   reads)
+HST_set(DISKSTATS_DELTA_READS_MERGED,   s_int,   reads_merged)
+HST_set(DISKSTATS_DELTA_READ_SECTORS,   s_int,   read_sectors)
+HST_set(DISKSTATS_DELTA_READ_TIME,      s_int,   read_time)
+HST_set(DISKSTATS_DELTA_WRITES,         s_int,   writes)
+HST_set(DISKSTATS_DELTA_WRITES_MERGED,  s_int,   writes_merged)
+HST_set(DISKSTATS_DELTA_WRITE_SECTORS,  s_int,   write_sectors)
+HST_set(DISKSTATS_DELTA_WRITE_TIME,     s_int,   write_time)
+HST_set(DISKSTATS_DELTA_IO_TIME,        s_int,   io_time)
+HST_set(DISKSTATS_DELTA_IO_WTIME,       s_int,   io_wtime)
+
+#undef setDECL
+#undef DEV_set
+#undef REG_set
+#undef HST_set
+
+
+// ___ Sorting Support ||||||||||||||||||||||||||||||||||||||||||||||||||||||||
+
+struct sort_parms {
+    int offset;
+    enum diskstats_sort_order order;
+};
+
+#define srtNAME(t) sort_results_ ## t
+#define srtDECL(t) static int srtNAME(t) \
+    (const struct diskstats_stack **A, const struct diskstats_stack **B, struct sort_parms *P)
+
+srtDECL(s_int) {
+    const struct diskstats_result *a = (*A)->head + P->offset; \
+    const struct diskstats_result *b = (*B)->head + P->offset; \
+    return P->order * (a->result.s_int - b->result.s_int);
+}
+
+srtDECL(ul_int) {
+    const struct diskstats_result *a = (*A)->head + P->offset; \
+    const struct diskstats_result *b = (*B)->head + P->offset; \
+    if ( a->result.ul_int > b->result.ul_int ) return P->order > 0 ?  1 : -1; \
+    if ( a->result.ul_int < b->result.ul_int ) return P->order > 0 ? -1 :  1; \
+    return 0;
+}
+
+srtDECL(str) {
+    const struct diskstats_result *a = (*A)->head + P->offset;
+    const struct diskstats_result *b = (*B)->head + P->offset;
+    return P->order * STR_COMPARE(a->result.str, b->result.str);
+}
+
+srtDECL(noop) { \
+    (void)A; (void)B; (void)P; \
+    return 0;
+}
+
+#undef srtDECL
+
+
+// ___ Controlling Table ||||||||||||||||||||||||||||||||||||||||||||||||||||||
+
+typedef void (*SET_t)(struct diskstats_result *, struct dev_node *);
+#define RS(e) (SET_t)setNAME(e)
+
+typedef int  (*QSR_t)(const void *, const void *, void *);
+#define QS(t) (QSR_t)srtNAME(t)
+
+        /*
+         * Need it be said?
+         * This table must be kept in the exact same order as
+         * those *enum diskstats_item* guys ! */
+static struct {
+    SET_t setsfunc;              // the actual result setting routine
+    QSR_t sortfunc;              // sort cmp func for a specific type
+} Item_table[] = {
+/*  setsfunc                            sortfunc
+    ----------------------------------  ---------  */
+  { RS(noop),                           QS(ul_int) },
+  { RS(extra),                          QS(noop)   },
+
+  { RS(DISKSTATS_NAME),                 QS(str)    },
+  { RS(DISKSTATS_TYPE),                 QS(s_int)  },
+  { RS(DISKSTATS_MAJOR),                QS(s_int)  },
+  { RS(DISKSTATS_MINOR),                QS(s_int)  },
+
+  { RS(DISKSTATS_READS),                QS(ul_int) },
+  { RS(DISKSTATS_READS_MERGED),         QS(ul_int) },
+  { RS(DISKSTATS_READ_SECTORS),         QS(ul_int) },
+  { RS(DISKSTATS_READ_TIME),            QS(ul_int) },
+  { RS(DISKSTATS_WRITES),               QS(ul_int) },
+  { RS(DISKSTATS_WRITES_MERGED),        QS(ul_int) },
+  { RS(DISKSTATS_WRITE_SECTORS),        QS(ul_int) },
+  { RS(DISKSTATS_WRITE_TIME),           QS(ul_int) },
+  { RS(DISKSTATS_IO_TIME),              QS(ul_int) },
+  { RS(DISKSTATS_IO_WTIME),             QS(ul_int) },
+
+  { RS(DISKSTATS_IO_INPROGRESS),        QS(s_int)  },
+
+  { RS(DISKSTATS_DELTA_READS),          QS(s_int)  },
+  { RS(DISKSTATS_DELTA_READS_MERGED),   QS(s_int)  },
+  { RS(DISKSTATS_DELTA_READ_SECTORS),   QS(s_int)  },
+  { RS(DISKSTATS_DELTA_READ_TIME),      QS(s_int)  },
+  { RS(DISKSTATS_DELTA_WRITES),         QS(s_int)  },
+  { RS(DISKSTATS_DELTA_WRITES_MERGED),  QS(s_int)  },
+  { RS(DISKSTATS_DELTA_WRITE_SECTORS),  QS(s_int)  },
+  { RS(DISKSTATS_DELTA_WRITE_TIME),     QS(s_int)  },
+  { RS(DISKSTATS_DELTA_IO_TIME),        QS(s_int)  },
+  { RS(DISKSTATS_DELTA_IO_WTIME),       QS(s_int)  },
+
+ // dummy entry corresponding to PROCPS_DISKSTATS_logical_end ...
+  { NULL,                               NULL       }
+};
+
+    /* please note,
+     * this enum MUST be 1 greater than the highest value of any enum */
+enum diskstats_item PROCPS_DISKSTATS_logical_end = PROCPS_DISKSTATS_DELTA_IO_WTIME + 1;
+
+#undef setNAME
+#undef srtNAME
+#undef RS
+#undef QS
+
+
+// ___ Private Functions ||||||||||||||||||||||||||||||||||||||||||||||||||||||
+// --- dev_node specific support ----------------------------------------------
+
+static struct dev_node *node_add (
+        struct procps_diskstats *info,
+        struct dev_node *this)
+{
+    struct dev_node *prev, *walk;
+
+#ifdef ALPHABETIC_NODES
+    if (!info->nodes
+    || (STR_COMPARE(this->name, info->nodes->name) < 0)) {
+        this->next = info->nodes;
+        info->nodes = this;
+        return this;
+    }
+    prev = info->nodes;
+    walk = info->nodes->next;
+    while (walk) {
+        if (STR_COMPARE(this->name, walk->name) < 0)
+            break;
+        prev = walk;
+        walk = walk->next;
+    }
+    prev->next = this;
+    this->next = walk;
+#else
+    if (!info->nodes)
+        info->nodes = this;
+    else {
+        walk = info->nodes;
+        do {
+            prev = walk;
+            walk = walk->next;
+        } while (walk);
+        prev->next = this;
+    }
+#endif
+    return this;
+} // end: node_add
+
+static void node_classify (
+        struct dev_node *this)
 {
     DIR *dirp;
     struct dirent *dent;
-    int myerrno, i;
-
-    if ((dirp = opendir(SYSBLOCK_DIR)) == NULL) {
-        myerrno = errno;
-        if (myerrno != ENOENT && myerrno != EACCES)
-            return -myerrno;
-        /* Cannot open the dir or perm denied, make all devs disks */
-        for (i=0 ; i < info->devs_used; i++)
-            info->devs[i].is_disk = 1;
-        return 0;
-    }
 
-    while((dent = readdir(dirp)) != NULL) {
-        for (i=0; i < info->devs_used; i++)
-            if (strcmp(dent->d_name, info->devs[i].name) == 0) {
-                info->devs[i].is_disk = 1;
-                break;
-            }
+    /* all disks start off as partitions. this function
+       checks /sys/block and changes a device found there
+       into a disk. if /sys/block cannot have the directory
+       read, all devices are then treated as disks. */
+    this->type = PROCPS_DISKSTATS_TYPE_PARTITION;
+
+    if (!(dirp = opendir(SYSBLOCK_DIR))) {
+        this->type = PROCPS_DISKSTATS_TYPE_DISK;
+        return;
+    }
+    while((dent = readdir(dirp))) {
+        if (strcmp(this->name, dent->d_name) == 0) {
+            this->type = PROCPS_DISKSTATS_TYPE_DISK;
+            break;
+        }
     }
     closedir(dirp);
-    return 0;
-}
+} // end: node_classify
+
 
-static void diskstat_clear(struct procps_diskstat *info)
+static struct dev_node *node_cut (
+        struct procps_diskstats *info,
+        struct dev_node *this)
 {
-    if (info == NULL)
-        return;
-    if (info->devs != NULL && info->devs_alloc > 0) {
-        memset(info->devs, 0, sizeof(struct procps_diskstat_dev)*info->devs_alloc);
-        info->devs_used = 0;
+    struct dev_node *node = info->nodes;
+
+    if (this) {
+        if (this == node) {
+            info->nodes = node->next;
+            return this;
+        }
+        do {
+            if (this == node->next) {
+                node->next = node->next->next;
+                return this;
+            }
+            node = node->next;
+        } while (node);
     }
-}
+    return NULL;
+} // end: node_cut
+
 
-static int diskdata_alloc(
-        struct procps_diskstat *info)
+static struct dev_node *node_get (
+        struct procps_diskstats *info,
+        const char *name)
 {
-    struct procps_diskstat_dev *new_disks;
-    int new_count;
+    struct dev_node *node = info->nodes;
+
+    while (node) {
+        if (strcmp(name, node->name) == 0)
+            break;
+        node = node->next;
+    }
+    if (node) {
+        /* if this disk or partition has somehow gotten stale, we'll lose
+           it and then pretend it was never actually found ...
+         [ we test against both stamps in case a 'read' was avoided ] */
+        if (node->stamped != info->old_stamp
+        && (node->stamped != info->new_stamp)) {
+            free(node_cut(info, node));
+            node = NULL;
+        }
+    }
+    return node;
+} // end: node_get
 
-    if (info == NULL)
-        return -EINVAL;
 
-    if (info->devs_used < info->devs_alloc)
+static int node_update (
+        struct procps_diskstats *info,
+        struct dev_node *source)
+{
+    struct dev_node *target = node_get(info, source->name);
+
+    if (!target) {
+        if (!(target = malloc(sizeof(struct dev_node))))
+            return -ENOMEM;
+        memcpy(target, source, sizeof(struct dev_node));
+        // let's not distort the deltas when a new node is created ...
+        memcpy(&target->old, &target->new, sizeof(struct dev_data));
+        node_classify(target);
+        node_add(info, target);
         return 0;
+    }
+    // remember history from last time around ...
+    memcpy(&source->old, &target->new, sizeof(struct dev_data));
+    // preserve some stuff from the existing node struct ...
+    source->type = target->type;
+    source->next = target->next;
+    // finally 'update' the existing node struct ...
+    memcpy(target, source, sizeof(struct dev_node));
+    return 0;
+} // end: node_update
 
-    /* Increment the allocated number of slabs */
-    new_count = info->devs_alloc * 5/4+30;
 
-    if ((new_disks = realloc(info->devs,
-                             sizeof(struct procps_diskstat_dev)*new_count)) == NULL)
-        return -ENOMEM;
-    info->devs = new_disks;
-    info->devs_alloc = new_count;
-    return 0;
-}
+// ___ Private Functions ||||||||||||||||||||||||||||||||||||||||||||||||||||||
+// --- generalized support ----------------------------------------------------
 
-static int get_disk(
-        struct procps_diskstat *info,
-        struct procps_diskstat_dev **node)
+static inline void assign_results (
+        struct diskstats_stack *stack,
+        struct dev_node *node)
 {
-    int retval;
+    struct diskstats_result *this = stack->head;
+
+    for (;;) {
+        enum diskstats_item item = this->item;
+        if (item >= PROCPS_DISKSTATS_logical_end)
+            break;
+        Item_table[item].setsfunc(this, node);
+        ++this;
+    }
+    return;
+} // end: assign_results
 
-    if (!info)
-        return -EINVAL;
 
-    if (info->devs_used == info->devs_alloc) {
-        if ((retval = diskdata_alloc(info)) < 0)
-            return retval;
+static inline void cleanup_stack (
+        struct diskstats_result *this)
+{
+    for (;;) {
+        if (this->item >= PROCPS_DISKSTATS_logical_end)
+            break;
+        if (this->item > PROCPS_DISKSTATS_noop)
+            this->result.ul_int = 0;
+        ++this;
     }
-    *node = &(info->devs[info->devs_used++]);
-    return 0;
-}
+} // end: cleanup_stack
 
-/*
- * procps_diskstat_new:
- *
- * Create a new container to hold the diskstat information
- *
- * The initial refcount is 1, and needs to be decremented
- * to release the resources of the structure.
- *
- * Returns: a new procps_diskstat container
- */
-PROCPS_EXPORT int procps_diskstat_new (
-        struct procps_diskstat **info)
+
+static inline void cleanup_stacks_all (
+        struct ext_support *this)
 {
-    struct procps_diskstat *ds;
-    ds = calloc(1, sizeof(struct procps_diskstat));
-    if (!ds)
-        return -ENOMEM;
+    struct stacks_extent *ext = this->extents;
+    int i;
+
+    while (ext) {
+        for (i = 0; ext->stacks[i]; i++)
+            cleanup_stack(ext->stacks[i]->head);
+        ext = ext->next;
+    };
+    this->dirty_stacks = 0;
+} // end: cleanup_stacks_all
+
+
+static void extents_free_all (
+        struct ext_support *this)
+{
+    while (this->extents) {
+        struct stacks_extent *p = this->extents;
+        this->extents = this->extents->next;
+        free(p);
+    };
+} // end: extents_free_all
+
+
+static inline struct diskstats_result *itemize_stack (
+        struct diskstats_result *p,
+        int depth,
+        enum diskstats_item *items)
+{
+    struct diskstats_result *p_sav = p;
+    int i;
+
+    for (i = 0; i < depth; i++) {
+        p->item = items[i];
+        p->result.ul_int = 0;
+        ++p;
+    }
+    return p_sav;
+} // end: itemize_stack
+
+
+static void itemize_stacks_all (
+        struct ext_support *this)
+{
+    struct stacks_extent *ext = this->extents;
+
+    while (ext) {
+        int i;
+        for (i = 0; ext->stacks[i]; i++)
+            itemize_stack(ext->stacks[i]->head, this->numitems, this->items);
+        ext = ext->next;
+    };
+    this->dirty_stacks = 0;
+} // end: static void itemize_stacks_all
+
+
+static inline int items_check_failed (
+        struct ext_support *this,
+        enum diskstats_item *items,
+        int numitems)
+{
+    int i;
+
+    /* if an enum is passed instead of an address of one or more enums, ol' gcc
+     * will silently convert it to an address (possibly NULL).  only clang will
+     * offer any sort of warning like the following:
+     *
+     * warning: incompatible integer to pointer conversion passing 'int' to parameter of type 'enum diskstats_item *'
+     * my_stack = procps_diskstats_select(info, PROCPS_DISKSTATS_noop, num);
+     *                                          ^~~~~~~~~~~~~~~~
+     */
+    if (numitems < 1
+    || (void *)items < (void *)(unsigned long)(2 * PROCPS_DISKSTATS_logical_end))
+        return -1;
+
+    for (i = 0; i < numitems; i++) {
+        // a diskstats_item is currently unsigned, but we'll protect our future
+        if (items[i] < 0)
+            return -1;
+        if (items[i] >= PROCPS_DISKSTATS_logical_end)
+            return -1;
+    }
 
-    ds->refcount = 1;
-    ds->diskstat_fp = NULL;
-    *info = ds;
     return 0;
-}
+} // end: items_check_failed
+
 
 /*
- * procps_diskstat_read:
+ * read_diskstats_failed:
  *
- * @info: info structure created at procps_diskstat_new
+ * @info: info structure created at procps_diskstats_new
  *
  * Read the data out of /proc/diskstats putting the information
  * into the supplied info structure
  *
  * Returns: 0 on success, negative on error
  */
-PROCPS_EXPORT int procps_diskstat_read (
-        struct procps_diskstat *info)
+static int read_diskstats_failed (
+        struct procps_diskstats *info)
 {
-    int retval;
-    char buf[DISKSTAT_LINE_LEN];
-    char devname[DISKSTAT_NAME_LEN+1];
-    struct procps_diskstat_dev *disk;
-
-    /* clear/zero structures */
-    diskstat_clear(info);
-
-    if (NULL == info->diskstat_fp &&
-        NULL == (info->diskstat_fp = fopen(DISKSTAT_FILE, "r")))
+    static const char *fmtstr = "%d %d %" STRINGIFY(DISKSTATS_NAME_LEN) \
+        "s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu";
+    char buf[DISKSTATS_LINE_LEN];
+    struct dev_node node;
+    int rc;
+
+    if (!info->diskstats_fp
+    && (!(info->diskstats_fp = fopen(DISKSTATS_FILE, "r"))))
         return -errno;
 
-    if (fseek(info->diskstat_fp, 0L, SEEK_SET) == -1)
+    if (fseek(info->diskstats_fp, 0L, SEEK_SET) == -1)
         return -errno;
 
-    while (fgets(buf, DISKSTAT_LINE_LEN, info->diskstat_fp)) {
-        if (sscanf(buf,
-                   "%*d %*d %" STRINGIFY(DISKSTAT_NAME_LEN) "s",
-                   devname) < 1) {
+    info->old_stamp = info->new_stamp;
+    info->new_stamp = time(NULL);
+
+    while (fgets(buf, DISKSTATS_LINE_LEN, info->diskstats_fp)) {
+        // clear out the soon to be 'current'values
+        memset(&node, 0, sizeof(struct dev_node));
+
+        rc = sscanf(buf, fmtstr
+            , &node.major
+            , &node.minor
+            , &node.name[0]
+            , &node.new.reads
+            , &node.new.reads_merged
+            , &node.new.read_sectors
+            , &node.new.read_time
+            , &node.new.writes
+            , &node.new.writes_merged
+            , &node.new.write_sectors
+            , &node.new.write_time
+            , &node.new.io_inprogress
+            , &node.new.io_time
+            , &node.new.io_wtime);
+
+        if (rc != 14) {
             if (errno != 0)
                 return -errno;
             return -EINVAL;
         }
-        if ((retval = get_disk(info, &disk)) < 0)
-                return retval;
-        if (sscanf(buf, "   %*d    %*d %*s %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
-                   &disk->reads, &disk->reads_merged, &disk->read_sectors,
-                   &disk->read_time,
-                   &disk->writes, &disk->writes_merged, &disk->write_sectors,
-                   &disk->write_time,
-                   &disk->io_inprogress, &disk->io_time, &disk->io_wtime) != 11) {
-            if (errno != 0)
-                return -errno;
-            return -EINVAL;
+        node.stamped = info->new_stamp;
+        if ((rc = node_update(info, &node)))
+            return rc;
+    }
+
+    return 0;
+} //end: read_diskstats_failed
+
+
+static struct stacks_extent *stacks_alloc (
+        struct ext_support *this,
+        int maxstacks)
+{
+    struct stacks_extent *p_blob;
+    struct diskstats_stack **p_vect;
+    struct diskstats_stack *p_head;
+    size_t vect_size, head_size, list_size, blob_size;
+    void *v_head, *v_list;
+    int i;
+
+    if (this == NULL || this->items == NULL)
+        return NULL;
+    if (maxstacks < 1)
+        return NULL;
+
+    vect_size  = sizeof(void *) * maxstacks;                      // size of the addr vectors |
+    vect_size += sizeof(void *);                                  // plus NULL addr delimiter |
+    head_size  = sizeof(struct diskstats_stack);                  // size of that head struct |
+    list_size  = sizeof(struct diskstats_result) * this->numitems;// any single results stack |
+    blob_size  = sizeof(struct stacks_extent);                    // the extent anchor itself |
+    blob_size += vect_size;                                       // plus room for addr vects |
+    blob_size += head_size * maxstacks;                           // plus room for head thing |
+    blob_size += list_size * maxstacks;                           // plus room for our stacks |
+
+    /* note: all of our memory is allocated in a single blob, facilitating some later free(). |
+             as a minimum, it is important that all those result structs themselves always be |
+             contiguous within every stack since they will be accessed via relative position. | */
+    if (NULL == (p_blob = calloc(1, blob_size)))
+        return NULL;
+
+    p_blob->next = this->extents;                                 // push this extent onto... |
+    this->extents = p_blob;                                       // ...some existing extents |
+    p_vect = (void *)p_blob + sizeof(struct stacks_extent);       // prime our vector pointer |
+    p_blob->stacks = p_vect;                                      // set actual vectors start |
+    v_head = (void *)p_vect + vect_size;                          // prime head pointer start |
+    v_list = v_head + (head_size * maxstacks);                    // prime our stacks pointer |
+
+    for (i = 0; i < maxstacks; i++) {
+        p_head = (struct diskstats_stack *)v_head;
+        p_head->head = itemize_stack((struct diskstats_result *)v_list, this->numitems, this->items);
+        p_blob->stacks[i] = p_head;
+        v_list += list_size;
+        v_head += head_size;
+    }
+    p_blob->ext_numstacks = maxstacks;
+    return p_blob;
+} // end: stacks_alloc
+
+
+static int stacks_fetch (
+        struct procps_diskstats *info)
+{
+ #define n_alloc  info->fetch.n_alloc
+ #define n_inuse  info->fetch.n_inuse
+ #define n_saved  info->fetch.n_alloc_save
+    struct stacks_extent *ext;
+    struct dev_node *node;
+
+    // initialize stuff -----------------------------------
+    if (!info->fetch.anchor) {
+        if (!(info->fetch.anchor = calloc(sizeof(void *), STACKS_INCR)))
+            return -ENOMEM;
+        n_alloc = STACKS_INCR;
+    }
+    if (!info->fetch_ext.extents) {
+        if (!(ext = stacks_alloc(&info->fetch_ext, n_alloc)))
+            return -ENOMEM;
+        memset(info->fetch.anchor, 0, sizeof(void *) * n_alloc);
+        memcpy(info->fetch.anchor, ext->stacks, sizeof(void *) * n_alloc);
+        itemize_stacks_all(&info->fetch_ext);
+    }
+    cleanup_stacks_all(&info->fetch_ext);
+
+    // iterate stuff --------------------------------------
+    n_inuse = 0;
+    node = info->nodes;
+    while (node) {
+        if (!(n_inuse < n_alloc)) {
+            n_alloc += STACKS_INCR;
+            if ((!(info->fetch.anchor = realloc(info->fetch.anchor, sizeof(void *) * n_alloc)))
+            || (!(ext = stacks_alloc(&info->fetch_ext, STACKS_INCR))))
+                return -1;
+            memcpy(info->fetch.anchor + n_inuse, ext->stacks, sizeof(void *) * STACKS_INCR);
         }
-        disk->is_disk = 0; /* default to no until we scan */
-        strcpy(disk->name, devname);
+        assign_results(info->fetch.anchor[n_inuse], node);
+        ++n_inuse;
+        node = node->next;
+    }
+
+    // finalize stuff -------------------------------------
+    /* note: we go to this trouble of maintaining a duplicate of the consolidated |
+             extent stacks addresses represented as our 'anchor' since these ptrs |
+             are exposed to a user (um, not that we don't trust 'em or anything). |
+             plus, we can NULL delimit these ptrs which we couldn't do otherwise. | */
+    if (n_saved < n_inuse + 1) {
+        n_saved = n_inuse + 1;
+        if (!(info->fetch.results.stacks = realloc(info->fetch.results.stacks, sizeof(void *) * n_saved)))
+            return -ENOMEM;
+    }
+    memcpy(info->fetch.results.stacks, info->fetch.anchor, sizeof(void *) * n_inuse);
+    info->fetch.results.stacks[n_inuse] = NULL;
+    info->fetch.results.total = n_inuse;
+
+    return n_inuse;
+ #undef n_alloc
+ #undef n_inuse
+ #undef n_saved
+} // end: stacks_fetch
+
+
+static int stacks_reconfig_maybe (
+        struct ext_support *this,
+        enum diskstats_item *items,
+        int numitems)
+{
+    if (items_check_failed(this, items, numitems))
+        return -EINVAL;
+    /* is this the first time or have things changed since we were last called?
+       if so, gotta' redo all of our stacks stuff ... */
+    if (this->numitems != numitems + 1
+    || memcmp(this->items, items, sizeof(enum diskstats_item) * numitems)) {
+        // allow for our PROCPS_DISKSTATS_logical_end
+        if (!(this->items = realloc(this->items, sizeof(enum diskstats_item) * (numitems + 1))))
+            return -ENOMEM;
+        memcpy(this->items, items, sizeof(enum diskstats_item) * numitems);
+        this->items[numitems] = PROCPS_DISKSTATS_logical_end;
+        this->numitems = numitems + 1;
+        if (this->extents)
+            extents_free_all(this);
+        return 1;
     }
-    if ((retval = scan_for_disks(info)) < 0)
-        return retval;
     return 0;
-}
+} // end: stacks_reconfig_maybe
+
+
+// ___ Public Functions |||||||||||||||||||||||||||||||||||||||||||||||||||||||
+
+// --- standard required functions --------------------------------------------
 
-PROCPS_EXPORT int procps_diskstat_ref (
-        struct procps_diskstat *info)
+/*
+ * procps_diskstats_new():
+ *
+ * @info: location of returned new structure
+ *
+ * Returns: < 0 on failure, 0 on success along with
+ *          a pointer to a new context struct
+ */
+PROCPS_EXPORT int procps_diskstats_new (
+        struct procps_diskstats **info)
 {
+    struct procps_diskstats *p;
+    int rc;
+
     if (info == NULL)
         return -EINVAL;
+
+    if (!(p = calloc(1, sizeof(struct procps_diskstats))))
+        return -ENOMEM;
+
+    p->refcount = 1;
+
+    /* do a priming read here for the following potential benefits: |
+         1) ensure there will be no problems with subsequent access |
+         2) make delta results potentially useful, even if 1st time | */
+    if ((rc = read_diskstats_failed(p))) {
+        procps_diskstats_unref(&p);
+        return rc;
+    }
+
+    *info = p;
+    return 0;
+} // end: procps_diskstats_new
+
+
+PROCPS_EXPORT int procps_diskstats_ref (
+        struct procps_diskstats *info)
+{
+    if (info == NULL)
+        return -EINVAL;
+
     info->refcount++;
     return info->refcount;
-}
+} // end: procps_diskstats_ref
+
 
-PROCPS_EXPORT int procps_diskstat_unref (
-        struct procps_diskstat **info)
+PROCPS_EXPORT int procps_diskstats_unref (
+        struct procps_diskstats **info)
 {
+    struct dev_node *node;
+
     if (info == NULL || *info == NULL)
         return -EINVAL;
+
     (*info)->refcount--;
     if ((*info)->refcount == 0) {
+        if ((*info)->diskstats_fp) {
+            fclose((*info)->diskstats_fp);
+            (*info)->diskstats_fp = NULL;
+        }
+        node = (*info)->nodes;
+        while (node) {
+            struct dev_node *p = node;
+            node = p->next;
+            free(p);
+        }
+        if ((*info)->select_ext.extents)
+            extents_free_all((&(*info)->select_ext));
+        if ((*info)->select_ext.items)
+            free((*info)->select_ext.items);
+
+        if ((*info)->fetch.anchor)
+            free((*info)->fetch.anchor);
+        if ((*info)->fetch.results.stacks)
+            free((*info)->fetch.results.stacks);
+
+        if ((*info)->fetch_ext.extents)
+            extents_free_all(&(*info)->fetch_ext);
+        if ((*info)->fetch_ext.items)
+            free((*info)->fetch_ext.items);
+
         free(*info);
         *info = NULL;
+
         return 0;
     }
     return (*info)->refcount;
-}
+} // end: procps_diskstats_unref
 
-/*
- * procps_diskstat_dev_count:
+
+// --- variable interface functions -------------------------------------------
+
+PROCPS_EXPORT struct diskstats_result *procps_diskstats_get (
+        struct procps_diskstats *info,
+        const char *name,
+        enum diskstats_item item)
+{
+    struct dev_node *node;
+    time_t cur_secs;
+
+    if (info == NULL)
+        return NULL;
+    if (item < 0 || item >= PROCPS_DISKSTATS_logical_end)
+        return NULL;
+
+    /* we will NOT read the diskstat file with every call - rather, we'll offer
+       a granularity of 1 second between reads ... */
+    cur_secs = time(NULL);
+    if (1 <= cur_secs - info->new_stamp) {
+        if (read_diskstats_failed(info))
+            return NULL;
+    }
+
+    info->get_this.item = item;
+//  with 'get', we must NOT honor the usual 'noop' guarantee
+//  if (item > PROCPS_DISKSTATS_noop)
+        info->get_this.result.ul_int = 0;
+
+    if (!(node = node_get(info, name)))
+        return NULL;
+    Item_table[item].setsfunc(&info->get_this, node);
+
+    return &info->get_this;
+} // end: procps_diskstats_get
+
+
+/* procps_diskstats_reap():
  *
- * @info: structure that has been read
+ * Harvest all the requested disks information providing
+ * the result stacks along with the total number of harvested.
  *
- * Returns: number of devices (disk+partitions)
+ * Returns: pointer to a diskstats_reap struct on success, NULL on error.
  */
-PROCPS_EXPORT int procps_diskstat_dev_count (
-        const struct procps_diskstat *info)
+PROCPS_EXPORT struct diskstats_reap *procps_diskstats_reap (
+        struct procps_diskstats *info,
+        enum diskstats_item *items,
+        int numitems)
 {
-    if (!info)
-        return -EINVAL;
-    return info->devs_used;
-}
+    if (info == NULL || items == NULL)
+        return NULL;
 
+    if (0 > stacks_reconfig_maybe(&info->fetch_ext, items, numitems))
+        return NULL;
 
-/*
- * procps_diskstat_dev_getbyname
- *
- * @info: diskstat data already read
- * @name: name of partition
+    if (info->fetch_ext.dirty_stacks)
+        cleanup_stacks_all(&info->fetch_ext);
+
+    if (read_diskstats_failed(info))
+        return NULL;
+    stacks_fetch(info);
+    info->fetch_ext.dirty_stacks = 1;
+
+    return &info->fetch.results;
+} // end: procps_diskstats_reap
+
+
+/* procps_diskstats_select():
  *
- * Find the device index by the name
+ * Obtain all the requested disk/partition information then return
+ * it in a single library provided results stack.
  *
- * Returns: devid if found
- *  -1 if not found
+ * Returns: pointer to a diskstats_stack struct on success, NULL on error.
  */
-PROCPS_EXPORT int procps_diskstat_dev_getbyname(
-        const struct procps_diskstat *info,
-        const char *name)
+PROCPS_EXPORT struct diskstats_stack *procps_diskstats_select (
+        struct procps_diskstats *info,
+        const char *name,
+        enum diskstats_item *items,
+        int numitems)
 {
-    int i;
+    struct dev_node *node;
 
-    if (info == NULL || info->devs_used == 0)
-        return -1;
-    for (i=0; i < info->devs_used; i++)
-        if (strcmp(info->devs[i].name, name) == 0)
-            return i;
-    return -1;
-}
+    if (info == NULL || items == NULL)
+        return NULL;
 
+    if (0 > stacks_reconfig_maybe(&info->select_ext, items, numitems))
+        return NULL;
 
-PROCPS_EXPORT unsigned long procps_diskstat_dev_get(
-        const struct procps_diskstat *info,
-        const enum procps_diskstat_devitem item,
-        const unsigned int devid)
-{
-    if (info == NULL)
-        return 0;
-    if (devid > info->devs_used)
-        return 0;
-    switch(item) {
-    case PROCPS_DISKSTAT_READS:
-        return info->devs[devid].reads;
-    case PROCPS_DISKSTAT_READS_MERGED:
-        return info->devs[devid].reads_merged;
-    case PROCPS_DISKSTAT_READ_SECTORS:
-        return info->devs[devid].read_sectors;
-    case PROCPS_DISKSTAT_READ_TIME:
-        return info->devs[devid].read_time;
-    case PROCPS_DISKSTAT_WRITES:
-        return info->devs[devid].writes;
-    case PROCPS_DISKSTAT_WRITES_MERGED:
-        return info->devs[devid].writes_merged;
-    case PROCPS_DISKSTAT_WRITE_SECTORS:
-        return info->devs[devid].write_sectors;
-    case PROCPS_DISKSTAT_WRITE_TIME:
-        return info->devs[devid].write_time;
-    case PROCPS_DISKSTAT_IO_INPROGRESS:
-        return info->devs[devid].io_inprogress;
-    case PROCPS_DISKSTAT_IO_TIME:
-        return info->devs[devid].io_time;
-    case PROCPS_DISKSTAT_IO_WTIME:
-        return info->devs[devid].io_wtime;
-    default:
-        return 0;
-    }
-}
+    if (!info->select_ext.extents
+    && !(stacks_alloc(&info->select_ext, 1)))
+       return NULL;
 
-PROCPS_EXPORT char* procps_diskstat_dev_getname(
-        const struct procps_diskstat *info,
-        const unsigned int devid)
-{
-    if (info == NULL)
-        return 0;
-    if (devid > info->devs_used)
-        return 0;
-    return info->devs[devid].name;
-}
+    if (info->select_ext.dirty_stacks)
+        cleanup_stacks_all(&info->select_ext);
+
+    if (read_diskstats_failed(info))
+        return NULL;
+    if (!(node = node_get(info, name)))
+        return NULL;
+
+    assign_results(info->select_ext.extents->stacks[0], node);
+    info->select_ext.dirty_stacks = 1;
+
+    return info->select_ext.extents->stacks[0];
+} // end: procps_diskstats_select
 
-PROCPS_EXPORT int procps_diskstat_dev_isdisk(
-        const struct procps_diskstat *info,
-        const unsigned int devid)
+
+/*
+ * procps_diskstats_sort():
+ *
+ * Sort stacks anchored in the passed stack pointers array
+ * based on the designated sort enumerator and specified order.
+ *
+ * Returns those same addresses sorted.
+ *
+ * Note: all of the stacks must be homogeneous (of equal length and content).
+ */
+PROCPS_EXPORT struct diskstats_stack **procps_diskstats_sort (
+        struct procps_diskstats *info,
+        struct diskstats_stack *stacks[],
+        int numstacked,
+        enum diskstats_item sortitem,
+        enum diskstats_sort_order order)
 {
-    if (info == NULL || devid > info->devs_used)
-        return -EINVAL;
+    struct diskstats_result *p;
+    struct sort_parms parms;
+    int offset;
+
+    if (info == NULL || stacks == NULL)
+        return NULL;
+
+    // a diskstats_item is currently unsigned, but we'll protect our future
+    if (sortitem < 0 || sortitem >= PROCPS_DISKSTATS_logical_end)
+        return NULL;
+    if (order != PROCPS_DISKSTATS_SORT_ASCEND && order != PROCPS_DISKSTATS_SORT_DESCEND)
+        return NULL;
+    if (numstacked < 2)
+        return stacks;
+
+    offset = 0;
+    p = stacks[0]->head;
+    for (;;) {
+        if (p->item == sortitem)
+            break;
+        ++offset;
+        if (p->item == PROCPS_DISKSTATS_logical_end)
+            return NULL;
+        ++p;
+    }
+    parms.offset = offset;
+    parms.order = order;
 
-    return info->devs[devid].is_disk;
-}
+    qsort_r(stacks, numstacked, sizeof(void *), (QSR_t)Item_table[p->item].sortfunc, &parms);
+    return stacks;
+} // end: procps_diskstats_sort
index 40df3c072cbfb5e034ff3cab1c4445664a1fd1a5..1ccb5cda59f4b5720d276a179894b2c73bc170d8 100644 (file)
@@ -1,9 +1,10 @@
 /*
- * diskstat - Disk statistics - part of procps
+ * diskstats - Disk statistics - part of procps
  *
  * Copyright (c) 2003 Fabian Frederick
  * Copyright (C) 2003 Albert Cahalan
  * Copyright (C) 2015 Craig Small <csmall@enc.com.au>
+ * Copyright (C) 2016 Jim Warner <james.warner@comcast.net>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
  * License along with this library; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
  */
-#ifndef PROC_DISKSTAT_H
-#define PROC_DISKSTAT_H
+#ifndef PROC_DISKSTATS_H
+#define PROC_DISKSTATS_H
 
 #include <sys/cdefs.h>
 
 __BEGIN_DECLS
 
-enum procps_diskstat_devitem {
-    PROCPS_DISKSTAT_READS,
-    PROCPS_DISKSTAT_READS_MERGED,
-    PROCPS_DISKSTAT_READ_SECTORS,
-    PROCPS_DISKSTAT_READ_TIME,
-    PROCPS_DISKSTAT_WRITES,
-    PROCPS_DISKSTAT_WRITES_MERGED,
-    PROCPS_DISKSTAT_WRITE_SECTORS,
-    PROCPS_DISKSTAT_WRITE_TIME,
-    PROCPS_DISKSTAT_IO_INPROGRESS,
-    PROCPS_DISKSTAT_IO_TIME,
-    PROCPS_DISKSTAT_IO_WTIME
+enum diskstats_item {
+    PROCPS_DISKSTATS_noop,                 //        ( never altered )
+    PROCPS_DISKSTATS_extra,                //        ( reset to zero )
+
+    PROCPS_DISKSTATS_NAME,                 //    str
+    PROCPS_DISKSTATS_TYPE,                 //  s_int
+    PROCPS_DISKSTATS_MAJOR,                //  s_int
+    PROCPS_DISKSTATS_MINOR,                //  s_int
+
+    PROCPS_DISKSTATS_READS,                // ul_int
+    PROCPS_DISKSTATS_READS_MERGED,         // ul_int
+    PROCPS_DISKSTATS_READ_SECTORS,         // ul_int
+    PROCPS_DISKSTATS_READ_TIME,            // ul_int
+    PROCPS_DISKSTATS_WRITES,               // ul_int
+    PROCPS_DISKSTATS_WRITES_MERGED,        // ul_int
+    PROCPS_DISKSTATS_WRITE_SECTORS,        // ul_int
+    PROCPS_DISKSTATS_WRITE_TIME,           // ul_int
+    PROCPS_DISKSTATS_IO_TIME,              // ul_int
+    PROCPS_DISKSTATS_IO_WTIME,             // ul_int
+
+    PROCPS_DISKSTATS_IO_INPROGRESS,        //  s_int
+
+    PROCPS_DISKSTATS_DELTA_READS,          //  s_int
+    PROCPS_DISKSTATS_DELTA_READS_MERGED,   //  s_int
+    PROCPS_DISKSTATS_DELTA_READ_SECTORS,   //  s_int
+    PROCPS_DISKSTATS_DELTA_READ_TIME,      //  s_int
+    PROCPS_DISKSTATS_DELTA_WRITES,         //  s_int
+    PROCPS_DISKSTATS_DELTA_WRITES_MERGED,  //  s_int
+    PROCPS_DISKSTATS_DELTA_WRITE_SECTORS,  //  s_int
+    PROCPS_DISKSTATS_DELTA_WRITE_TIME,     //  s_int
+    PROCPS_DISKSTATS_DELTA_IO_TIME,        //  s_int
+    PROCPS_DISKSTATS_DELTA_IO_WTIME        //  s_int
+};
+
+enum diskstats_sort_order {
+    PROCPS_DISKSTATS_SORT_ASCEND   = +1,
+    PROCPS_DISKSTATS_SORT_DESCEND  = -1
+};
+
+
+struct diskstats_result {
+    enum diskstats_item item;
+    union {
+        signed int     s_int;
+        unsigned long  ul_int;
+        char          *str;
+    } result;
 };
 
-struct procps_diskstat;
-struct procps_diskstat_dev;
+struct diskstats_stack {
+    struct diskstats_result *head;
+};
+
+struct diskstats_reap {
+    int total;
+    struct diskstats_stack **stacks;
+};
+
+
+#define PROCPS_DISKSTATS_TYPE_DISK       -11111
+#define PROCPS_DISKSTATS_TYPE_PARTITION  -22222
+
+#define PROCPS_DISKSTATS_GET( diskstats, actual_enum, type ) \
+    procps_diskstats_get( diskstats, actual_enum ) -> result . type
+
+#define PROCPS_DISKSTATS_VAL( relative_enum, type, stack) \
+    stack -> head [ relative_enum ] . result . type
+
+
+struct procps_diskstats;
 
-int procps_diskstat_new (struct procps_diskstat **info);
-int procps_diskstat_read (struct procps_diskstat *info);
+int procps_diskstats_new   (struct procps_diskstats **info);
+int procps_diskstats_ref   (struct procps_diskstats  *info);
+int procps_diskstats_unref (struct procps_diskstats **info);
 
-int procps_diskstat_ref (struct procps_diskstat *info);
-int procps_diskstat_unref (struct procps_diskstat **info);
+struct diskstats_result *procps_diskstats_get (
+    struct procps_diskstats *info,
+    const char *name,
+    enum diskstats_item item);
 
-int procps_diskstat_dev_count (const struct procps_diskstat *info);
-int procps_diskstat_dev_getbyname(
-        const struct procps_diskstat *info,
-        const char *name);
+struct diskstats_reap *procps_diskstats_reap (
+    struct procps_diskstats *info,
+    enum diskstats_item *items,
+    int numitems);
 
-unsigned long procps_diskstat_dev_get(
-        const struct procps_diskstat *info,
-        const enum procps_diskstat_devitem item,
-        const unsigned int devid);
+struct diskstats_stack *procps_diskstats_select (
+    struct procps_diskstats *info,
+    const char *name,
+    enum diskstats_item *items,
+    int numitems);
 
-char* procps_diskstat_dev_getname(
-        const struct procps_diskstat *info,
-        const unsigned int devid);
+struct diskstats_stack **procps_diskstats_sort (
+    struct procps_diskstats *info,
+    struct diskstats_stack *stacks[],
+    int numstacked,
+    enum diskstats_item sortitem,
+    enum diskstats_sort_order order);
 
-int procps_diskstat_dev_isdisk(
-        const struct procps_diskstat *info,
-        const unsigned int devid);
 __END_DECLS
 #endif
index 68ff59c76a7123480516b6d2c4b5cdfa01326bdf..10f3f005b7c24a1f920444e7574bd43d336ffe90 100644 (file)
@@ -3,15 +3,13 @@ global:
        escape_str;
        fatal_proc_unmounted;
        procps_cpu_count;
-       procps_diskstat_dev_count;
-       procps_diskstat_dev_get;
-       procps_diskstat_dev_getbyname;
-       procps_diskstat_dev_getname;
-       procps_diskstat_dev_isdisk;
-       procps_diskstat_new;
-       procps_diskstat_read;
-       procps_diskstat_ref;
-       procps_diskstat_unref;
+       procps_diskstats_new;
+       procps_diskstats_ref;
+       procps_diskstats_unref;
+       procps_diskstats_get;
+       procps_diskstats_reap;
+       procps_diskstats_select;
+       procps_diskstats_sort;
        procps_hertz_get;
        procps_linux_version;
        procps_loadavg;