Skip to contentSkip to conversation

Show slab memory usage on OOM and SysRq-M

PATCH In review Pavel Emelianov 3 participants 8 messages Apr 17, 2007 – Apr 18, 2007

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 Show slab memory usage on OOM and SysRq-M

The out_of_memory() function and SysRq-M handler call show_mem() to show the current memory usage state.

This is also helpful to see which slabs are the largest in the system.

---

drivers/char/sysrq.c +1 -0
index 39cc318..7c27647 100644
@@ -234,6 +234,7 @@ static struct sysrq_key_op sysrq_showsta
 static void sysrq_handle_showmem(int key, struct tty_struct *tty)
 {
 	show_mem();
+	show_slabs();
 }
 static struct sysrq_key_op sysrq_showmem_op = {
 	.handler	= sysrq_handle_showmem,
include/linux/mm.h +0 -0
include/linux/slab.h +6 -0
index 67425c2..1e2919d 100644
@@ -170,6 +170,12 @@ static inline void *kzalloc(size_t size,
 }
 #endif
 
+#ifdef CONFIG_SLAB
+extern void show_slabs(void);
+#else
+#define show_slabs(void)	do { } while (0)
+#endif
+
 #ifndef CONFIG_NUMA
 static inline void *kmalloc_node(size_t size, gfp_t flags, int node)
 {
mm/oom_kill.c +1 -0
index 4bdc7c0..aefdd06 100644
@@ -409,6 +409,7 @@ void out_of_memory(struct zonelist *zone
 			current->comm, gfp_mask, order, current->oomkilladj);
 		dump_stack();
 		show_mem();
+		show_slabs();
 	}
 
 	cpuset_lock();
mm/slab.c +74 -0

103 lines not on this page · raw message

#1 lore raw

Hi,

Quoted 5 lines · Pavel Emelianov

The out_of_memory() function and SysRq-M handler call show_mem() to show the current memory usage state.

This is also helpful to see which slabs are the largest in the system.

Makes sense.

mm/slab.c static inline void init_lock_keys(void) · line 752

Quoted 9 lines · Pavel Emelianov
mm/slab.c +1 -0
index 21b3c61..9a5829a 100644
@@ -749,6 +749,7 @@ static inline void init_lock_keys(void)
  * 2. Protect sanity of cpu_online_map against cpu hotplug events
  */
 static DEFINE_MUTEX(cache_chain_mutex);
+static DEFINE_SPINLOCK(cache_chain_lock);

So, now we have two locks protecting cache_chain? Please explain why you can't use the mutex.

Quoted 17 lines
mm/slab.c +17 -0
+static unsigned long get_cache_size(struct kmem_cache *cachep)
+{
+       unsigned long slabs;
+       struct kmem_list3 *l3;
+       struct list_head *lh;
+       int node;
+
+       slabs = 0;
+
+       for_each_online_node (node) {
+               l3 = cachep->nodelists[node];
+               if (l3 == NULL)
+                       continue;
+
+               spin_lock(&l3->list_lock);
+               list_for_each (lh, &l3->slabs_full)
+                       slabs++;

mm/slab.c const struct seq_operations slabstats_op · line 4581

Quoted 10 lines
mm/slab.c +10 -0
+               list_for_each (lh, &l3->slabs_partial)
+                       slabs++;
+               list_for_each (lh, &l3->slabs_free)
+                       slabs++;
+               spin_unlock(&l3->list_lock);
+       }
+
+       return slabs * ((PAGE_SIZE << cachep->gfporder) +
+               (OFF_SLAB(cachep) ? cachep->slabp_cache->buffer_size : 0));
+}

Considering you're doing this at out_of_memory() time, wouldn't it make more sense to add a ->nr_pages to struct kmem_cache and do the tracking in kmem_getpages/kmem_freepages?

I would also drop the OFF_SLAB bits because it really doesn't matter that much for your purposes. Besides, you're already per-node and per-CPU caches here which attribute to much more memory on NUMA setups for example.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#2 lore raw
Quoted 10 lines · Pekka Enberg

Hi,

Quoted 5 lines · Pavel Emelianov

The out_of_memory() function and SysRq-M handler call show_mem() to show the current memory usage state.

This is also helpful to see which slabs are the largest in the system.

Makes sense.

Thanks! :)

Quoted 5 lines · Pavel Emelianov
Quoted 4 lines
mm/slab.c +0 -0
index 21b3c61..9a5829a 100644
Quoted 8 lines

mm/slab.c static inline void init_lock_keys(void) · line 752

Quoted 5 lines
mm/slab.c +1 -0
@@ -749,6 +749,7 @@ static inline void init_lock_keys(void)
  * 2. Protect sanity of cpu_online_map against cpu hotplug events
  */
 static DEFINE_MUTEX(cache_chain_mutex);
