]> granicus.if.org Git - procps-ng/commitdiff
library: revised sort + new allocate APIs for slabinfo
authorJim Warner <james.warner@comcast.net>
Sat, 11 Jul 2015 05:00:00 +0000 (00:00 -0500)
committerCraig Small <csmall@enc.com.au>
Tue, 14 Jul 2015 12:36:07 +0000 (22:36 +1000)
With this patch, we will be close to an implementation
which will be needed when accommodating tasks/threads.
The following explanation was from an earlier message:

The slabtop requirements are similar to those of PIDs.
One must accommodate the variable number of slab nodes
(PIDs) while also accepting different data (char * and
unsigned long). Furthermore, some generalized means to
sort all that acquired stuff must somehow be provided.
------------------------------------------------------

So this patch expands the API to provide dynamic chain
allocation plus allow sorting of those dynamic chains.
While specific to slab needs (nodes, not global stats)
it is not too early to begin to think of newlib chains
as the opaque replacement for a deprecated old proc_t.

Better yet, any newlib chain is inherently variable in
length, something the old proc_t couldn't claim to be.
Of course, as we get to PIDs we'll want to grow/shrink
chains (easily accomplished with a special item enum).
And we'll want to grow/shrink those **head arrays too.
But these minor details don't seem insurmountable now.

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

index efacdf97ff64150322d58e1d5539851a9f05179c..0125a55ef2652077121460b5ca1816ebe87faebc 100644 (file)
@@ -46,13 +46,17 @@ global:
        procps_slabinfo_read;
        procps_slabinfo_ref;
        procps_slabinfo_unref;
-       procps_slabinfo_stat_get;
-       procps_slabinfo_stat_getchain;
-       procps_slabinfo_sort;
-       procps_slabinfo_node_count;
-       procps_slabinfo_node_get;
-       procps_slabinfo_node_getchain;
-       procps_slabinfo_node_getname;
+       procps_slabs_get;
+       procps_slabs_getchain;
+       procps_slabnode_count;
+       procps_slabnode_getname;
+       procps_slabnode_get;
+       procps_slabnode_getchain;
+       procps_slabnode_chain_fill;
+       procps_slabnode_chain_alloc;
+       procps_slabnode_chains_fill;
+       procps_slabnode_chains_sort;
+       procps_slabnode_chains_alloc;
        procps_stat_new;
        procps_stat_read;
        procps_stat_read_jiffs;
index a1e26c40010418971b093284658faa27416dc7cd..3c39feda34bfa8ad6cad88a45aa77d4790742762 100644 (file)
@@ -6,6 +6,7 @@
  *
  * Copyright (C) 2003 Chris Rivera
  * Copyright 2004, Albert Cahalan
+ * Copyright (C) 2015 Craig Small <csmall@enc.com.au>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Lesser General Public
 #include <sys/stat.h>
 #include <fcntl.h>
 
-#include "slab.h"
+#include <proc/slab.h>
 #include "procps-private.h"
 
-#define SLABINFO_LINE_LEN    2048
-#define SLABINFO_VER_LEN    100
-#define SLAB_INFO_NAME_LEN      128
 #define SLABINFO_FILE        "/proc/slabinfo"
-#define INITIAL_NODES       30
+#define SLABINFO_LINE_LEN    2048
+#define SLAB_INFO_NAME_LEN   128
 
