From b78d4b9d98b8486199ba9072d7fc345121a4601f Mon Sep 17 00:00:00 2001 From: Brian Behlendorf Date: Tue, 1 May 2012 14:27:29 -0700 Subject: [PATCH] Ensure a minimum of one slab is reclaimed To minimize the chance of triggering an OOM during direct reclaim. The kmem caches have been improved to make a best effort to reclaim at least one slab when a reclaim function is registered. This helps avoid the case where objects are released but they are spread over multiple slabs so no memory gets reclaimed. Care has been taken to avoid deadlocking if the reclaim function is unable to make forward progress. Additionally, the reclaim function may be skipped entirely if there are already free slabs which can be safely reaped. Signed-off-by: Prakash Surya Signed-off-by: Brian Behlendorf Closes #107 --- module/spl/spl-kmem.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/module/spl/spl-kmem.c b/module/spl/spl-kmem.c index f7d5f7e86..e1d74d3c0 100644 --- a/module/spl/spl-kmem.c +++ b/module/spl/spl-kmem.c @@ -1923,8 +1923,38 @@ spl_kmem_cache_reap_now(spl_kmem_cache_t *skc, int count) atomic_inc(&skc->skc_ref); - if (skc->skc_reclaim) - skc->skc_reclaim(skc->skc_private); + /* + * When a reclaim function is available it may be invoked repeatedly + * until at least a single slab can be freed. This ensures that we + * do free memory back to the system. This helps minimize the chance + * of an OOM event when the bulk of memory is used by the slab. + * + * When free slabs are already available the reclaim callback will be + * skipped. Additionally, if no forward progress is detected despite + * a reclaim function the cache will be skipped to avoid deadlock. + * + * Longer term this would be the correct place to add the code which + * repacks the slabs in order minimize fragmentation. + */ + if (skc->skc_reclaim) { + uint64_t objects = UINT64_MAX; + int do_reclaim; + + do { + spin_lock(&skc->skc_lock); + do_reclaim = + (skc->skc_slab_total > 0) && + ((skc->skc_slab_total - skc->skc_slab_alloc) == 0) && + (skc->skc_obj_alloc < objects); + + objects = skc->skc_obj_alloc; + spin_unlock(&skc->skc_lock); + + if (do_reclaim) + skc->skc_reclaim(skc->skc_private); + + } while (do_reclaim); + } /* Reclaim from the cache, ignoring it's age and delay. */ spl_slab_reclaim(skc, count, 1);