<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
Sharyathi Nagesh wrote:
<blockquote TYPE=CITE>This is the patch after making following changes
<br>o Created a new function gather_cpudata_list_v2_nodes().
<br>o gather_cpudata_list_v2_nodes() will be called for each node and it
will update avail with corresponding value of shared memory..
<br>o gather_cpudata_list_v2_nodes() is called inside do_slab_chain_percpu_v2_nodes
during SLAB_WALKTHROUGH instead outside as earlier..
<br>o Have removed the commented out section of SLAB_WALKTHROUGH (specified
slab).
<br>o updated with FREEBUF at possible exit points.
<br>o updated dump_vm_table() to dump vt->kmem_cache_len_nodes
<p>Opens
<br>o The si->found field was not getting set for the dump I analysed.
so if(si->found) part of the code was not getting executed.
<br>Needs to be checked for this case.
<p>Please go through the patch and let me know of your opinions.
<br>Thanks
<br>Sharyathi Nagesh</blockquote>
<tt>Hi Sharyathi,</tt><tt></tt>
<p><tt>OK -- it's getting there, but there are a couple problems with</tt>
<br><tt>this second patch.</tt><tt></tt>
<p><tt>The purpose of gather_cpudata_list_v2_nodes() is to fill</tt>
<br><tt>several arrays of cached objects:</tt><tt></tt>
<p><tt>(1) the list of per-cpu objects attached to the kmem_cache,</tt>
<br><tt>    referenced by the "array_cache[NR_CPUS]" member:</tt><tt></tt>
<p><tt>      struct kmem_cache {</tt>
<br><tt>          ...</tt>
<br><tt>          struct array_cache
*array[NR_CPUS];</tt>
<br><tt>          ...</tt><tt></tt>
<p><tt>    So, for each cpu, its cached per-cpu objects
from the</tt>
<br><tt>    above get stored in crash in each dynamically-allocated,</tt>
<br><tt>    per-cpu "cpudata" array:</tt><tt></tt>
<p><tt>      struct si_meminfo {</tt>
<br><tt>          ...</tt>
<br><tt>          ulong *cpudata[NR_CPUS];</tt><tt></tt>
<p><tt>    Now that gather_cpudata_list_v2_nodes() is being
called</tt>
<br><tt>    multiple times instead of just once, the loading
of each</tt>
<br><tt>    of the cpudata[NR_CPUS] array is being done
redundantly.</tt>
<br><tt>    This is harmless, although you could probably
just look</tt>
<br><tt>    at the "index" argument you now pass in, and
if it's</tt>
<br><tt>    non-zero, the redundant reading of the cached
per-cpu</tt>
<br><tt>    objects above can be avoided.</tt><tt></tt>
<p><tt>(2) The second function of the current gather_cpudata_list_v2()</tt>
<br><tt>    is to gather the "shared" list from the singular
kmem_list3</tt>
<br><tt>    structure:</tt><tt></tt>
<p><tt>      struct kmem_list3 {</tt>
<br><tt>          ...</tt>
<br><tt>          struct array_cache     
*shared;</tt><tt></tt>
<p><tt>    Since this has (until now) been a single list
per kmem_cache,</tt>
<br><tt>    crash has been storing the list of shared objects
in a singular</tt>
<br><tt>    dynamically-allocated array:</tt>
<p><tt>      struct si_meminfo {</tt>
<br><tt>          ...</tt>
<br><tt>          ulong *shared_array_cache;</tt>
<br><tt> </tt>
<br><tt>and that's the problem with your new gather_cpudata_list_v2_nodes().</tt>
<br><tt>It's using a singular "shared_array_cache" array multiple times,</tt>
<br><tt>once for each "index" of kmem_list3 structure.  So it ends
up</tt>
<br><tt>over-writing the array each time, and so the si->shared_array_cache</tt>
<br><tt>ends up containing the list of objects from the *last* "index"</tt>
<br><tt>only.</tt><tt></tt>
<p><tt>> o The si->found field was not getting set for the dump I analysed.</tt>
<br><tt>>   so if(si->found) part of the code was not getting
executed.</tt><tt></tt>
<p><tt>Given the above, this makes sense, since later on, when the</tt>
<br><tt>shared_array_cache list is searched for a particular object, it</tt>
<br><tt>most likely won't find it unless it happened to be contained in</tt>
<br><tt>the last "index'd" set of objects.</tt><tt></tt>
<p><tt>Now this can be solved in a couple of ways:</tt><tt></tt>
<p><tt>The dynamic allocation of the singular shared_array_cache array</tt>
<br><tt>can be increased by multiplying its currently predetermined</tt>
<br><tt>max-size *times* the vt->kmem_cache_len_nodes.  And then each</tt>
<br><tt>time gather_cpudata_list_v2_nodes() is called, it could read</tt>
<br><tt>the next set of objects into the location in the array where</tt>
<br><tt>it left off the last time, i.e., the first non-zero entry.</tt><tt></tt>
<p><tt>Alternatively, a different array could be used, although</tt>
<br><tt>it couldn't use NR_CPUS as a delimiter, but would have</tt>
<br><tt>to be aware of a maximum-number-of-numa-nodes, which</tt>
<br><tt>would be kind of ugly.  And if you use a new array, then</tt>
<br><tt>the check_shared_list() function would have to be updated</tt>
<br><tt>to check all of the lists instead of the current single</tt>
<br><tt>list.</tt><tt></tt>
<p><tt>BTW, there's a bug in check_shared_list() that I just</tt>
<br><tt>noticed now -- it's harmless, but dumb:</tt><tt></tt>
<p><tt>static int</tt>
<br><tt>check_shared_list(struct meminfo *si, ulong obj)</tt>
<br><tt>{</tt>
<br><tt>        int i;</tt><tt></tt>
<p><tt>        if (INVALID_MEMBER(kmem_list3_shared)
||</tt>
<br><tt>           
!si->shared_array_cache)</tt>
<br><tt>               
return FALSE;</tt><tt></tt>
<p><tt>        for (i = 0; i < si->shared_array_cache[i];
i++) {</tt>
<br><tt>               
if (si->shared_array_cache[i] == obj)</tt>
<br><tt>                       
return TRUE;</tt>
<br><tt>        }</tt><tt></tt>
<p><tt>        return FALSE;</tt>
<br><tt>}</tt><tt></tt>
<p><tt>The for loop should be:</tt><tt></tt>
<p><tt>        for (i = 0; si->shared_array_cache[i];
i++) {</tt><tt></tt>
<p><tt>It works now by dumb luck, stopping at the first non-zero</tt>
<br><tt>entry (object address), which is what it's supposed</tt>
<br><tt>to do.</tt><tt></tt>
<p><tt>Finally, there's one other "gotcha" with this scheme.</tt>
<br><tt>During intitialization, the value of vt->kmem_max_limit</tt>
<br><tt>is determined in max_cpudata_limit, and later on it's</tt>
<br><tt>used to allocate the object array:</tt><tt></tt>
<p><tt>  si->shared_array_cache = (ulong *)</tt>
<br><tt>      GETBUF(vt->kmem_max_limit * sizeof(ulong));</tt><tt></tt>
<p><tt>vt->kmem_max_limit is calculated during initialization</tt>
<br><tt>by determining the maximum cached object list size amongst</tt>
<br><tt>both the kmem_cache->array[NR_CPUS] and the singular</tt>
<br><tt>kmem_list3->array.  Therefore, the max_cpudata_limit()</tt>
<br><tt>function is only checking the first one:</tt><tt></tt>
<p><tt>        /*</tt>
<br><tt>         *  If the
shared list can be accessed, check its size as well.</tt>
<br><tt>         */</tt>
<br><tt>        if (VALID_MEMBER(kmem_list3_shared)
&&</tt>
<br><tt>           
VALID_MEMBER(kmem_cache_s_lists) &&</tt>
<br><tt>           
readmem(cache+OFFSET(kmem_cache_s_lists)+OFFSET(kmem_list3_shared),</tt>
<br><tt>           
KVADDR, &shared, sizeof(void *), "kmem_list3 shared",</tt>
<br><tt>           
RETURN_ON_ERROR|QUIET) &&</tt>
<br><tt>           
readmem(shared+OFFSET(array_cache_limit),</tt>
<br><tt>           
KVADDR, &limit, sizeof(int), "shared array_cache limit",</tt>
<br><tt>           
RETURN_ON_ERROR|QUIET)) {</tt>
<br><tt>               
if (limit > max_limit)</tt>
<br><tt>                       
max_limit = limit;</tt>
<br><tt>        }</tt><tt></tt>
<p><tt>Like your other new functions, if there's more than one list,</tt>
<br><tt>they should probably all be checked for the largest one.</tt><tt></tt>
<p><tt>Clear as mud?</tt><tt></tt>
<p><tt>Dave</tt>
<br><tt></tt> </html>