From bd0af72956d525e6159241a277a3d7451c54b867 Mon Sep 17 00:00:00 2001
From: Vinayak Menon <vinayakm.list@gmail.com>
Date: Thu, 10 Mar 2016 19:28:22 +0530
Subject: [PATCH] Fix for the wrong calculation of slub total and inuse
 objects. With a ramdump of a system with 386MB of available memory, "kmem -s"
 shows something like this

CACHE       NAME                 OBJSIZE  ALLOCATED     TOTAL  SLABS  SSIZE
dd801c00 kmalloc-2048            2048     670869         670960  41935    32k

The memory occupied by objects go beyond the total system memory
if we use the above numbers to calculate that. The output is wrong
because with SLUB, slabs can be of different sizes depending on the
order of allocation. And objects are calculated considering slabs
are of fixed size.

---
 defs.h   |  1 +
 memory.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
 2 files changed, 68 insertions(+), 14 deletions(-)

diff --git a/defs.h b/defs.h
index 2ecfa03..259ef3a 100644
--- a/defs.h
+++ b/defs.h
@@ -1686,6 +1686,7 @@ struct offset_table {                    /* stash of commonly-used offsets */
 	long page_freelist;
 	long kmem_cache_node_nr_partial;
 	long kmem_cache_node_nr_slabs;
+	long kmem_cache_node_total_objects;
 	long kmem_cache_node_partial;
 	long kmem_cache_node_full;
 	long pid_numbers;
diff --git a/memory.c b/memory.c
index 9ac6757..904268e 100644
--- a/memory.c
+++ b/memory.c
@@ -279,7 +279,7 @@ static int page_to_nid(ulong);
 static int get_kmem_cache_list(ulong **);
 static int get_kmem_cache_slub_data(long, struct meminfo *);
 static ulong compound_head(ulong);
-static long count_partial(ulong, struct meminfo *);
+static long count_partial(ulong, struct meminfo *, ulong *);
 static ulong get_freepointer(struct meminfo *, void *);
 static int count_free_objects(struct meminfo *, ulong);
 char *is_slab_page(struct meminfo *, char *);
@@ -742,6 +742,8 @@ vm_init(void)
 			"kmem_cache_node", "nr_partial");
 		MEMBER_OFFSET_INIT(kmem_cache_node_nr_slabs, 
 			"kmem_cache_node", "nr_slabs");
+		MEMBER_OFFSET_INIT(kmem_cache_node_total_objects,
+			"kmem_cache_node", "total_objects");
 		MEMBER_OFFSET_INIT(kmem_cache_node_partial, 
 			"kmem_cache_node", "partial");
 		MEMBER_OFFSET_INIT(kmem_cache_node_full, 
@@ -17788,7 +17790,7 @@ dump_kmem_cache_info_slub(struct meminfo *si)
         fprintf(fp, b1, si->inuse);
 
         fprintf(fp, "%8ld  %5ld  %4ldk\n",  
-		si->num_slabs * si->objects, 
+		si->inuse + si->free,
                 si->num_slabs, si->slabsize/1024); 
 }
 
@@ -17919,10 +17921,10 @@ static int
 get_kmem_cache_slub_data(long cmd, struct meminfo *si)
 {
 	int i, n, node;
-	ulong total_objects, total_slabs;
+	ulong total_objects, total_slabs, free_objects;
 	ulong cpu_slab_ptr, node_ptr;
-	ulong node_nr_partial, node_nr_slabs;
-	int full_slabs, objects;
+	ulong node_nr_partial, node_nr_slabs, node_total_objects;
+	int full_slabs, objects, node_total_avail = 0;
 	long p;
 	short inuse;
         ulong *nodes, *per_cpu;
@@ -17935,7 +17937,10 @@ get_kmem_cache_slub_data(long cmd, struct meminfo *si)
         nodes = (ulong *)GETBUF(2 * sizeof(ulong) * vt->numnodes);
         per_cpu = nodes + vt->numnodes;
 
-	total_slabs = total_objects = 0; 
+	total_slabs = total_objects = free_objects = 0;
+
+	if (VALID_MEMBER(kmem_cache_node_total_objects))
+		node_total_avail = 1;
 
 	for (i = 0; i < kt->cpus; i++) {
 		cpu_slab_ptr = get_cpu_slab_ptr(si, i, NULL);
@@ -17953,7 +17958,9 @@ get_kmem_cache_slub_data(long cmd, struct meminfo *si)
 			    KVADDR, &inuse, sizeof(short), 
 			    "page inuse", RETURN_ON_ERROR))
 				return FALSE;