+static DEFINE_SPINLOCK(cache_chain_lock);

So, now we have two locks protecting cache_chain? Please explain why you can't use the mutex.

Because OOM can actually happen with this mutex locked. For example kmem_cache_create() locks it and calls kmalloc(), or write to /proc/slabinfo also locks it and calls do_tune_cpu_caches(). This is very rare case and the deadlock is VERY unlikely to happen, but it will be very disappointing if it happens.

Moreover, I put the call to show_slabs() into sysrq handler, so it may be called from atomic context.

Making mutex_trylock() is possible, but we risk of loosing this info in case OOM happens while the mutex is locked for cache shrinking (see cache_reap() for example)...

So we have a choice - either we have an additional lock on a slow and rare paths and show this info for sure, or we do not have a lock, but have a risk of loosing this info.

Quoted 24 lines
Quoted 24 lines
mm/slab.c +24 -0
+static unsigned long get_cache_size(struct kmem_cache *cachep)
+{
+       unsigned long slabs;
+       struct kmem_list3 *l3;
+       struct list_head *lh;
+       int node;
+
+       slabs = 0;
+
+       for_each_online_node (node) {
+               l3 = cachep->nodelists[node];
+               if (l3 == NULL)
+                       continue;
+
+               spin_lock(&l3->list_lock);
+               list_for_each (lh, &l3->slabs_full)
+                       slabs++;
+               list_for_each (lh, &l3->slabs_partial)
+                       slabs++;
+               list_for_each (lh, &l3->slabs_free)
+                       slabs++;
+               spin_unlock(&l3->list_lock);
+       }
+
Quoted 8 lines
Quoted 4 lines
mm/slab.c +2 -0
+       return slabs * ((PAGE_SIZE << cachep->gfporder) +
+               (OFF_SLAB(cachep) ? cachep->slabp_cache->buffer_size :

0));
+}

Considering you're doing this at out_of_memory() time, wouldn't it make more sense to add a ->nr_pages to struct kmem_cache and do the tracking in kmem_getpages/kmem_freepages?

Sounds good.

Quoted 4 lines

I would also drop the OFF_SLAB bits because it really doesn't matter that much for your purposes. Besides, you're already per-node and per-CPU caches here which attribute to much more memory on NUMA setups for example.

This gives us a more precise information :) The precision is less than 1% so if nobody likes/needs it, this may be dropped.

Pavel.

#3 lore raw

Hi Pavel,

Quoted 2 lines · At some point in time, I wrote:
Quoted 2 lines

So, now we have two locks protecting cache_chain? Please explain why you can't use the mutex.

Quoted 9 lines · Pavel Emelianov

Because OOM can actually happen with this mutex locked. For example kmem_cache_create() locks it and calls kmalloc(), or write to /proc/slabinfo also locks it and calls do_tune_cpu_caches(). This is very rare case and the deadlock is VERY unlikely to happen, but it will be very disappointing if it happens.

Moreover, I put the call to show_slabs() into sysrq handler, so it may be called from atomic context.

Quoted 7 lines

Making mutex_trylock() is possible, but we risk of loosing this info in case OOM happens while the mutex is locked for cache shrinking (see cache_reap() for example)...

So we have a choice - either we have an additional lock on a slow and rare paths and show this info for sure, or we do not have a lock, but have a risk of loosing this info.

I don't worry about performance as much I do about maintenance. Do you know if mutex_trylock() is a problem in practice? Could we perhaps fix the worst offenders who are holding cache_chain_mutex for a long time?

In any case, if we do end up adding the lock, please add a BIG FAT COMMENT explaining why we have it.

Quoted 4 lines · At some point in time, I wrote:
Quoted 4 lines

I would also drop the OFF_SLAB bits because it really doesn't matter that much for your purposes. Besides, you're already per-node and per-CPU caches here which attribute to much more memory on NUMA setups for example.

Quoted 2 lines · Pavel Emelianov

This gives us a more precise information :) The precision is less than 1% so if nobody likes/needs it, this may be dropped.

My point is that the "precision" is useless here. We probably waste more memory in the caches which are not accounted here. So I'd just drop it.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#4 lore raw
Quoted 20 lines · Pekka J Enberg

Hi Pavel,

Quoted 2 lines · At some point in time, I wrote:
Quoted 2 lines

So, now we have two locks protecting cache_chain? Please explain why you can't use the mutex.

Quoted 13 lines · Pavel Emelianov

Because OOM can actually happen with this mutex locked. For example kmem_cache_create() locks it and calls kmalloc(), or write to /proc/slabinfo also locks it and calls do_tune_cpu_caches(). This is very rare case and the deadlock is VERY unlikely to happen, but it will be very disappointing if it happens.

