[Crash-utility] More fixes for kmem on slabs

Bob Montgomery bob.montgomery at hp.com
Wed Feb 8 23:49:31 UTC 2012


More testing revealed a machine in our stable that either failed to
initialize kmem:

please wait... (gathering kmem slab cache data)
crash-6.0.3: page excluded: kernel virtual address: ffff8801263d6000  type: "kmem_cache buffer"

crash-6.0.3: unable to initialize kmem slab cache subsystem

Or succeeded on initialize and then failed on a kmem -s command:

crash-6.0.3> kmem -s
CACHE            NAME                 OBJSIZE  ALLOCATED     TOTAL  SLABS  SSIZE
Segmentation fault


The problem is that the array struct at the end of kmem_cache remains declared as
32 elements, but for all dynamically allocated copies, is actually trimmed down
to nr_cpu_ids in length.

crash-6.0.3.best> struct kmem_cache
struct kmem_cache {
    unsigned int batchcount;
...

    struct list_head next;
    struct kmem_list3 **nodelists;
    struct array_cache *array[32];
}
SIZE: 368


On my normal play machine, nr_cpu_ids = 32 and actual cpus = 16.

On the failing machine, nr_cpus_ids and actual cpus are both 2.

Two problems occur:

1)  max_cpudata_limit traverses the array until it finds a 0x0 or
reaches the real size.  On the 2-cpu system, the "third" element in the
array belonged elsewhere, was non-zero, and pointed to data that caused
the apparent limit to be 0xffffffffffff8801, which didn't work well as
a length in a memcopy.

2) kmem_cache structs can be allocated near enough to the edge of a page
that the old incorrect length crosses the page boundary, even though the
real smaller structure fits in the page.  That caused a readmem of the
structure to cross into a coincidentally missing page in the dump.

This patch fixes both of those (after wrestling ARRAY_LENGTH to the
ground), but *does not* fix the similar page crossing problem when I try
to use a "struct kmem_cache" command on the particular structure at the
end of the page.

Reference this unfortunate comment in include/linux/slab_def.h:

/* 6) per-cpu/per-node data, touched during every alloc/free */
        /*
         * We put array[] at the end of kmem_cache, because we want to size
         * this array to nr_cpu_ids slots instead of NR_CPUS
         * (see kmem_cache_init())
         * We still use [NR_CPUS] and not [1] or [0] because cache_cache
         * is statically defined, so we reserve the max number of cpus.
         */
        struct kmem_list3 **nodelists;
        struct array_cache *array[NR_CPUS];
        /*
         * Do not add fields after array[]
         */
};

Bob Montgomery

-------------- next part --------------
A non-text attachment was scrubbed...
Name: kmem_cache_array.patch
Type: text/x-patch
Size: 1903 bytes
Desc: not available
URL: <http://listman.redhat.com/archives/crash-utility/attachments/20120208/f64837bc/attachment.bin>


More information about the Crash-utility mailing list