]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blobdiff - mm/zsmalloc.c
Merge branch 'i2c/for-3.20' of git://git.kernel.org/pub/scm/linux/kernel/git/wsa...
[mirror_ubuntu-artful-kernel.git] / mm / zsmalloc.c
index b72403927aa4b06610cb08e9506d677c98a1a5cd..0dec1fa5f6565dab6be9c9515d52c9caae8e0ea6 100644 (file)
@@ -91,6 +91,7 @@
 #include <linux/hardirq.h>
 #include <linux/spinlock.h>
 #include <linux/types.h>
+#include <linux/debugfs.h>
 #include <linux/zsmalloc.h>
 #include <linux/zpool.h>
 
@@ -168,6 +169,22 @@ enum fullness_group {
        ZS_FULL
 };
 
+enum zs_stat_type {
+       OBJ_ALLOCATED,
+       OBJ_USED,
+       NR_ZS_STAT_TYPE,
+};
+
+#ifdef CONFIG_ZSMALLOC_STAT
+
+static struct dentry *zs_stat_root;
+
+struct zs_size_stat {
+       unsigned long objs[NR_ZS_STAT_TYPE];
+};
+
+#endif
+
 /*
  * number of size_classes
  */
@@ -200,6 +217,10 @@ struct size_class {
        /* Number of PAGE_SIZE sized pages to combine to form a 'zspage' */
        int pages_per_zspage;
 
+#ifdef CONFIG_ZSMALLOC_STAT
+       struct zs_size_stat stats;
+#endif
+
        spinlock_t lock;
 
        struct page *fullness_list[_ZS_NR_FULLNESS_GROUPS];
@@ -217,10 +238,16 @@ struct link_free {
 };
 
 struct zs_pool {
+       char *name;
+
        struct size_class **size_class;
 
        gfp_t flags;    /* allocation flags used when growing pool */
        atomic_long_t pages_allocated;
+
+#ifdef CONFIG_ZSMALLOC_STAT
+       struct dentry *stat_dentry;
+#endif
 };
 
 /*
@@ -246,9 +273,9 @@ struct mapping_area {
 
 #ifdef CONFIG_ZPOOL
 
-static void *zs_zpool_create(gfp_t gfp, struct zpool_ops *zpool_ops)
+static void *zs_zpool_create(char *name, gfp_t gfp, struct zpool_ops *zpool_ops)
 {
-       return zs_create_pool(gfp);
+       return zs_create_pool(name, gfp);
 }
 
 static void zs_zpool_destroy(void *pool)
@@ -942,6 +969,166 @@ static bool can_merge(struct size_class *prev, int size, int pages_per_zspage)
        return true;
 }
 
+#ifdef CONFIG_ZSMALLOC_STAT
+
+static inline void zs_stat_inc(struct size_class *class,
+                               enum zs_stat_type type, unsigned long cnt)
+{
+       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;
+}
+
+static inline unsigned long zs_stat_get(struct size_class *class,
+                               enum zs_stat_type type)
+{
+       return class->stats.objs[type];
+}
+
+static int __init zs_stat_init(void)
+{
+       if (!debugfs_initialized())
+               return -ENODEV;
+
+       zs_stat_root = debugfs_create_dir("zsmalloc", NULL);
+       if (!zs_stat_root)
+               return -ENOMEM;
+
+       return 0;
+}
+
+static void __exit zs_stat_exit(void)
+{
+       debugfs_remove_recursive(zs_stat_root);
+}
+
+static int zs_stats_size_show(struct seq_file *s, void *v)
+{
+       int i;
+       struct zs_pool *pool = s->private;
+       struct size_class *class;
+       int objs_per_zspage;
+       unsigned long obj_allocated, obj_used, pages_used;
+       unsigned long total_objs = 0, total_used_objs = 0, total_pages = 0;
+
+       seq_printf(s, " %5s %5s %13s %10s %10s\n", "class", "size",
+                               "obj_allocated", "obj_used", "pages_used");
+
+       for (i = 0; i < zs_size_classes; i++) {
+               class = pool->size_class[i];
+
+               if (class->index != i)
+                       continue;
+
+               spin_lock(&class->lock);
+               obj_allocated = zs_stat_get(class, OBJ_ALLOCATED);
+               obj_used = zs_stat_get(class, OBJ_USED);
+               spin_unlock(&class->lock);
+
+               objs_per_zspage = get_maxobj_per_zspage(class->size,
+                               class->pages_per_zspage);
+               pages_used = obj_allocated / objs_per_zspage *
+                               class->pages_per_zspage;
+
+               seq_printf(s, " %5u %5u    %10lu %10lu %10lu\n", i,
+                       class->size, obj_allocated, obj_used, pages_used);
+
+               total_objs += obj_allocated;
+               total_used_objs += obj_used;
+               total_pages += pages_used;
+       }
+
+       seq_puts(s, "\n");
+       seq_printf(s, " %5s %5s    %10lu %10lu %10lu\n", "Total", "",
+                       total_objs, total_used_objs, total_pages);
+
+       return 0;
+}
+
+static int zs_stats_size_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, zs_stats_size_show, inode->i_private);
+}
+
+static const struct file_operations zs_stat_size_ops = {
+       .open           = zs_stats_size_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int zs_pool_stat_create(char *name, struct zs_pool *pool)
+{
+       struct dentry *entry;
+
+       if (!zs_stat_root)
+               return -ENODEV;
+
+       entry = debugfs_create_dir(name, zs_stat_root);
+       if (!entry) {
+               pr_warn("debugfs dir <%s> creation failed\n", name);
+               return -ENOMEM;
+       }
+       pool->stat_dentry = entry;
+
+       entry = debugfs_create_file("obj_in_classes", S_IFREG | S_IRUGO,
+                       pool->stat_dentry, pool, &zs_stat_size_ops);
+       if (!entry) {
+               pr_warn("%s: debugfs file entry <%s> creation failed\n",
+                               name, "obj_in_classes");
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+static void zs_pool_stat_destroy(struct zs_pool *pool)
+{
+       debugfs_remove_recursive(pool->stat_dentry);
+}
+
+#else /* CONFIG_ZSMALLOC_STAT */
+
+static inline void zs_stat_inc(struct size_class *class,
+                               enum zs_stat_type type, unsigned long cnt)
+{
+}
+
+static inline void zs_stat_dec(struct size_class *class,
+                               enum zs_stat_type type, unsigned long cnt)
+{
+}
+
+static inline unsigned long zs_stat_get(struct size_class *class,
+                               enum zs_stat_type type)
+{
+       return 0;
+}
+
+static int __init zs_stat_init(void)
+{
+       return 0;
+}
+
+static void __exit zs_stat_exit(void)
+{
+}
+
+static inline int zs_pool_stat_create(char *name, struct zs_pool *pool)
+{
+       return 0;
+}
+
+static inline void zs_pool_stat_destroy(struct zs_pool *pool)
+{
+}
+
+#endif
+
 unsigned long zs_get_total_pages(struct zs_pool *pool)
 {
        return atomic_long_read(&pool->pages_allocated);
@@ -1074,7 +1261,10 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)
                set_zspage_mapping(first_page, class->index, ZS_EMPTY);
                atomic_long_add(class->pages_per_zspage,
                                        &pool->pages_allocated);
+
                spin_lock(&class->lock);
+               zs_stat_inc(class, OBJ_ALLOCATED, get_maxobj_per_zspage(
+                               class->size, class->pages_per_zspage));
        }
 
        obj = (unsigned long)first_page->freelist;