-			total_objects += inuse;
+			if (!node_total_avail)
+				total_objects += inuse;
+			total_slabs++;
 			break;
 
 		case GET_SLUB_SLABS:
@@ -17984,13 +17991,21 @@ get_kmem_cache_slub_data(long cmd, struct meminfo *si)
 		    KVADDR, &node_nr_slabs, sizeof(ulong), 
 		    "kmem_cache_node nr_slabs", RETURN_ON_ERROR))
 			goto bailout;
+		if (node_total_avail) {
+			if (!readmem(node_ptr + OFFSET(kmem_cache_node_total_objects),
+			KVADDR, &node_total_objects, sizeof(ulong),
+			"kmem_cache_node total_objects", RETURN_ON_ERROR))
+				goto bailout;
+		}
 
 		switch (cmd)
 		{
 		case GET_SLUB_OBJECTS:
-			if ((p = count_partial(node_ptr, si)) < 0)
+			if ((p = count_partial(node_ptr, si, &free_objects)) < 0)
 				return FALSE;
-			total_objects += p;
+			if (!node_total_avail)
+				total_objects += p;
+			total_slabs += node_nr_partial;
 			break;
 
 		case GET_SLUB_SLABS:
@@ -18004,7 +18019,11 @@ get_kmem_cache_slub_data(long cmd, struct meminfo *si)
 		switch (cmd)
 		{
 		case GET_SLUB_OBJECTS:
-			total_objects += (full_slabs * objects);
+			if (node_total_avail)
+				total_objects += node_total_objects;
+			else
+				total_objects += (full_slabs * objects);
+			total_slabs += full_slabs;
 			break;
 
 		case GET_SLUB_SLABS:
@@ -18019,7 +18038,11 @@ get_kmem_cache_slub_data(long cmd, struct meminfo *si)
 	switch (cmd)
 	{
 	case GET_SLUB_OBJECTS:
-		si->inuse = total_objects;
+		si->inuse = total_objects - free_objects;
+		if (VALID_MEMBER(page_objects))
+			si->free = free_objects;
+		else
+			si->free = (total_slabs * si->objects) - si->inuse;
 		break;
 
 	case GET_SLUB_SLABS:
@@ -18586,10 +18609,10 @@ compound_head(ulong page)
 }
 
 long 
-count_partial(ulong node, struct meminfo *si)
+count_partial(ulong node, struct meminfo *si, ulong *free)
 {
-	ulong list_head, next, last;
-	short inuse;
+	ulong list_head, next, last, objects_vaddr;
+	short inuse, objects;
 	ulong total_inuse;
 	ulong count = 0;
 
@@ -18617,6 +18640,36 @@ count_partial(ulong node, struct meminfo *si)
 			break;
 		}
 		total_inuse += inuse;
+
+		if (VALID_MEMBER(page_objects)) {
+			objects_vaddr = last + OFFSET(page_objects);
+			if (si->flags & SLAB_BITFIELD)
+				objects_vaddr += sizeof(ushort);
+			if (!readmem(objects_vaddr, KVADDR, &objects,
+				sizeof(ushort), "page.objects", RETURN_ON_ERROR)) {
+				hq_close();
+				return -1;
+			}
+
+			if (si->flags & SLAB_BITFIELD) {
+				if (__BYTE_ORDER == __LITTLE_ENDIAN) {
+					objects <<= 1;
+					objects >>= 1;
+				}
+				if (__BYTE_ORDER == __BIG_ENDIAN)
+					objects >>= 1;
+			}
+
+			if (objects == (ushort)(-1)) {
+				error(INFO, "%s: slab: %lx invalid page.objects: -1\n",
+					si->curname, last);
+				hq_close();
+				return -1;
+			}
+
+			*free += objects - inuse;
+		}
+
 		if (!readmem(next, KVADDR, &next, sizeof(ulong),
 		    "page.lru.next", RETURN_ON_ERROR)) {
 			hq_close();
