x86: add tizen_qemu_x86_defconfig & tizen_qemu_x86_64_defconfig
[platform/kernel/linux-rpi.git] / mm / zsmalloc.c
index 68e8831..a664318 100644 (file)
@@ -57,6 +57,7 @@
 #include <linux/wait.h>
 #include <linux/pagemap.h>
 #include <linux/fs.h>
+#include <linux/local_lock.h>
 
 #define ZSPAGE_MAGIC   0x58
 
 
 #define ZS_HANDLE_SIZE (sizeof(unsigned long))
 
+#ifdef CONFIG_PREEMPT_RT
+
+struct zsmalloc_handle {
+       unsigned long addr;
+       spinlock_t lock;
+};
+
+#define ZS_HANDLE_ALLOC_SIZE (sizeof(struct zsmalloc_handle))
+
+#else
+
+#define ZS_HANDLE_ALLOC_SIZE (sizeof(unsigned long))
+#endif
+
 /*
  * Object location (<PFN>, <obj_idx>) is encoded as
  * a single (unsigned long) handle value.
@@ -293,6 +308,7 @@ struct zspage {
 };
 
 struct mapping_area {
+       local_lock_t lock;
        char *vm_buf; /* copy buffer for objects that span pages */
        char *vm_addr; /* address of kmap_atomic()'ed pages */
        enum zs_mapmode vm_mm; /* mapping mode */
