]> granicus.if.org Git - zfs/commitdiff
Ensure a minimum of one slab is reclaimed
authorBrian Behlendorf <behlendorf1@llnl.gov>
Tue, 1 May 2012 21:27:29 +0000 (14:27 -0700)
committerBrian Behlendorf <behlendorf1@llnl.gov>
Mon, 7 May 2012 18:54:28 +0000 (11:54 -0700)
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 <surya1@llnl.gov>
Signed-off-by: Brian Behlendorf <behlendorf1@llnl.gov>
Closes #107

module/spl/spl-kmem.c

index f7d5f7e86b31129eba02d589ebbe2e610703cadc..e1d74d3c05eabc90358852583cd84a906ca3d1fd 100644 (file)
@@ -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);