clk: defer clk_gets on orphan clocks
[firefly-linux-kernel-4.4.55.git] / mm / zsmalloc.c
index f135b1b6fcdcab49aaf0845e078c6fc299b4b28b..c1ea19478119f21afa1a3a2d27537c251f6c152d 100644 (file)
@@ -16,7 +16,7 @@
  * struct page(s) to form a zspage.
  *
  * Usage of struct page fields:
- *     page->first_page: points to the first component (0-order) page
+ *     page->private: points to the first component (0-order) page
  *     page->index (union with page->freelist): offset of the first object
  *             starting in this page. For the first page, this is
  *             always 0, so we use this field (aka freelist) to point
@@ -26,8 +26,7 @@
  *
  *     For _first_ page only:
  *
- *     page->private (union with page->first_page): refers to the
- *             component page after the first page
+ *     page->private: refers to the component page after the first page
  *             If the page is first_page for huge object, it stores handle.
  *             Look at size_class->huge.
  *     page->freelist: points to the first free object in zspage.
@@ -38,6 +37,7 @@
  *     page->lru: links together first pages of various zspages.
  *             Basically forming list of zspages in a fullness group.
  *     page->mapping: class index and fullness group of the zspage
+ *     page->inuse: the number of objects that are used in this zspage
  *
  * Usage of struct page flags:
  *     PG_private: identifies the first component page
@@ -58,7 +58,7 @@
 #include <linux/cpumask.h>
 #include <linux/cpu.h>
 #include <linux/vmalloc.h>
-#include <linux/hardirq.h>
+#include <linux/preempt.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
 #include <linux/debugfs.h>
@@ -166,9 +166,14 @@ enum zs_stat_type {
        OBJ_USED,
        CLASS_ALMOST_FULL,
        CLASS_ALMOST_EMPTY,
-       NR_ZS_STAT_TYPE,
 };
 
+#ifdef CONFIG_ZSMALLOC_STAT
+#define NR_ZS_STAT_TYPE        (CLASS_ALMOST_EMPTY + 1)
+#else
+#define NR_ZS_STAT_TYPE        (OBJ_USED + 1)
+#endif
+
 struct zs_size_stat {
        unsigned long objs[NR_ZS_STAT_TYPE];
 };