@@ -322,7 +338,7 @@ static void SetZsPageMovable(struct zs_pool *pool, struct zspage *zspage) {}
 
 static int create_cache(struct zs_pool *pool)
 {
-       pool->handle_cachep = kmem_cache_create("zs_handle", ZS_HANDLE_SIZE,
+       pool->handle_cachep = kmem_cache_create("zs_handle", ZS_HANDLE_ALLOC_SIZE,
                                        0, 0, NULL);
        if (!pool->handle_cachep)
                return 1;
@@ -346,9 +362,26 @@ static void destroy_cache(struct zs_pool *pool)
 
 static unsigned long cache_alloc_handle(struct zs_pool *pool, gfp_t gfp)
 {
-       return (unsigned long)kmem_cache_alloc(pool->handle_cachep,
-                       gfp & ~(__GFP_HIGHMEM|__GFP_MOVABLE));
+       void *p;
+
+       p = kmem_cache_alloc(pool->handle_cachep,
+                            gfp & ~(__GFP_HIGHMEM|__GFP_MOVABLE));
+#ifdef CONFIG_PREEMPT_RT
+       if (p) {
+               struct zsmalloc_handle *zh = p;
+
+               spin_lock_init(&zh->lock);
+       }
+#endif
+       return (unsigned long)p;
+}
+
+#ifdef CONFIG_PREEMPT_RT
+static struct zsmalloc_handle *zs_get_pure_handle(unsigned long handle)
+{
+       return (void *)(handle & ~((1 << OBJ_TAG_BITS) - 1));
 }
+#endif
 
 static void cache_free_handle(struct zs_pool *pool, unsigned long handle)
 {
@@ -368,12 +401,18 @@ static void cache_free_zspage(struct zs_pool *pool, struct zspage *zspage)
 
 static void record_obj(unsigned long handle, unsigned long obj)
 {
+#ifdef CONFIG_PREEMPT_RT
+       struct zsmalloc_handle *zh = zs_get_pure_handle(handle);
+
+       WRITE_ONCE(zh->addr, obj);
+#else
        /*
         * lsb of @obj represents handle lock while other bits
         * represent object value the handle is pointing so
         * updating shouldn't do store tearing.
         */
        WRITE_ONCE(*(unsigned long *)handle, obj);
+#endif
 }
 
 /* zpool driver */
@@ -455,7 +494,9 @@ MODULE_ALIAS("zpool-zsmalloc");
 #endif /* CONFIG_ZPOOL */
 
 /* per-cpu VM mapping areas for zspage accesses that cross page boundaries */
-static DEFINE_PER_CPU(struct mapping_area, zs_map_area);
+static DEFINE_PER_CPU(struct mapping_area, zs_map_area) = {
+       .lock   = INIT_LOCAL_LOCK(lock),
+};
 
 static bool is_zspage_isolated(struct zspage *zspage)
 {
@@ -862,7 +903,13 @@ static unsigned long location_to_obj(struct page *page, unsigned int obj_idx)
 
 static unsigned long handle_to_obj(unsigned long handle)
 {
+#ifdef CONFIG_PREEMPT_RT
+       struct zsmalloc_handle *zh = zs_get_pure_handle(handle);
+
+       return zh->addr;
+#else
        return *(unsigned long *)handle;
+#endif
 }
 
 static unsigned long obj_to_head(struct page *page, void *obj)
@@ -876,22 +923,46 @@ static unsigned long obj_to_head(struct page *page, void *obj)
 
 static inline int testpin_tag(unsigned long handle)
 {
+#ifdef CONFIG_PREEMPT_RT
+       struct zsmalloc_handle *zh = zs_get_pure_handle(handle);
+
+       return spin_is_locked(&zh->lock);
+#else
        return bit_spin_is_locked(HANDLE_PIN_BIT, (unsigned long *)handle);
+#endif
 }
 
 static inline int trypin_tag(unsigned long handle)
 {
+#ifdef CONFIG_PREEMPT_RT
+       struct zsmalloc_handle *zh = zs_get_pure_handle(handle);
+
+       return spin_trylock(&zh->lock);
+#else
        return bit_spin_trylock(HANDLE_PIN_BIT, (unsigned long *)handle);
+#endif
 }
 
 static void pin_tag(unsigned long handle) __acquires(bitlock)
 {
+#ifdef CONFIG_PREEMPT_RT
+       struct zsmalloc_handle *zh = zs_get_pure_handle(handle);
+
+       return spin_lock(&zh->lock);
+#else
        bit_spin_lock(HANDLE_PIN_BIT, (unsigned long *)handle);
+#endif
 }
 
 static void unpin_tag(unsigned long handle) __releases(bitlock)
 {
+#ifdef CONFIG_PREEMPT_RT
+       struct zsmalloc_handle *zh = zs_get_pure_handle(handle);
+
+       return spin_unlock(&zh->lock);
+#else
        bit_spin_unlock(HANDLE_PIN_BIT, (unsigned long *)handle);
+#endif
 }
 
 static void reset_page(struct page *page)
@@ -1274,7 +1345,8 @@ void *zs_map_object(struct zs_pool *pool, unsigned long handle,
        class = pool->size_class[class_idx];
        off = (class->size * obj_idx) & ~PAGE_MASK;
 
-       area = &get_cpu_var(zs_map_area);
+       local_lock(&zs_map_area.lock);
+       area = this_cpu_ptr(&zs_map_area);
        area->vm_mm = mm;
        if (off + class->size <= PAGE_SIZE) {
                /* this object is contained entirely within a page */
@@ -1328,7 +1400,7 @@ void zs_unmap_object(struct zs_pool *pool, unsigned long handle)
 
                __zs_unmap_object(area, pages, off, class->size);
        }
-       put_cpu_var(zs_map_area);
+       local_unlock(&zs_map_area.lock);
 
        migrate_read_unlock(zspage);
        unpin_tag(handle);
@@ -1743,11 +1815,40 @@ static enum fullness_group putback_zspage(struct size_class *class,
  */
 static void lock_zspage(struct zspage *zspage)
 {
-       struct page *page = get_first_page(zspage);
+       struct page *curr_page, *page;
 
-       do {
-               lock_page(page);
-       } while ((page = get_next_page(page)) != NULL);
+       /*
+        * Pages we haven't locked yet can be migrated off the list while we're
+        * trying to lock them, so we need to be careful and only attempt to
+        * lock each page under migrate_read_lock(). Otherwise, the page we lock
+        * may no longer belong to the zspage. This means that we may wait for
+        * the wrong page to unlock, so we must take a reference to the page
+        * prior to waiting for it to unlock outside migrate_read_lock().
+        */
+       while (1) {
+               migrate_read_lock(zspage);
+               page = get_first_page(zspage);
+               if (trylock_page(page))
+                       break;
+               get_page(page);
+               migrate_read_unlock(zspage);
+               wait_on_page_locked(page);
+               put_page(page);
+       }
+
+       curr_page = page;
+       while ((page = get_next_page(curr_page))) {
+               if (trylock_page(page)) {
+                       curr_page = page;
+               } else {
+                       get_page(page);
+                       migrate_read_unlock(zspage);
+                       wait_on_page_locked(page);
+                       put_page(page);
+                       migrate_read_lock(zspage);
+               }
+       }
+       migrate_read_unlock(zspage);
 }
 
 static int zs_init_fs_context(struct fs_context *fc)
@@ -1830,10 +1931,11 @@ static inline void zs_pool_dec_isolated(struct zs_pool *pool)
        VM_BUG_ON(atomic_long_read(&pool->isolated_pages) <= 0);
        atomic_long_dec(&pool->isolated_pages);
        /*
-        * There's no possibility of racing, since wait_for_isolated_drain()
-        * checks the isolated count under &class->lock after enqueuing
-        * on migration_wait.
+        * Checking pool->destroying must happen after atomic_long_dec()
+        * for pool->isolated_pages above. Paired with the smp_mb() in
+        * zs_unregister_migration().
         */
+       smp_mb__after_atomic();
        if (atomic_long_read(&pool->isolated_pages) == 0 && pool->destroying)
                wake_up_all(&pool->migration_wait);
 }