Moreover, I put the call to show_slabs() into sysrq handler, so it may be called from atomic context.

Making mutex_trylock() is possible, but we risk of loosing this info in case OOM happens while the mutex is locked for cache shrinking (see cache_reap() for example)...

Quoted 6 lines
Quoted 3 lines

So we have a choice - either we have an additional lock on a slow and rare paths and show this info for sure, or we do not have a lock, but have a risk of loosing this info.

I don't worry about performance as much I do about maintenance. Do you know if mutex_trylock() is a problem in practice? Could we perhaps fix

No, this mutex is unlocked most of the time, but I have already been in the situations when the information that might not get on the screen did not actually get there in the most inappropriate moment :)

Quoted 4 lines

the worst offenders who are holding cache_chain_mutex for a long time?

In any case, if we do end up adding the lock, please add a BIG FAT COMMENT explaining why we have it.

OK. I will keep this lock unless someone have a forcible argument for not doing this.

Quoted 6 lines
Quoted 4 lines · At some point in time, I wrote:
Quoted 4 lines

I would also drop the OFF_SLAB bits because it really doesn't matter that much for your purposes. Besides, you're already per-node and per-CPU caches here which attribute to much more memory on NUMA setups for example.

Quoted 6 lines
Quoted 2 lines · Pavel Emelianov

This gives us a more precise information :) The precision is less than 1% so if nobody likes/needs it, this may be dropped.

My point is that the "precision" is useless here. We probably waste more memory in the caches which are not accounted here. So I'd just drop it.

OK. I will rework the patch according to your comments.

Pavel.

#5 lore raw
In reply to Pekka Enberg
Quoted 26 lines · On Tue, 17 Apr 2007 16:22:48 +0300 "Pekka Enberg" <[email protected]>…

Hi,

Quoted 23 lines · Pavel Emelianov
mm/slab.c +23 -0
+static unsigned long get_cache_size(struct kmem_cache *cachep)
+{
+       unsigned long slabs;
+       struct kmem_list3 *l3;
+       struct list_head *lh;
+       int node;
+
+       slabs = 0;
+
+       for_each_online_node (node) {
+               l3 = cachep->nodelists[node];
+               if (l3 == NULL)
+                       continue;
+
+               spin_lock(&l3->list_lock);
+               list_for_each (lh, &l3->slabs_full)
+                       slabs++;
+               list_for_each (lh, &l3->slabs_partial)
+                       slabs++;
+               list_for_each (lh, &l3->slabs_free)
+                       slabs++;
+               spin_unlock(&l3->list_lock);
+       }
Quoted 9 lines

mm/slab.c const struct seq_operations slabstats_op · line 4581

Quoted 4 lines
mm/slab.c +4 -0
+
+       return slabs * ((PAGE_SIZE << cachep->gfporder) +
+               (OFF_SLAB(cachep) ? cachep->slabp_cache->buffer_size : 0));
+}

Considering you're doing this at out_of_memory() time, wouldn't it make more sense to add a ->nr_pages to struct kmem_cache and do the tracking in kmem_getpages/kmem_freepages?

To avoid a deadlock ? yes...

This nr_pages should be in struct kmem_list3, not in struct kmem_cache, or else you defeat NUMA optimizations if touching a field in kmem_cache at kmem_getpages()/kmem_freepages() time.

       for_each_online_node (node) {
               l3 = cachep->nodelists[node];
               if (l3)
                   slabs += l3->nr_pages; /* dont lock l3->list_lock */
       }

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#6 lore raw
Quoted 3 lines · Eric Dumazet

This nr_pages should be in struct kmem_list3, not in struct kmem_cache, or else you defeat NUMA optimizations if touching a field in kmem_cache at kmem_getpages()/kmem_freepages() time.

We already touch ->flags, ->gfpflags, and ->gfporder in kmem_getpages(). Sorry for my ignorance, but how is this different?

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#7 lore raw
Quoted 8 lines · On Wed, 18 Apr 2007 09:17:19 +0300 (EEST) Pekka J Enberg…
Quoted 3 lines · Eric Dumazet

This nr_pages should be in struct kmem_list3, not in struct kmem_cache, or else you defeat NUMA optimizations if touching a field in kmem_cache at kmem_getpages()/kmem_freepages() time.

We already touch ->flags, ->gfpflags, and ->gfporder in kmem_getpages(). Sorry for my ignorance, but how is this different?

Those fields are read. Thats OK, because several CPUS might share all those without problem.

But modifying one field in kmem_cache would invalidate one cache line for all cpus that would have to reload it later.

This is what we call "false sharing" or cache line ping pongs

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>