Hi,
SLUB cpu_slab has 2 freelist. One is cpu_slab->freelist for local
cpu. One is cpu_slab->page->freelist for remote cpu.
So, we have to check both of freelists to know details. Note,
page->inuse counts only for free on page->freelist, not
cpu_slab->freelist.
so total free objects are
(page->objects - page->inuse) + count(cpu_slab->freelist))
---
memory.c | 213 ++++++++++++++++++++++++++++----------------------------------
1 file changed, 99 insertions(+), 114 deletions(-)
diff -puN memory.c~crash-slub-freelist-fix memory.c
--- crash-64/memory.c~crash-slub-freelist-fix 2016-04-18 02:29:57.743774055 +0900
+++ crash-64-hirofumi/memory.c 2016-04-18 02:32:30.999515870 +0900
@@ -17914,15 +17914,62 @@ bailout:
FREEBUF(si->cache_buf);
}
+static ushort slub_page_objects(struct meminfo *si, ulong page)
+{
+ ulong objects_vaddr;
+ ushort objects;
+
+ /*
+ * Pre-2.6.27, the object count and order were fixed in the
+ * kmem_cache structure. Now they may change, say if a high
+ * order slab allocation fails, so the per-slab object count
+ * is kept in the slab.
+ */
+ if (VALID_MEMBER(page_objects)) {
+ objects_vaddr = page + 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))
+ return 0;
+ /*
+ * Strip page.frozen bit.
+ */
+ if (si->flags & SLAB_BITFIELD) {
+ if (__BYTE_ORDER == __LITTLE_ENDIAN) {
+ objects <<= 1;
+ objects >>= 1;
+ }
+ if (__BYTE_ORDER == __BIG_ENDIAN)
+ objects >>= 1;
+ }
+
+ if (CRASHDEBUG(1) && (objects != si->objects))
+ error(NOTE, "%s: slab: %lx oo objects: %ld "
+ "slab objects: %d\n",
+ si->curname, si->slab,
+ si->objects, objects);
+
+ if (objects == (ushort)(-1)) {
+ error(INFO, "%s: slab: %lx invalid page.objects: -1\n",
+ si->curname, si->slab);
+ return 0;
+ }
+ } else
+ objects = (ushort)si->objects;
+
+ return objects;
+}
+
static short
count_cpu_partial(struct meminfo *si, int cpu)
{
short cpu_partial_inuse, cpu_partial_objects, free_objects;
- ulong cpu_partial, objects_vaddr;
+ ulong cpu_partial;
free_objects = 0;
- if (VALID_MEMBER(kmem_cache_cpu_partial)) {
+ if (VALID_MEMBER(kmem_cache_cpu_partial) && VALID_MEMBER(page_objects)) {
readmem(ULONG(si->cache_buf + OFFSET(kmem_cache_cpu_slab)) +
kt->__per_cpu_offset[cpu] + OFFSET(kmem_cache_cpu_partial),
KVADDR, &cpu_partial, sizeof(ulong),
@@ -17939,27 +17986,13 @@ count_cpu_partial(struct meminfo *si, in
return 0;
if (cpu_partial_inuse == -1)
return 0;
- if (VALID_MEMBER(page_objects)) {
- objects_vaddr = cpu_partial + OFFSET(page_objects);
- if (si->flags & SLAB_BITFIELD)
- objects_vaddr += sizeof(ushort);
- if (!readmem(objects_vaddr, KVADDR,
- &cpu_partial_objects, sizeof(ushort),
- "page.objects", RETURN_ON_ERROR))
- return 0;
- if (si->flags & SLAB_BITFIELD) {
- if (__BYTE_ORDER == __LITTLE_ENDIAN) {
- cpu_partial_objects <<= 1;
- cpu_partial_objects >>= 1;
- }
- if (__BYTE_ORDER == __BIG_ENDIAN)
- cpu_partial_objects >>= 1;
- }
- if (cpu_partial_objects == (short)(-1))
- return 0;
- free_objects +=
- cpu_partial_objects - cpu_partial_inuse;
- }
+
+ cpu_partial_objects = slub_page_objects(si,
+ cpu_partial);
+ if (!cpu_partial_objects)
+ return 0;
+ free_objects += cpu_partial_objects - cpu_partial_inuse;
+
readmem(cpu_partial + OFFSET(page_next), KVADDR,
&cpu_partial, sizeof(ulong), "page.next",
RETURN_ON_ERROR);
@@ -18011,14 +18044,12 @@ get_kmem_cache_slub_data(long cmd, struc
KVADDR, &inuse, sizeof(short),
"page inuse", RETURN_ON_ERROR))
return FALSE;
- if (!cpu_freelist)
- if (!readmem(cpu_slab_ptr + OFFSET(page_freelist),
- KVADDR, &cpu_freelist, sizeof(ulong),
- "page freelist", RETURN_ON_ERROR))
- return FALSE;
+ objects = slub_page_objects(si, cpu_slab_ptr);
+ if (!objects)
+ return FALSE;
- free_objects +=
- count_free_objects(si, cpu_freelist);
+ free_objects += objects - inuse;
+ free_objects += count_free_objects(si, cpu_freelist);
free_objects += count_cpu_partial(si, i);
if (!node_total_avail)
@@ -18255,7 +18286,7 @@ static int
do_slab_slub(struct meminfo *si, int verbose)
{
physaddr_t paddr;
- ulong vaddr, objects_vaddr;
+ ulong vaddr;
ushort inuse, objects;
ulong freelist, cpu_freelist, cpu_slab_ptr;
int i, free_objects, cpu_slab, is_free, node;
@@ -18287,50 +18318,17 @@ do_slab_slub(struct meminfo *si, int ver
if (!readmem(si->slab + OFFSET(page_freelist), KVADDR, &freelist,
sizeof(void *), "page.freelist", RETURN_ON_ERROR))
return FALSE;
- /*
- * Pre-2.6.27, the object count and order were fixed in the
- * kmem_cache structure. Now they may change, say if a high
- * order slab allocation fails, so the per-slab object count
- * is kept in the slab.
- */
- if (VALID_MEMBER(page_objects)) {
- objects_vaddr = si->slab + 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))
- return FALSE;
- /*
- * Strip page.frozen bit.
- */
- if (si->flags & SLAB_BITFIELD) {
- if (__BYTE_ORDER == __LITTLE_ENDIAN) {
- objects <<= 1;
- objects >>= 1;
- }
- if (__BYTE_ORDER == __BIG_ENDIAN)
- objects >>= 1;
- }
-
- if (CRASHDEBUG(1) && (objects != si->objects))
- error(NOTE, "%s: slab: %lx oo objects: %ld "
- "slab objects: %d\n",
- si->curname, si->slab,
- si->objects, objects);
- if (objects == (ushort)(-1)) {
- error(INFO, "%s: slab: %lx invalid page.objects: -1\n",
- si->curname, si->slab);
- return FALSE;
- }
- } else
- objects = (ushort)si->objects;
+ objects = slub_page_objects(si, si->slab);
+ if (!objects)
+ return FALSE;
if (!verbose) {
DUMP_SLAB_INFO_SLUB();
return TRUE;
}
+ cpu_freelist = 0;
for (i = 0, cpu_slab = -1; i < kt->cpus; i++) {
cpu_slab_ptr = get_cpu_slab_ptr(si, i, &cpu_freelist);
@@ -18342,11 +18340,15 @@ do_slab_slub(struct meminfo *si, int ver
* Later slub scheme uses the per-cpu freelist
* so count the free objects by hand.
*/
- if (cpu_freelist)
- freelist = cpu_freelist;
- if ((free_objects = count_free_objects(si, freelist)) < 0)
+ if ((free_objects = count_free_objects(si, cpu_freelist)) < 0)
return FALSE;
- inuse = si->objects - free_objects;
+ /*
+ * If the object is freed on foreign cpu, the
+ * object is liked to page->freelist.
+ */
+ if (freelist)
+ free_objects += objects - inuse;
+ inuse = objects - free_objects;
break;
}
}
@@ -18377,28 +18379,31 @@ do_slab_slub(struct meminfo *si, int ver
for (p = vaddr; p < vaddr + objects * si->size; p += si->size) {
hq_open();
is_free = FALSE;
- for (is_free = 0, q = freelist; q;
- q = get_freepointer(si, (void *)q)) {
+ /* Search an object on both of freelist and cpu_freelist */
+ ulong lists[] = { freelist, cpu_freelist, };
+ for (int i = 0; i < sizeof(lists) / sizeof(lists[0]); i++) {
+ for (is_free = 0, q = lists[i]; q;
+ q = get_freepointer(si, (void *)q)) {
- if (q == BADADDR) {
- hq_close();
- return FALSE;
- }
- if (q & PAGE_MAPPING_ANON)
- break;
- if (p == q) {
- is_free = TRUE;
- break;
- }
- if (!hq_enter(q)) {
- hq_close();
- error(INFO,
- "%s: slab: %lx duplicate freelist object: %lx\n",
- si->curname, si->slab, q);
- return FALSE;
+ if (q == BADADDR) {
+ hq_close();
+ return FALSE;
+ }
+ if (q & PAGE_MAPPING_ANON)
+ break;
+ if (p == q) {
+ is_free = TRUE;
+ goto found_object;
+ }
+ if (!hq_enter(q)) {
+ hq_close();
+ error(INFO, "%s: slab: %lx duplicate freelist object: %lx\n",
+ si->curname, si->slab, q);
+ return FALSE;
+ }
}
-
}
+ found_object:
hq_close();
if (si->flags & ADDRESS_SPECIFIED) {
@@ -18677,7 +18682,7 @@ compound_head(ulong page)
long
count_partial(ulong node, struct meminfo *si, ulong *free)
{
- ulong list_head, next, last, objects_vaddr;
+ ulong list_head, next, last;
short inuse, objects;
ulong total_inuse;
ulong count = 0;
@@ -18708,31 +18713,11 @@ count_partial(ulong node, struct meminfo
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 == (short)(-1)) {
- error(INFO, "%s: slab: %lx invalid page.objects: -1\n",
- si->curname, last);
+ objects = slub_page_objects(si, last);
+ if (!objects) {
hq_close();
return -1;
}
-
*free += objects - inuse;
}
_