-struct procps_slabnode {
+struct slabinfo_node {
     char name[SLAB_INFO_NAME_LEN];  /* name of this cache */
     unsigned long cache_size;       /* size of entire cache */
     unsigned nr_objs;               /* number of objects in this cache */
@@ -55,7 +54,6 @@ struct procps_slabnode {
     unsigned use;                   /* percent full: total / active */
 };
 
-
 struct slabinfo_stats {
     unsigned long total_size;       /* size of all objects */
     unsigned long active_size;      /* size of all active objects */
@@ -75,29 +73,45 @@ struct procps_slabinfo {
     int refcount;
     FILE *slabinfo_fp;
     struct slabinfo_stats stats;
-    struct procps_slabnode *nodes;  /* first slabnode of this list */
+    struct slabinfo_node *nodes;    /* first slabnode of this list */
     int nodes_alloc;                /* nodes alloc()ed */
     int nodes_used;                 /* nodes using alloced memory */
+    struct chains_anchor *chained;
+};
+
+struct chain_vectors {
+    struct chains_anchor *owner;
+    struct slabnode_chain **heads;
+};
+
+struct chains_anchor {
+    int depth;
+    int inuse;
+    int header_size;
+    struct chain_vectors *vectors;
+    struct chains_anchor *self;
+    struct chains_anchor *next;
 };
 
+
 /*
  * Zero out the slabnode data, keeping the memory allocated.
  */
-static void slabnodes_clear(
+static void slabnodes_clear (
         struct procps_slabinfo *info)
 {
     if (info == NULL || info->nodes == NULL || info->nodes_alloc < 1)
         return;
-    memset(info->nodes, 0, sizeof(struct procps_slabnode)*info->nodes_alloc);
+    memset(info->nodes, 0, sizeof(struct slabinfo_node)*info->nodes_alloc);
     info->nodes_used = 0;
 }
 
 /* Alloc up more slabnode memory, if required
  */
-static int slabnodes_alloc(
+static int slabnodes_alloc (
         struct procps_slabinfo *info)
 {
-    struct procps_slabnode *new_nodes;
+    struct slabinfo_node *new_nodes;
     int new_count;
 
     if (info == NULL)
@@ -108,8 +122,8 @@ static int slabnodes_alloc(
     /* Increment the allocated number of slabs */
     new_count = info->nodes_alloc * 5/4+30;
 
-    if ((new_nodes = realloc(info->nodes,
-                             sizeof(struct procps_slabnode)*new_count)) == NULL)
+    new_nodes = realloc(info->nodes, sizeof(struct slabinfo_node) * new_count);
+    if (!new_nodes)
         return -ENOMEM;
     info->nodes = new_nodes;
     info->nodes_alloc = new_count;
@@ -124,9 +138,9 @@ static int slabnodes_alloc(
  * both for simplicity and because the number of slab caches is fairly
  * constant.
  */
-static int get_slabnode(
+static int get_slabnode (
         struct procps_slabinfo *info,
-        struct procps_slabnode **node)
+        struct slabinfo_node **node)
 {
     int retval;
 
@@ -173,10 +187,10 @@ static int get_slabnode(
  *  : globalstat <listallocs> <maxobjs> <grown> <reaped> <error> <maxfreeable> <freelimit> \
  *  : cpustat <allochit> <allocmiss> <freehit> <freemiss>
  */
-static int parse_slabinfo20(
+static int parse_slabinfo20 (
         struct procps_slabinfo *info)
 {
-    struct procps_slabnode *node;
+    struct slabinfo_node *node;
     char buffer[SLABINFO_LINE_LEN];
     int retval;
     int page_size = getpagesize();
@@ -205,6 +219,9 @@ static int parse_slabinfo20(
             return -EINVAL;
         }
 
+        if (!node->name[0])
+            snprintf(node->name, sizeof(node->name), "%s", "unknown");
+
         if (node->obj_size < stats->min_obj_size)
             stats->min_obj_size = node->obj_size;
         if (node->obj_size > stats->max_obj_size)
@@ -236,81 +253,13 @@ static int parse_slabinfo20(
 }
 
 /*
- * parse_slabinfo11 - actual parsing routine for slabinfo 1.1 (2.4 kernels)
- */
-static int parse_slabinfo11(
-        struct procps_slabinfo *info)
-{
-    struct procps_slabnode *node;
-    char buffer[SLABINFO_LINE_LEN];
-    int retval;
-    int page_size = getpagesize();
-    struct slabinfo_stats *stats = &(info->stats);
-
-    stats->min_obj_size = INT_MAX;
-    stats->max_obj_size = 0;
-
-    while (fgets(buffer, SLABINFO_LINE_LEN, info->slabinfo_fp )) {
-        if (buffer[0] == '#')
-            continue;
-
-        if ((retval = get_slabnode(info, &node)) < 0)
-            return retval;
-
-        if (sscanf(buffer,
-                   "%" STRINGIFY(SLAB_INFO_NAME_LEN)
-                   "s %d %d %d %d %d %d",
-                   node->name, &node->nr_active_objs,
-                   &node->nr_objs, &node->obj_size,
-                   &node->nr_active_slabs, &node->nr_slabs,
-                   &node->pages_per_slab) < 6) {
-            if (errno != 0)
-                return -errno;
-            return -EINVAL;
-        }
-
-        if (node->obj_size < stats->min_obj_size)
-            stats->min_obj_size = node->obj_size;
-        if (node->obj_size > stats->max_obj_size)
-            stats->max_obj_size = node->obj_size;
-
-        node->cache_size = (unsigned long)node->nr_slabs *
-            node->pages_per_slab * page_size;
-
-        if (node->nr_objs) {
-            node->use = 100 * node->nr_active_objs / node->nr_objs;
-            stats->nr_active_caches++;
-        } else
-            node->use = 0;
-
-        if (node->obj_size)
-            node->objs_per_slab = node->pages_per_slab *
-                    page_size / node->obj_size;
-
-        stats->nr_objs += node->nr_objs;
-        stats->nr_active_objs += node->nr_active_objs;
-        stats->total_size += (unsigned long)node->nr_objs * node->obj_size;
-        stats->active_size += (unsigned long)node->nr_active_objs * node->obj_size;
-        stats->nr_pages += node->nr_slabs * node->pages_per_slab;
-        stats->nr_slabs += node->nr_slabs;
-        stats->nr_active_slabs += node->nr_active_slabs;
-        stats->nr_caches++;
-    }
-
-    if (stats->nr_objs)
-        stats->avg_obj_size = stats->total_size / stats->nr_objs;
-
-    return 0;
-}
-
-/*
- * procps_slabinfo_new:
+ * procps_slabinfo_new():
  *
  * @info: location of returned new structure
  *
  * Returns: 0 on success <0 on failure
  */
-PROCPS_EXPORT int procps_slabinfo_new(
+PROCPS_EXPORT int procps_slabinfo_new (
         struct procps_slabinfo **info)
 {
     struct procps_slabinfo *si;
@@ -331,7 +280,7 @@ PROCPS_EXPORT int procps_slabinfo_new(
     return 0;
 }
 
-/* procps_slabinfo_read:
+/* procps_slabinfo_read():
  *
  * Read the data out of /proc/slabinfo putting the information
  * into the supplie info container
@@ -342,7 +291,7 @@ PROCPS_EXPORT int procps_slabinfo_read (
         struct procps_slabinfo *info)
 {
     char line[SLABINFO_LINE_LEN];
-    int retval, size, major, minor;
+    int retval, major, minor;
 
     if (info == NULL)
         return -1;
@@ -350,11 +299,13 @@ PROCPS_EXPORT int procps_slabinfo_read (
     memset(&(info->stats), 0, sizeof(struct slabinfo_stats));
     if ((retval = slabnodes_alloc(info)) < 0)
         return retval;
+
     slabnodes_clear(info);
 
     if (NULL == info->slabinfo_fp &&
         (info->slabinfo_fp = fopen(SLABINFO_FILE, "r")) == NULL)
         return -errno;
+
     if (fseek(info->slabinfo_fp, 0L, SEEK_SET) < 0)
         return -errno;
 
@@ -367,10 +318,9 @@ PROCPS_EXPORT int procps_slabinfo_read (
 
     if (major == 2)
         retval = parse_slabinfo20(info);
-    else if (major == 1 && minor == 1)
-        retval = parse_slabinfo11(info);
     else
         return -ERANGE;
+
     return retval;
 }
 
@@ -394,6 +344,14 @@ PROCPS_EXPORT int procps_slabinfo_unref (
             fclose((*info)->slabinfo_fp);
             (*info)->slabinfo_fp = NULL;
         }
+        if ((*info)->chained) {
+            do {
+                struct chains_anchor *p = (*info)->chained;
+                (*info)->chained = (*info)->chained->next;
+                free(p);
+            } while((*info)->chained);
+        }
+        free((*info)->nodes);
         free(*info);
         *info = NULL;
         return 0;
@@ -401,96 +359,99 @@ PROCPS_EXPORT int procps_slabinfo_unref (
     return (*info)->refcount;
 }
 
-PROCPS_EXPORT unsigned long procps_slabinfo_stat_get(
+PROCPS_EXPORT unsigned long procps_slabs_get (
         struct procps_slabinfo *info,
-        enum procps_slabinfo_stat item)
+        enum slabs_item item)
 {
-    if (!item)
-        return 0;
+    if (info == NULL)
+        return -EINVAL;
+
     switch (item) {
-    case PROCPS_SLABINFO_OBJS:
-        return info->stats.nr_objs;
-    case PROCPS_SLABINFO_AOBJS:
-        return info->stats.nr_active_objs;
-    case PROCPS_SLABINFO_PAGES:
-        return info->stats.nr_pages;
-    case PROCPS_SLABINFO_SLABS:
-        return info->stats.nr_slabs;
-    case PROCPS_SLABINFO_ASLABS:
-        return info->stats.nr_active_slabs;
-    case PROCPS_SLABINFO_CACHES:
-        return info->stats.nr_caches;
-    case PROCPS_SLABINFO_ACACHES:
-        return info->stats.nr_active_caches;
-    case PROCPS_SLABINFO_SIZE_AVG:
-        return info->stats.avg_obj_size;
-    case PROCPS_SLABINFO_SIZE_MIN:
-        return info->stats.min_obj_size;
-    case PROCPS_SLABINFO_SIZE_MAX:
-        return info->stats.max_obj_size;
-    case PROCPS_SLABINFO_SIZE_TOTAL:
-        return info->stats.total_size;
-    case PROCPS_SLABINFO_SIZE_ACTIVE:
-        return info->stats.active_size;
+        case PROCPS_SLABS_OBJS:
+            return info->stats.nr_objs;
+        case PROCPS_SLABS_AOBJS:
+            return info->stats.nr_active_objs;
+        case PROCPS_SLABS_PAGES:
+            return info->stats.nr_pages;
+        case PROCPS_SLABS_SLABS:
+            return info->stats.nr_slabs;
+        case PROCPS_SLABS_ASLABS:
+            return info->stats.nr_active_slabs;
+        case PROCPS_SLABS_CACHES:
+            return info->stats.nr_caches;
+        case PROCPS_SLABS_ACACHES:
+            return info->stats.nr_active_caches;
+        case PROCPS_SLABS_SIZE_AVG:
+            return info->stats.avg_obj_size;
+        case PROCPS_SLABS_SIZE_MIN:
+            return info->stats.min_obj_size;
+        case PROCPS_SLABS_SIZE_MAX:
+            return info->stats.max_obj_size;
+        case PROCPS_SLABS_SIZE_TOTAL:
+            return info->stats.total_size;
+        case PROCPS_SLABS_SIZE_ACTIVE:
+            return info->stats.active_size;
+        case PROCPS_SLABS_noop:
+            break;
     }
     return 0;
 }
 
-PROCPS_EXPORT int procps_slabinfo_stat_getchain(
+PROCPS_EXPORT int procps_slabs_getchain (
         struct procps_slabinfo *info,
-        struct procps_slabinfo_result *result)
+        struct slabs_result *these)
 {
-    if (!info || !result)
+    if (info == NULL || these == NULL)
         return -EINVAL;
 
     do {
-        switch (result->item) {
-        case PROCPS_SLABINFO_OBJS:
-            result->result = info->stats.nr_objs;
-            break;
-        case PROCPS_SLABINFO_AOBJS:
-            result->result = info->stats.nr_active_objs;
-            break;
-        case PROCPS_SLABINFO_PAGES:
-            result->result = info->stats.nr_pages;
-            break;
-        case PROCPS_SLABINFO_SLABS:
-            result->result = info->stats.nr_slabs;
-            break;
-        case PROCPS_SLABINFO_ASLABS:
-            result->result = info->stats.nr_active_slabs;
-            break;
-        case PROCPS_SLABINFO_CACHES:
-            result->result = info->stats.nr_caches;
-            break;
-        case PROCPS_SLABINFO_ACACHES:
-            result->result = info->stats.nr_active_caches;
-            break;
-        case PROCPS_SLABINFO_SIZE_AVG:
-            result->result = info->stats.avg_obj_size;
-            break;
-        case PROCPS_SLABINFO_SIZE_MIN:
-            result->result = info->stats.min_obj_size;
-            break;
-        case PROCPS_SLABINFO_SIZE_MAX:
-            result->result = info->stats.max_obj_size;
-            break;
-        case PROCPS_SLABINFO_SIZE_TOTAL:
-            result->result = info->stats.total_size;
-            break;
-        case PROCPS_SLABINFO_SIZE_ACTIVE:
-            result->result = info->stats.active_size;
-            break;
-        default:
-            return -EINVAL;
+        switch (these->item) {
+            case PROCPS_SLABS_OBJS:
+                these->result = info->stats.nr_objs;
+                break;
+            case PROCPS_SLABS_AOBJS:
+                these->result = info->stats.nr_active_objs;
+                break;
+            case PROCPS_SLABS_PAGES:
+                these->result = info->stats.nr_pages;
+                break;
+            case PROCPS_SLABS_SLABS:
+                these->result = info->stats.nr_slabs;
+                break;
+            case PROCPS_SLABS_ASLABS:
+                these->result = info->stats.nr_active_slabs;
+                break;
+            case PROCPS_SLABS_CACHES:
+                these->result = info->stats.nr_caches;
+                break;
+            case PROCPS_SLABS_ACACHES:
+                these->result = info->stats.nr_active_caches;
+                break;
+            case PROCPS_SLABS_SIZE_AVG:
+                these->result = info->stats.avg_obj_size;
+                break;
+            case PROCPS_SLABS_SIZE_MIN:
+                these->result = info->stats.min_obj_size;
+                break;
+            case PROCPS_SLABS_SIZE_MAX:
+                these->result = info->stats.max_obj_size;
+                break;
+            case PROCPS_SLABS_SIZE_TOTAL:
+                these->result = info->stats.total_size;
+                break;
+            case PROCPS_SLABS_SIZE_ACTIVE:
+                these->result = info->stats.active_size;
+                break;
+            default:
+                return -EINVAL;
         }
-        result = result->next;
-    } while(result);
+        these = these->next;
+    } while(these);
     return 0;
 }
 
 /*
- * procps_slabinfo_node_getname():
+ * procps_slabnode_getname():
  *
  * @info: slabinfo structure with data read in
  * @nodeid: number of node we want the name for
@@ -499,7 +460,7 @@ PROCPS_EXPORT int procps_slabinfo_stat_getchain(
  *
  * Returns: name or NULL on error
  */
-PROCPS_EXPORT char *procps_slabinfo_node_getname(
+PROCPS_EXPORT const char *procps_slabnode_getname (
         struct procps_slabinfo *info,
         int nodeid)
 {
@@ -510,181 +471,355 @@ PROCPS_EXPORT char *procps_slabinfo_node_getname(
     return info->nodes[nodeid].name;
 }
 
-PROCPS_EXPORT int procps_slabinfo_node_getchain (
+PROCPS_EXPORT unsigned long procps_slabnode_get (
         struct procps_slabinfo *info,
-        struct procps_slabnode_result *result,
+        enum slabnode_item item,
         int nodeid)
 {
-    if (info == NULL || result == NULL)
-        return -EINVAL;
-    if (nodeid > info->nodes_used)
+    if (info == NULL)
         return -EINVAL;
 
-    do {
-        switch (result->item) {
+    switch (item) {
         case PROCPS_SLABNODE_SIZE:
-            result->result = info->nodes[nodeid].cache_size;
-            break;
+            return info->nodes[nodeid].cache_size;
         case PROCPS_SLABNODE_OBJS:
-            result->result = info->nodes[nodeid].nr_objs;
-            break;
+            return info->nodes[nodeid].nr_objs;
         case PROCPS_SLABNODE_AOBJS:
-            result->result = info->nodes[nodeid].nr_active_objs;
-            break;
+            return info->nodes[nodeid].nr_active_objs;
         case PROCPS_SLABNODE_OBJ_SIZE:
-            result->result = info->nodes[nodeid].obj_size;
-            break;
+            return info->nodes[nodeid].obj_size;
         case PROCPS_SLABNODE_OBJS_PER_SLAB:
-            result->result = info->nodes[nodeid].objs_per_slab;
-            break;
+            return info->nodes[nodeid].objs_per_slab;
         case PROCPS_SLABNODE_PAGES_PER_SLAB:
-            result->result = info->nodes[nodeid].pages_per_slab;
-            break;
+            return info->nodes[nodeid].pages_per_slab;
         case PROCPS_SLABNODE_SLABS:
-            result->result = info->nodes[nodeid].nr_slabs;
-            break;
+            return info->nodes[nodeid].nr_slabs;
         case PROCPS_SLABNODE_ASLABS:
-            result->result = info->nodes[nodeid].nr_active_slabs;
-            break;
+            return info->nodes[nodeid].nr_active_slabs;
         case PROCPS_SLABNODE_USE:
-            result->result = info->nodes[nodeid].use;
-            break;
+            return info->nodes[nodeid].use;
+        case PROCPS_SLABNODE_NAME:
+        case PROCPS_SLABNODE_noop:
+            return 0;
         default:
             return -EINVAL;
-        }
-        result = result->next;
-    } while(result);
+    }
     return 0;
 }
 
-/*
- * Sorting functions
- *
- * These functions sort the slabnodes. The sort type is
- * the same as the get enum
- */
-static int sort_name(const void *a, const void *b)
+PROCPS_EXPORT int procps_slabnode_getchain (
+        struct procps_slabinfo *info,
+        struct slabnode_result *these,
+        int nodeid)
 {
-    return strcmp(
-                   ((struct procps_slabnode*)a)->name,
-                   ((struct procps_slabnode*)b)->name);
-}
+    if (info == NULL || these == NULL)
+        return -EINVAL;
+    if (nodeid > info->nodes_used)
+        return -EINVAL;
 
-static int sort_objs(const void *a, const void *b)
-{
-    return ((struct procps_slabnode*)b)->nr_objs -
-        ((struct procps_slabnode*)a)->nr_objs;
+    do {
+        switch (these->item) {
+            case PROCPS_SLABNODE_SIZE:
+                these->result.num = info->nodes[nodeid].cache_size;
+                break;
+            case PROCPS_SLABNODE_OBJS:
+                these->result.num = info->nodes[nodeid].nr_objs;
+                break;
+            case PROCPS_SLABNODE_AOBJS:
+                these->result.num = info->nodes[nodeid].nr_active_objs;
+                break;
+            case PROCPS_SLABNODE_OBJ_SIZE:
+                these->result.num = info->nodes[nodeid].obj_size;
+                break;
+            case PROCPS_SLABNODE_OBJS_PER_SLAB:
+                these->result.num = info->nodes[nodeid].objs_per_slab;
+                break;
+            case PROCPS_SLABNODE_PAGES_PER_SLAB:
+                these->result.num = info->nodes[nodeid].pages_per_slab;
+                break;
+            case PROCPS_SLABNODE_SLABS:
+                these->result.num = info->nodes[nodeid].nr_slabs;
+                break;
+            case PROCPS_SLABNODE_ASLABS:
+                these->result.num = info->nodes[nodeid].nr_active_slabs;
+                break;
+            case PROCPS_SLABNODE_USE:
+                these->result.num = info->nodes[nodeid].use;
+                break;
+            case PROCPS_SLABNODE_NAME:
+                these->result.str = info->nodes[nodeid].name;
+                break;
+            case PROCPS_SLABNODE_noop:
+                break;
+            default:
+                return -EINVAL;
+        }
+        these = these->next;
+    } while(these);
+    return 0;
 }
 
-static int sort_aobjs(const void *a, const void *b)
-{
-    return ((struct procps_slabnode*)b)->nr_active_objs -
-        ((struct procps_slabnode*)a)->nr_active_objs;
-}
 
-static int sort_size(const void *a, const void *b)
+PROCPS_EXPORT int procps_slabnode_chain_fill (
+    struct procps_slabinfo *info,
+    struct slabnode_chain *chain,
+    int nodeid)
 {
-    return ((struct procps_slabnode*)b)->obj_size -
-        ((struct procps_slabnode*)a)->obj_size;
+    struct slabnode_result *these = chain->head;
+
+    if (info == NULL || these == NULL)
+        return -EINVAL;
+
+    return procps_slabnode_getchain(info, these, nodeid);
 }
 
-static int sort_objsperslab(const void *a, const void *b)
+/*
+ * procps_slabnode_count():
+ *
+ * @info: read in slabinfo structure
+ *
+ * Returns: number of nodes in @info or <0 on error
+ */
+PROCPS_EXPORT int procps_slabnode_count (
+        const struct procps_slabinfo *info)
 {
-    return ((struct procps_slabnode*)b)->objs_per_slab -
-        ((struct procps_slabnode*)a)->objs_per_slab;
+    if (!info)
+        return -EINVAL;
+    return info->nodes_used;
 }
 
-static int sort_pagesperslab(const void *a, const void *b)
+PROCPS_EXPORT int procps_slabnode_chains_fill (
+    struct procps_slabinfo *info,
+    struct slabnode_chain **chains,
+    int maxchains)
 {
-    return ((struct procps_slabnode*)b)->pages_per_slab -
-        ((struct procps_slabnode*)a)->pages_per_slab;
+    int i, rc;
+
+    if (info == NULL || *chains == NULL)
+        return -EINVAL;
+    if (maxchains < 1)
+        return -EINVAL;
+
+    if ((rc = procps_slabinfo_read(info)) < 0)
+        return rc;
+
+    if (maxchains > info->chained->depth)
+        maxchains = info->chained->depth;
+    if (maxchains > info->nodes_used)
+        maxchains = info->nodes_used;
+
+    for (i = 0; i < maxchains; i++) {
+        if (chains[i] == NULL)
+            break;
+        if ((rc = procps_slabnode_chain_fill(info, chains[i], i) < 0))
+            return rc;
+    }
+
+    info->chained->inuse = i;
+    return info->chained->inuse;
 }
 
-static int sort_slabs(const void *a, const void *b)
+static void chains_validate (struct slabnode_chain **v, const char *who)
 {
-    return ((struct procps_slabnode*)b)->nr_slabs -
-        ((struct procps_slabnode*)a)->nr_slabs;
+#if 0
+    #include <stdio.h>
+    int i, x, n = 0;
+    struct chain_vectors *p = (struct chain_vectors *)v - 1;
+
+    fprintf(stderr, "%s: called by '%s'\n", __func__, who);
+    fprintf(stderr, "%s: owned by %p (whose self = %p)\n", __func__, p->owner, p->owner->self);
+    for (x = 0; v[x]; x++) {
+        struct slabnode_chain *h = v[x];
+        struct slabnode_result *r = h->head;
+        fprintf(stderr, "%s:   vector[%02d] = %p", __func__, x, h);
+        i = 0;
+        do {
+            i++;
+            r = r->next;
+        } while (r);
+        fprintf(stderr, ", chain %d found %d elements\n", n, i);
+        ++n;
+    }
+    fprintf(stderr, "%s: found %d chain(s)\n", __func__, x);
+    fprintf(stderr, "%s: this header size = %2d\n", __func__, (int)p->owner->header_size);
+    fprintf(stderr, "%s: sizeof(struct slabnode_chain)  = %2d\n", __func__, (int)sizeof(struct slabnode_chain));
+    fprintf(stderr, "%s: sizeof(struct slabnode_result) = %2d\n", __func__, (int)sizeof(struct slabnode_result));
+    fputc('\n', stderr);
+    return;
+#endif
 }
 
-static int sort_use(const void *a, const void *b)
+static struct slabnode_result *chain_make (
+        struct slabnode_result *p,
+        int maxitems,
+        enum slabnode_item *items)
 {
-    return ((struct procps_slabnode*)b)->use -
-        ((struct procps_slabnode*)a)->use;
+    struct slabnode_result *p_sav = p;
+    int i;
+
+    for (i = 0; i < maxitems; i++) {
+        if (i > PROCPS_SLABNODE_noop)
+            p->item = PROCPS_SLABNODE_noop;
+        else
+            p->item = items[i];
+        p->result.num = 0;
+        p->next = p + 1;
+        ++p;
+    }
+    (--p)->next = NULL;
+
+    return p_sav;
 }
 
-static int sort_aslabs(const void *a, const void *b)
+/*
+ * procps_slabnode_chains_alloc():
+ *
+ * Allocate and initialize one or more chains each of which is anchored in an
+ * associated meminfo_chain structure (which may include extra user space).
+ *
+ * All such chains will will have their result structures properly primed with
+ * 'items' and 'next' pointers, while the result itself will be zeroed.
+ *
+ * Returns an array of pointers representing the 'heads' of each new chain.
+ */
+PROCPS_EXPORT struct slabnode_chain **procps_slabnode_chains_alloc (
+        struct procps_slabinfo *info,
+        int maxchains,
+        int chain_extra,
+        int maxitems,
+        enum slabnode_item *items)
 {
-    return ((struct procps_slabnode*)b)->nr_active_slabs -
-        ((struct procps_slabnode*)a)->nr_active_slabs;
+    struct chains_anchor *p_blob;
+    struct chain_vectors *p_vect;
+    struct slabnode_chain *p_head;
+    size_t vect_size, head_size, list_size, blob_size;
+    void *v_head, *v_list;
+    int i;
+
+    if (info == NULL || items == NULL)
+        return NULL;
+    if (maxchains < 1 || maxitems < 1)
+        return NULL;
+
+    vect_size  = sizeof(struct chain_vectors);                 // address vector struct
+    vect_size += sizeof(void *) * maxchains;                   // plus vectors themselves
+    vect_size += sizeof(void *);                               // plus NULL delimiter
+    head_size  = sizeof(struct slabnode_chain) + chain_extra;  // a head struct + user stuff
+    list_size  = sizeof(struct slabnode_result) * maxitems;    // a results chain
+    blob_size  = sizeof(struct chains_anchor);                 // the anchor itself
+    blob_size += vect_size;                                    // all vectors + delims
+    blob_size += head_size * maxchains;                        // all head structs + user stuff
+    blob_size += list_size * maxchains;                        // all results chains
+
+    /* note: all memory is allocated in a single blob, facilitating a later free().
+       as a minimum, it's important that the result structures themselves always be
+       contiguous for any given chain (just as they are when defined statically). */
+    if (NULL == (p_blob = calloc(1, blob_size)))
+        return NULL;
+
+    p_blob->next = info->chained;
+    info->chained = p_blob;
+    p_blob->self  = p_blob;
+    p_blob->header_size = head_size;
+    p_blob->vectors = (void *)p_blob + sizeof(struct chains_anchor);
+    p_vect = p_blob->vectors;
+    p_vect->owner = p_blob->self;
+    p_vect->heads = (void *)p_vect + sizeof(struct chain_vectors);
+    v_head = (void *)p_vect + vect_size;
+    v_list = v_head + (head_size * maxchains);
+
+    for (i = 0; i < maxchains; i++) {
+        p_head = (struct slabnode_chain *)v_head;
+        p_head->head = chain_make((struct slabnode_result *)v_list, maxitems, items);
+        p_blob->vectors->heads[i] = p_head;
+        v_list += list_size;
+        v_head += head_size;
+    }
+    p_blob->depth = maxchains;
+    chains_validate(p_blob->vectors->heads, __func__);
+    return p_blob->vectors->heads;
 }
 
 /*
- * procps_slabinfo_sort:
+ * procps_slabnode_chain_alloc():
  *
- * @info: the slabinfo that has the read data
- * @item: slabnode item to sort by
+ * Allocate and initialize a single result chain under a simplified interface.
  *
- * Sort the slabnodes contained in @info based
- * upon the criteria @item
+ * Such a chain will will have its result structures properly primed with
+ * 'items' and 'next' pointers, while the result itself is set to zero.
  *
- * Returns: 0 on success < on error
  */
-PROCPS_EXPORT int procps_slabinfo_sort(
+PROCPS_EXPORT struct slabnode_chain *procps_slabnode_chain_alloc (
         struct procps_slabinfo *info,
-        const enum procps_slabinfo_nodeitem item)
+        int maxitems,
+        enum slabnode_item *items)
 {
-    void * sort_func = NULL;
+    struct slabnode_chain **v;
 
-    if (info == NULL)
-        return -EINVAL;
+    if (info == NULL || items == NULL || maxitems < 1)
+        return NULL;
+    v = procps_slabnode_chains_alloc(info, 1, 0, maxitems, items);
+    if (!v)
+        return NULL;
+    chains_validate(v, __func__);
+    return v[0];
+}
 
-    switch (item) {
-    case PROCPS_SLABNODE_NAME:
-        sort_func = sort_name;
-        break;
-    case PROCPS_SLABNODE_OBJS:
-        sort_func = sort_objs;
-        break;
-    case PROCPS_SLABNODE_AOBJS:
-        sort_func = sort_aobjs;
-        break;
-    case PROCPS_SLABNODE_SIZE:
-        sort_func = sort_size;
-        break;
-    case PROCPS_SLABNODE_OBJS_PER_SLAB:
-        sort_func = sort_objsperslab;
-        break;
-    case PROCPS_SLABNODE_PAGES_PER_SLAB:
-        sort_func = sort_pagesperslab;
-        break;
-    case PROCPS_SLABNODE_SLABS:
-        sort_func = sort_slabs;
-        break;
-    case PROCPS_SLABNODE_ASLABS:
-        sort_func = sort_aslabs;
-        break;
-    case PROCPS_SLABNODE_USE:
-        sort_func = sort_use;
-        break;
-    default:
-        return -EINVAL;
-    }
-    qsort(info->nodes, info->nodes_used,
-          sizeof(struct procps_slabnode), sort_func);
+static int chains_sort (
+        const struct slabnode_chain **A,
+        const struct slabnode_chain **B,
+        enum slabnode_item *offset)
+{
+    const struct slabnode_result *a = (*A)->head + *offset;
+    const struct slabnode_result *b = (*B)->head + *offset;
+    // note: strings are sorted normally, but numbers will be high-to-low
+    if (a->item == PROCPS_SLABNODE_NAME)
+        return strcoll(a->result.str, b->result.str);
+    if ( a->result.num > b->result.num ) return -1;
+    if ( a->result.num < b->result.num ) return +1;
     return 0;
 }
 
 /*
- * procps_slabinfo_node_count():
+ * procps_slabnode_chains_sort():
  *
- * @info: read in slabinfo structure
+ * Sort chains anchored as 'heads' in the passed slabnode_chain pointers
+ * array based on the designated sort enumerator.
  *
- * Returns: number of nodes in @info or <0 on error
+ * Returns the same structure with those pointers sorted.
+ *
+ * Note: all of the chains must be homogeneous (of equal length and content).
  */
-PROCPS_EXPORT int procps_slabinfo_node_count(
-        const struct procps_slabinfo *info)
+PROCPS_EXPORT struct slabnode_chain **procps_slabnode_chains_sort (
+        struct procps_slabinfo *info,
+        struct slabnode_chain **chains,
+        int numchained,
+        enum slabnode_item sort)
 {
-    if (!info)
-        return -EINVAL;
-    return info->nodes_used;
+ #define QSORT_r  int (*)(const void *, const void *, void *)
+    struct slabnode_result *p = chains[0]->head;
+    int offset = 0;;
+
+    if (info == NULL || chains == NULL)
+        return NULL;
+    if (sort < 0  || sort >= PROCPS_SLABNODE_noop)
+        return NULL;
+    if (numchained > info->chained->depth)
+        return NULL;
+    if (numchained < 2)
+        return chains;
+
+    if (numchained > info->chained->inuse)
+        numchained = info->chained->inuse;
+
+    for (;;) {
+        if (p->item == sort)
+            break;
+        ++offset;
+        if (!(p = p->next))
+            return NULL;
+    }
+    qsort_r(chains, numchained, sizeof(void *), (QSORT_r)chains_sort, &offset);
+    return chains;
+ #undef QSORT_r
 }
index 299544d451c78c482d7c6844f01c8de1966fd481..8e2b6a0da424848b2beac3b3ad77142742d6d255 100644 (file)
@@ -1,25 +1,46 @@
+/*
+ * slab.h - slab related functions for libproc
+ *
+ * Copyright (C) 1998-2005 Albert Cahalan
+ * Copyright (C) 2015 Craig Small <csmall@enc.com.au>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy 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_SLAB_H
 #define _PROC_SLAB_H
 
 __BEGIN_DECLS
 
-enum procps_slabinfo_stat {
-    PROCPS_SLABINFO_OBJS,
-    PROCPS_SLABINFO_AOBJS,
-    PROCPS_SLABINFO_PAGES,
-    PROCPS_SLABINFO_SLABS,
-    PROCPS_SLABINFO_ASLABS,
-    PROCPS_SLABINFO_CACHES,
-    PROCPS_SLABINFO_ACACHES,
-    PROCPS_SLABINFO_SIZE_AVG,
-    PROCPS_SLABINFO_SIZE_MIN,
-    PROCPS_SLABINFO_SIZE_MAX,
-    PROCPS_SLABINFO_SIZE_TOTAL,
-    PROCPS_SLABINFO_SIZE_ACTIVE,
+enum slabs_item {
+    PROCPS_SLABS_OBJS,
+    PROCPS_SLABS_AOBJS,
+    PROCPS_SLABS_PAGES,
+    PROCPS_SLABS_SLABS,
+    PROCPS_SLABS_ASLABS,
+    PROCPS_SLABS_CACHES,
+    PROCPS_SLABS_ACACHES,
+    PROCPS_SLABS_SIZE_AVG,
+    PROCPS_SLABS_SIZE_MIN,
+    PROCPS_SLABS_SIZE_MAX,
+    PROCPS_SLABS_SIZE_TOTAL,
+    PROCPS_SLABS_SIZE_ACTIVE,
+    PROCPS_SLABS_noop
 };
 
-enum procps_slabinfo_nodeitem {
-    PROCPS_SLABNODE_NAME,
+enum slabnode_item {
     PROCPS_SLABNODE_SIZE,
     PROCPS_SLABNODE_OBJS,
     PROCPS_SLABNODE_AOBJS,
@@ -28,22 +49,30 @@ enum procps_slabinfo_nodeitem {
     PROCPS_SLABNODE_PAGES_PER_SLAB,
     PROCPS_SLABNODE_SLABS,
     PROCPS_SLABNODE_ASLABS,
-    PROCPS_SLABNODE_USE
+    PROCPS_SLABNODE_USE,
+    PROCPS_SLABNODE_NAME,
+    PROCPS_SLABNODE_noop
 };
 
 struct procps_slabinfo;
-struct procps_slabnode;
 
-struct procps_slabinfo_result {
-    enum procps_slabinfo_stat item;
+struct slabs_result {
+    enum slabs_item item;
     unsigned long result;
-    struct procps_slabinfo_result *next;
+    struct slabs_result *next;
 };
 
-struct procps_slabnode_result {
-    enum procps_slabinfo_nodeitem item;
-    unsigned long result;
-    struct procps_slabnode_result *next;
+struct slabnode_chain {
+    struct slabnode_result *head;
+};
+
+struct slabnode_result {
+    enum slabnode_item item;
+    union {
+        unsigned long num;
+        char *str;
+    } result;
+    struct slabnode_result *next;
 };
 
 int procps_slabinfo_new (struct procps_slabinfo **info);
@@ -52,26 +81,58 @@ int procps_slabinfo_read (struct procps_slabinfo *info);
 int procps_slabinfo_ref (struct procps_slabinfo *info);
 int procps_slabinfo_unref (struct procps_slabinfo **info);
 
-unsigned long procps_slabinfo_stat_get (struct procps_slabinfo *info,
-                              enum procps_slabinfo_stat item);
+unsigned long procps_slabs_get (
+    struct procps_slabinfo *info,
+    enum slabs_item item);
+
+int procps_slabs_getchain (
+    struct procps_slabinfo *info,
+    struct slabs_result *these);
+
+int procps_slabnode_count (const struct procps_slabinfo *info);
+
+const char *procps_slabnode_getname (
+    struct procps_slabinfo *info,
+    int nodeid);
+
+unsigned long procps_slabnode_get (
+    struct procps_slabinfo *info,
+    enum slabnode_item item,
+    int nodeid);
+
+int procps_slabnode_getchain (
+    struct procps_slabinfo *info,
+    struct slabnode_result *these,
+    int nodeid);
+
+int procps_slabnode_chain_fill (
+    struct procps_slabinfo *info,
+    struct slabnode_chain *chain,
+    int nodeid);
+
+int procps_slabnode_chains_fill (
+    struct procps_slabinfo *info,
+    struct slabnode_chain **chains,
+    int maxchains);
+
+struct slabnode_chain *procps_slabnode_chain_alloc (
+    struct procps_slabinfo *info,
+    int maxitems,
+    enum slabnode_item *items);
+
+struct slabnode_chain **procps_slabnode_chains_alloc (
+    struct procps_slabinfo *info,
+    int maxchains,
+    int chain_extra,
+    int maxitems,
+    enum slabnode_item *items);
+
+struct slabnode_chain **procps_slabnode_chains_sort (
+    struct procps_slabinfo *info,
+    struct slabnode_chain **chains,
+    int numchained,
+    enum slabnode_item sort);
 
-int procps_slabinfo_stat_getchain (struct procps_slabinfo *info,
-                                   struct procps_slabinfo_result *result);
-
-int procps_slabinfo_sort( struct procps_slabinfo *info,
-                          const enum procps_slabinfo_nodeitem item);
-
-int procps_slabinfo_node_count(const struct procps_slabinfo *info);
-
-int procps_slabinfo_node_get (struct procps_slabinfo *info,
-                              struct procps_slabnode **node);
-int procps_slabinfo_node_getchain (struct procps_slabinfo *info,
-                                   struct procps_slabnode_result *result,
-                                   int nodeid);
-
-char *procps_slabinfo_node_getname(struct procps_slabinfo *info,
-                                   int nodeid);
 __END_DECLS
 
-
 #endif /* _PROC_SLAB_H */