@@ -237,7 +242,7 @@ struct link_free {
 };
 
 struct zs_pool {
-       char *name;
+       const char *name;
 
        struct size_class **size_class;
        struct kmem_cache *handle_cachep;
@@ -304,14 +309,19 @@ static void free_handle(struct zs_pool *pool, unsigned long handle)
 
 static void record_obj(unsigned long handle, unsigned long obj)
 {
-       *(unsigned long *)handle = obj;
+       /*
+        * 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);
 }
 
 /* zpool driver */
 
 #ifdef CONFIG_ZPOOL
 
-static void *zs_zpool_create(char *name, gfp_t gfp,
+static void *zs_zpool_create(const char *name, gfp_t gfp,
                             const struct zpool_ops *zpool_ops,
                             struct zpool *zpool)
 {
@@ -447,19 +457,23 @@ static int get_size_class_index(int size)
 static inline void zs_stat_inc(struct size_class *class,
                                enum zs_stat_type type, unsigned long cnt)
 {
-       class->stats.objs[type] += cnt;
+       if (type < NR_ZS_STAT_TYPE)
+               class->stats.objs[type] += cnt;
 }
 
 static inline void zs_stat_dec(struct size_class *class,
                                enum zs_stat_type type, unsigned long cnt)
 {
-       class->stats.objs[type] -= cnt;
+       if (type < NR_ZS_STAT_TYPE)
+               class->stats.objs[type] -= cnt;
 }
 
 static inline unsigned long zs_stat_get(struct size_class *class,
                                enum zs_stat_type type)
 {
-       return class->stats.objs[type];
+       if (type < NR_ZS_STAT_TYPE)
+               return class->stats.objs[type];
+       return 0;
 }
 
 #ifdef CONFIG_ZSMALLOC_STAT
@@ -548,7 +562,7 @@ static const struct file_operations zs_stat_size_ops = {
        .release        = single_release,
 };
 
-static int zs_pool_stat_create(char *name, struct zs_pool *pool)
+static int zs_pool_stat_create(const char *name, struct zs_pool *pool)
 {
        struct dentry *entry;
 
@@ -588,7 +602,7 @@ static void __exit zs_stat_exit(void)
 {
 }
 
-static inline int zs_pool_stat_create(char *name, struct zs_pool *pool)
+static inline int zs_pool_stat_create(const char *name, struct zs_pool *pool)
 {
        return 0;
 }
@@ -764,7 +778,7 @@ static struct page *get_first_page(struct page *page)
        if (is_first_page(page))
                return page;
        else
-               return page->first_page;
+               return (struct page *)page_private(page);
 }
 
 static struct page *get_next_page(struct page *page)
@@ -824,7 +838,7 @@ static unsigned long obj_to_head(struct size_class *class, struct page *page,
 {
        if (class->huge) {
                VM_BUG_ON(!is_first_page(page));
-               return *(unsigned long *)page_private(page);
+               return page_private(page);
        } else
                return *(unsigned long *)obj;
 }
@@ -949,7 +963,7 @@ static struct page *alloc_zspage(struct size_class *class, gfp_t flags)
         * Allocate individual pages and link them together as:
         * 1. first page->private = first sub-page
         * 2. all sub-pages are linked together using page->lru
-        * 3. each sub-page is linked to the first page using page->first_page
+        * 3. each sub-page is linked to the first page using page->private
         *
         * For each size class, First/Head pages are linked together using
         * page->lru. Also, we set PG_private to identify the first page
@@ -974,7 +988,7 @@ static struct page *alloc_zspage(struct size_class *class, gfp_t flags)
                if (i == 1)
                        set_page_private(first_page, (unsigned long)page);
                if (i >= 1)
-                       page->first_page = first_page;
+                       set_page_private(page, (unsigned long)first_page);
                if (i >= 2)
                        list_add(&page->lru, &prev_page->lru);
                if (i == class->pages_per_zspage - 1)   /* last page */
@@ -1428,8 +1442,6 @@ static void obj_free(struct zs_pool *pool, struct size_class *class,
        struct page *first_page, *f_page;
        unsigned long f_objidx, f_offset;
        void *vaddr;
-       int class_idx;
-       enum fullness_group fullness;
 
        BUG_ON(!obj);
 
@@ -1437,7 +1449,6 @@ static void obj_free(struct zs_pool *pool, struct size_class *class,
        obj_to_location(obj, &f_page, &f_objidx);
        first_page = get_first_page(f_page);
 
-       get_zspage_mapping(first_page, &class_idx, &fullness);
        f_offset = obj_idx_to_offset(f_page, f_objidx, class->size);
 
        vaddr = kmap_atomic(f_page);
@@ -1629,6 +1640,13 @@ static int migrate_zspage(struct zs_pool *pool, struct size_class *class,
                free_obj = obj_malloc(d_page, class, handle);
                zs_object_copy(free_obj, used_obj, class);
                index++;
+               /*
+                * record_obj updates handle's value to free_obj and it will
+                * invalidate lock bit(ie, HANDLE_PIN_BIT) of handle, which
+                * breaks synchronization using pin_tag(e,g, zs_free) so
+                * let's keep the lock bit.
+                */
+               free_obj |= BIT(HANDLE_PIN_BIT);
                record_obj(handle, free_obj);
                unpin_tag(handle);
                obj_free(pool, class, used_obj);
@@ -1714,10 +1732,13 @@ static struct page *isolate_source_page(struct size_class *class)
 static unsigned long zs_can_compact(struct size_class *class)
 {
        unsigned long obj_wasted;
+       unsigned long obj_allocated = zs_stat_get(class, OBJ_ALLOCATED);
+       unsigned long obj_used = zs_stat_get(class, OBJ_USED);
 
-       obj_wasted = zs_stat_get(class, OBJ_ALLOCATED) -
-               zs_stat_get(class, OBJ_USED);
+       if (obj_allocated <= obj_used)
+               return 0;
 
+       obj_wasted = obj_allocated - obj_used;
        obj_wasted /= get_maxobj_per_zspage(class->size,
                        class->pages_per_zspage);
 
@@ -1822,9 +1843,6 @@ static unsigned long zs_shrinker_count(struct shrinker *shrinker,
        struct zs_pool *pool = container_of(shrinker, struct zs_pool,
                        shrinker);
 
-       if (!pool->shrinker_enabled)
-               return 0;
-
        for (i = zs_size_classes - 1; i >= 0; i--) {
                class = pool->size_class[i];
                if (!class)
@@ -1866,7 +1884,7 @@ static int zs_register_shrinker(struct zs_pool *pool)
  * On success, a pointer to the newly created pool is returned,
  * otherwise NULL.
  */
-struct zs_pool *zs_create_pool(char *name, gfp_t flags)
+struct zs_pool *zs_create_pool(const char *name, gfp_t flags)
 {
        int i;
        struct zs_pool *pool;