@@ -1088,6 +1278,7 @@ unsigned long zs_malloc(struct zs_pool *pool, size_t size)
        kunmap_atomic(vaddr);
 
        first_page->inuse++;
+       zs_stat_inc(class, OBJ_USED, 1);
        /* Now move the zspage to another fullness group, if required */
        fix_fullness_group(pool, first_page);
        spin_unlock(&class->lock);
@@ -1128,6 +1319,12 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
 
        first_page->inuse--;
        fullness = fix_fullness_group(pool, first_page);
+
+       zs_stat_dec(class, OBJ_USED, 1);
+       if (fullness == ZS_EMPTY)
+               zs_stat_dec(class, OBJ_ALLOCATED, get_maxobj_per_zspage(
+                               class->size, class->pages_per_zspage));
+
        spin_unlock(&class->lock);
 
        if (fullness == ZS_EMPTY) {
@@ -1148,7 +1345,7 @@ EXPORT_SYMBOL_GPL(zs_free);
  * On success, a pointer to the newly created pool is returned,
  * otherwise NULL.
  */
-struct zs_pool *zs_create_pool(gfp_t flags)
+struct zs_pool *zs_create_pool(char *name, gfp_t flags)
 {
        int i;
        struct zs_pool *pool;
@@ -1158,9 +1355,16 @@ struct zs_pool *zs_create_pool(gfp_t flags)
        if (!pool)
                return NULL;
 
+       pool->name = kstrdup(name, GFP_KERNEL);
+       if (!pool->name) {
+               kfree(pool);
+               return NULL;
+       }
+
        pool->size_class = kcalloc(zs_size_classes, sizeof(struct size_class *),
                        GFP_KERNEL);
        if (!pool->size_class) {
+               kfree(pool->name);
                kfree(pool);
                return NULL;
        }
@@ -1210,6 +1414,9 @@ struct zs_pool *zs_create_pool(gfp_t flags)
 
        pool->flags = flags;
 
+       if (zs_pool_stat_create(name, pool))
+               goto err;
+
        return pool;
 
 err:
@@ -1222,6 +1429,8 @@ void zs_destroy_pool(struct zs_pool *pool)
 {
        int i;
 
+       zs_pool_stat_destroy(pool);
+
        for (i = 0; i < zs_size_classes; i++) {
                int fg;
                struct size_class *class = pool->size_class[i];
@@ -1242,6 +1451,7 @@ void zs_destroy_pool(struct zs_pool *pool)
        }
 
        kfree(pool->size_class);
+       kfree(pool->name);
        kfree(pool);
 }
 EXPORT_SYMBOL_GPL(zs_destroy_pool);
@@ -1250,17 +1460,30 @@ static int __init zs_init(void)
 {
        int ret = zs_register_cpu_notifier();
 
-       if (ret) {
-               zs_unregister_cpu_notifier();
-               return ret;
-       }
+       if (ret)
+               goto notifier_fail;
 
        init_zs_size_classes();
 
 #ifdef CONFIG_ZPOOL
        zpool_register_driver(&zs_zpool_driver);
 #endif
+
+       ret = zs_stat_init();
+       if (ret) {
+               pr_err("zs stat initialization failed\n");
+               goto stat_fail;
+       }
        return 0;
+
+stat_fail:
+#ifdef CONFIG_ZPOOL
+       zpool_unregister_driver(&zs_zpool_driver);
+#endif
+notifier_fail:
+       zs_unregister_cpu_notifier();
+
+       return ret;
 }
 
 static void __exit zs_exit(void)
@@ -1269,6 +1492,8 @@ static void __exit zs_exit(void)
        zpool_unregister_driver(&zs_zpool_driver);
 #endif
        zs_unregister_cpu_notifier();
+
+       zs_stat_exit();
 }
 
 module_init(zs_init);