Gitweb:     
http://git.kernel.org/git/?p=linux/kernel/git/torvalds/linux-2.6.git;a=commit;h=1ceef40249f21eceabf8633934d94962e7d8e1d7
Commit:     1ceef40249f21eceabf8633934d94962e7d8e1d7
Parent:     fcda3d89bf1366f6801447eab2d8a75ac5b9c4ce
Author:     Christoph Lameter <[EMAIL PROTECTED]>
AuthorDate: Tue Aug 7 15:11:48 2007 -0700
Committer:  Christoph Lameter <[EMAIL PROTECTED]>
CommitDate: Thu Aug 9 21:57:16 2007 -0700

    SLUB: Fix dynamic dma kmalloc cache creation
    
    The dynamic dma kmalloc creation can run into trouble if a
    GFP_ATOMIC allocation is the first one performed for a certain size
    of dma kmalloc slab.
    
    - Move the adding of the slab to sysfs into a workqueue
      (sysfs does GFP_KERNEL allocations)
    - Do not call kmem_cache_destroy() (uses slub_lock)
    - Only acquire the slub_lock once and--if we cannot wait--do a trylock.
    
      This introduces a slight risk of the first kmalloc(x, GFP_DMA|GFP_ATOMIC)
      for a range of sizes failing due to another process holding the slub_lock.
      However, we only need to acquire the spinlock once in order to establish
      each power of two DMA kmalloc cache. The possible conflict is with the
      slub_lock taken during slab management actions (create / remove slab 
cache).
    
      It is rather typical that a driver will first fill its buffers using
      GFP_KERNEL allocations which will wait until the slub_lock can be 
acquired.
      Drivers will also create its slab caches first outside of an atomic
      context before starting to use atomic kmalloc from an interrupt context.
    
      If there are any failures then they will occur early after boot or when
      loading of multiple drivers concurrently. Drivers can already accomodate
      failures of GFP_ATOMIC for other reasons. Retries will then create the 
slab.
    
    Signed-off-by: Christoph Lameter <[EMAIL PROTECTED]>
---
 mm/slub.c |   59 +++++++++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 45 insertions(+), 14 deletions(-)

diff --git a/mm/slub.c b/mm/slub.c
index 64fd80b..69d02e3 100644
--- a/mm/slub.c
+++ b/mm/slub.c
@@ -211,7 +211,8 @@ static inline void ClearSlabDebug(struct page *page)
 #define MAX_OBJECTS_PER_SLAB 65535
 
 /* Internal SLUB flags */
-#define __OBJECT_POISON 0x80000000     /* Poison object */
+#define __OBJECT_POISON                0x80000000 /* Poison object */
+#define __SYSFS_ADD_DEFERRED   0x40000000 /* Not yet visible via sysfs */
 
 /* Not all arches define cache_line_size */
 #ifndef cache_line_size
@@ -2277,10 +2278,26 @@ panic:
 }
 
 #ifdef CONFIG_ZONE_DMA
+
+static void sysfs_add_func(struct work_struct *w)
+{
+       struct kmem_cache *s;
+
+       down_write(&slub_lock);
+       list_for_each_entry(s, &slab_caches, list) {
+               if (s->flags & __SYSFS_ADD_DEFERRED) {
+                       s->flags &= ~__SYSFS_ADD_DEFERRED;
+                       sysfs_slab_add(s);
+               }
+       }
+       up_write(&slub_lock);
+}
+
+static DECLARE_WORK(sysfs_add_work, sysfs_add_func);
+
 static noinline struct kmem_cache *dma_kmalloc_cache(int index, gfp_t flags)
 {
        struct kmem_cache *s;
-       struct kmem_cache *x;
        char *text;
        size_t realsize;
 
@@ -2289,22 +2306,36 @@ static noinline struct kmem_cache 
*dma_kmalloc_cache(int index, gfp_t flags)
                return s;
 
        /* Dynamically create dma cache */
-       x = kmalloc(kmem_size, flags & ~SLUB_DMA);
-       if (!x)
-               panic("Unable to allocate memory for dma cache\n");
+       if (flags & __GFP_WAIT)
+               down_write(&slub_lock);
+       else {
+               if (!down_write_trylock(&slub_lock))
+                       goto out;
+       }
+
+       if (kmalloc_caches_dma[index])
+               goto unlock_out;
 
        realsize = kmalloc_caches[index].objsize;
-       text = kasprintf(flags & ~SLUB_DMA, "kmalloc_dma-%d",
-                       (unsigned int)realsize);
-       s = create_kmalloc_cache(x, text, realsize, flags);
-       down_write(&slub_lock);
-       if (!kmalloc_caches_dma[index]) {
-               kmalloc_caches_dma[index] = s;
-               up_write(&slub_lock);
-               return s;
+       text = kasprintf(flags & ~SLUB_DMA, "kmalloc_dma-%d", (unsigned 
int)realsize),
+       s = kmalloc(kmem_size, flags & ~SLUB_DMA);
+
+       if (!s || !text || !kmem_cache_open(s, flags, text,
+                       realsize, ARCH_KMALLOC_MINALIGN,
+                       SLAB_CACHE_DMA|__SYSFS_ADD_DEFERRED, NULL)) {
+               kfree(s);
+               kfree(text);
+               goto unlock_out;
        }
+
+       list_add(&s->list, &slab_caches);
+       kmalloc_caches_dma[index] = s;
+
+       schedule_work(&sysfs_add_work);
+
+unlock_out:
        up_write(&slub_lock);
-       kmem_cache_destroy(s);
+out:
        return kmalloc_caches_dma[index];
 }
 #endif
-
To unsubscribe from this list: send the line "unsubscribe git-commits-head" in
the body of a message to [EMAIL PROTECTED]
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to