Skip to contentSkip to conversation

zram: add zpool support

PATCH In review Geliang Tang 8 participants 14 messages Jun 8, 2016 – Jun 20, 2016

Subsystems (4)
lore
Share & tools
lore
#0 lore raw

PATCH 1/1 zram: add zpool support

This patch adds zpool support for zram, it will allow us to use both the zpool api and directly zsmalloc api in zram.

---
 drivers/block/zram/zram_drv.c | 97 +++++++++++++++++++++++++++++++++++++++++++
 drivers/block/zram/zram_drv.h |  5 +++
 2 files changed, 102 insertions(+)
drivers/block/zram/zram_drv.c +97 -0
index 9e2a83c..1f90bd0 100644
@@ -43,6 +43,11 @@ static const char *default_compressor = "lzo";
 /* Module params (documentation at end) */
 static unsigned int num_devices = 1;
 
+#ifdef CONFIG_ZPOOL
+/* Compressed storage zpool to use */
+#define ZRAM_ZPOOL_DEFAULT "zsmalloc"
+#endif
+
 static inline void deprecated_attr_warn(const char *name)
 {
 	pr_warn_once("%d (%s) Attribute %s (and others) will be removed. %s\n",
@@ -228,7 +233,11 @@ static ssize_t mem_used_total_show(struct device *dev,
 	down_read(&zram->init_lock);
 	if (init_done(zram)) {
 		struct zram_meta *meta = zram->meta;
+#ifdef CONFIG_ZPOOL
+		val = zpool_get_total_size(meta->mem_pool) >> PAGE_SHIFT;
+#else
 		val = zs_get_total_pages(meta->mem_pool);
+#endif
 	}
 	up_read(&zram->init_lock);
 
@@ -296,8 +305,14 @@ static ssize_t mem_used_max_store(struct device *dev,
 	down_read(&zram->init_lock);
 	if (init_done(zram)) {
 		struct zram_meta *meta = zram->meta;
+#ifdef CONFIG_ZPOOL
+		atomic_long_set(&zram->stats.max_used_pages,
+				zpool_get_total_size(meta->mem_pool)
+				>> PAGE_SHIFT);
+#else
 		atomic_long_set(&zram->stats.max_used_pages,
 				zs_get_total_pages(meta->mem_pool));
+#endif
 	}
 	up_read(&zram->init_lock);
 
@@ -366,6 +381,18 @@ static ssize_t comp_algorithm_store(struct device *dev,
 	return len;
 }
 
+#ifdef CONFIG_ZPOOL
+static void zpool_compact(void *pool)
+{
+	zs_compact(pool);
+}
+
+static void zpool_stats(void *pool, struct zs_pool_stats *stats)
+{
+	zs_pool_stats(pool, stats);
+}
+#endif
+
 static ssize_t compact_store(struct device *dev,
 		struct device_attribute *attr, const char *buf, size_t len)
 {
@@ -379,7 +406,11 @@ static ssize_t compact_store(struct device *dev,
 	}
 
 	meta = zram->meta;
+#ifdef CONFIG_ZPOOL
+	zpool_compact(meta->mem_pool);
+#else
 	zs_compact(meta->mem_pool);
+#endif
 	up_read(&zram->init_lock);
 
 	return len;
@@ -416,8 +447,14 @@ static ssize_t mm_stat_show(struct device *dev,
 
 	down_read(&zram->init_lock);
 	if (init_done(zram)) {
+#ifdef CONFIG_ZPOOL
+		mem_used = zpool_get_total_size(zram->meta->mem_pool)
+				>> PAGE_SHIFT;
+		zpool_stats(zram->meta->mem_pool, &pool_stats);
+#else
 		mem_used = zs_get_total_pages(zram->meta->mem_pool);
 		zs_pool_stats(zram->meta->mem_pool, &pool_stats);
+#endif
 	}
 
 	orig_size = atomic64_read(&zram->stats.pages_stored);
@@ -490,10 +527,18 @@ static void zram_meta_free(struct zram_meta *meta, u64 disksize)
 		if (!handle)
 			continue;
 
+#ifdef CONFIG_ZPOOL
+		zpool_free(meta->mem_pool, handle);
+#else
 		zs_free(meta->mem_pool, handle);
+#endif
 	}
 
+#ifdef CONFIG_ZPOOL
+	zpool_destroy_pool(meta->mem_pool);
+#else
 	zs_destroy_pool(meta->mem_pool);
+#endif
 	vfree(meta->table);
 	kfree(meta);
 }
@@ -513,7 +558,17 @@ static struct zram_meta *zram_meta_alloc(char *pool_name, u64 disksize)
 		goto out_error;
 	}
 
+#ifdef CONFIG_ZPOOL
+	if (!zpool_has_pool(ZRAM_ZPOOL_DEFAULT)) {
+		pr_err("zpool %s not available\n", ZRAM_ZPOOL_DEFAULT);
+		goto out_error;
+	}
+
+	meta->mem_pool = zpool_create_pool(ZRAM_ZPOOL_DEFAULT,
+					pool_name, 0, NULL);
+#else
 	meta->mem_pool = zs_create_pool(pool_name);
+#endif
 	if (!meta->mem_pool) {
 		pr_err("Error creating memory pool\n");
 		goto out_error;
@@ -549,7 +604,11 @@ static void zram_free_page(struct zram *zram, size_t index)
 		return;
 	}
 
+#ifdef CONFIG_ZPOOL
+	zpool_free(meta->mem_pool, handle);
+#else
 	zs_free(meta->mem_pool, handle);
+#endif
 
 	atomic64_sub(zram_get_obj_size(meta, index),
 			&zram->stats.compr_data_size);
@@ -577,7 +636,11 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index)
 		return 0;
 	}
 
+#ifdef CONFIG_ZPOOL
+	cmem = zpool_map_handle(meta->mem_pool, handle, ZPOOL_MM_RO);
+#else
 	cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_RO);
+#endif
 	if (size == PAGE_SIZE) {
 		copy_page(mem, cmem);
 	} else {
@@ -586,7 +649,11 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index)
 		ret = zcomp_decompress(zstrm, cmem, size, mem);
 		zcomp_stream_put(zram->comp);
 	}
+#ifdef CONFIG_ZPOOL
+	zpool_unmap_handle(meta->mem_pool, handle);
+#else
 	zs_unmap_object(meta->mem_pool, handle);
+#endif
 	bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
 
 	/* Should NEVER happen. Return bio error if it does. */
@@ -735,20 +802,34 @@ compress_again:
 	 * from the slow path and handle has already been allocated.
 	 */
 	if (!handle)
+#ifdef CONFIG_ZPOOL
+		ret = zpool_malloc(meta->mem_pool, clen,
+				__GFP_KSWAPD_RECLAIM |
+				__GFP_NOWARN |
+				__GFP_HIGHMEM |
+				__GFP_MOVABLE, &handle);
+#else
 		handle = zs_malloc(meta->mem_pool, clen,
 				__GFP_KSWAPD_RECLAIM |
 				__GFP_NOWARN |
 				__GFP_HIGHMEM |
 				__GFP_MOVABLE);
+#endif
 	if (!handle) {
 		zcomp_stream_put(zram->comp);
 		zstrm = NULL;
 
 		atomic64_inc(&zram->stats.writestall);
 
+#ifdef CONFIG_ZPOOL
+		ret = zpool_malloc(meta->mem_pool, clen,
+				GFP_NOIO | __GFP_HIGHMEM |
+				__GFP_MOVABLE, &handle);
+#else
 		handle = zs_malloc(meta->mem_pool, clen,
 				GFP_NOIO | __GFP_HIGHMEM |
 				__GFP_MOVABLE);
+#endif
 		if (handle)
 			goto compress_again;
 
@@ -758,16 +839,28 @@ compress_again:
 		goto out;
 	}
 
+#ifdef CONFIG_ZPOOL
+	alloced_pages = zpool_get_total_size(meta->mem_pool) >> PAGE_SHIFT;
+#else
 	alloced_pages = zs_get_total_pages(meta->mem_pool);
+#endif
 	update_used_max(zram, alloced_pages);
 
 	if (zram->limit_pages && alloced_pages > zram->limit_pages) {
+#ifdef CONFIG_ZPOOL
+		zpool_free(meta->mem_pool, handle);
+#else
 		zs_free(meta->mem_pool, handle);
+#endif
 		ret = -ENOMEM;
 		goto out;
 	}
 
+#ifdef CONFIG_ZPOOL
+	cmem = zpool_map_handle(meta->mem_pool, handle, ZPOOL_MM_WO);
+#else
 	cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_WO);
+#endif
 
 	if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) {
 		src = kmap_atomic(page);
@@ -779,7 +872,11 @@ compress_again:
 
 	zcomp_stream_put(zram->comp);
 	zstrm = NULL;
+#ifdef CONFIG_ZPOOL
+	zpool_unmap_handle(meta->mem_pool, handle);
+#else
 	zs_unmap_object(meta->mem_pool, handle);
+#endif
 
 	/*
 	 * Free memory associated with this sector
drivers/block/zram/zram_drv.h +5 -0
index 74fcf10..68f1222 100644
@@ -17,6 +17,7 @@
 
 #include <linux/rwsem.h>
 #include <linux/zsmalloc.h>
+#include <linux/zpool.h>
 #include <linux/crypto.h>
 
 #include "zcomp.h"
@@ -91,7 +92,11 @@ struct zram_stats {
 
 struct zram_meta {
 	struct zram_table_entry *table;
+#ifdef CONFIG_ZPOOL
+	struct zpool *mem_pool;
+#else
 	struct zs_pool *mem_pool;
+#endif
 };
 
 struct zram {
Signature · 7 lines
1.9.1

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#1 lore raw
Quoted 2 lines · Geliang Tang

This patch adds zpool support for zram, it will allow us to use both the zpool api and directly zsmalloc api in zram.

besides the problems below, this was discussed a while ago and I believe Minchan is still against it, as nobody has so far shown what the benefit to zram would be; zram doesn't need the predictability, or evictability, of zbud or z3fold.

Quoted 12 lines
---
 drivers/block/zram/zram_drv.c | 97 +++++++++++++++++++++++++++++++++++++++++++
 drivers/block/zram/zram_drv.h |  5 +++
 2 files changed, 102 insertions(+)
drivers/block/zram/zram_drv.c +0 -0
index 9e2a83c..1f90bd0 100644

drivers/block/zram/zram_drv.c static const char *default_compressor = "lzo"; · line 49

Quoted 7 lines
drivers/block/zram/zram_drv.c +4 -0
@@ -43,6 +43,11 @@ static const char *default_compressor = "lzo";
 /* Module params (documentation at end) */
 static unsigned int num_devices = 1;
 
+#ifdef CONFIG_ZPOOL
+/* Compressed storage zpool to use */
+#define ZRAM_ZPOOL_DEFAULT "zsmalloc"
+#endif

It doesn't make sense for zram to conditionally use zpool; either it uses it and thus has 'select ZPOOL' in its Kconfig entry, or it doesn't use it at all.

Quoted 36 lines
drivers/block/zram/zram_drv.c +12 -0
+
 static inline void deprecated_attr_warn(const char *name)
 {
        pr_warn_once("%d (%s) Attribute %s (and others) will be removed. %s\n",
@@ -228,7 +233,11 @@ static ssize_t mem_used_total_show(struct device *dev,
        down_read(&zram->init_lock);
        if (init_done(zram)) {
                struct zram_meta *meta = zram->meta;
+#ifdef CONFIG_ZPOOL
+               val = zpool_get_total_size(meta->mem_pool) >> PAGE_SHIFT;
+#else
                val = zs_get_total_pages(meta->mem_pool);
+#endif
        }
        up_read(&zram->init_lock);
 
@@ -296,8 +305,14 @@ static ssize_t mem_used_max_store(struct device *dev,
        down_read(&zram->init_lock);
        if (init_done(zram)) {
                struct zram_meta *meta = zram->meta;
+#ifdef CONFIG_ZPOOL
+               atomic_long_set(&zram->stats.max_used_pages,
+                               zpool_get_total_size(meta->mem_pool)
+                               >> PAGE_SHIFT);
+#else
                atomic_long_set(&zram->stats.max_used_pages,
                                zs_get_total_pages(meta->mem_pool));
+#endif
        }
        up_read(&zram->init_lock);
 
@@ -366,6 +381,18 @@ static ssize_t comp_algorithm_store(struct device *dev,
        return len;
 }
 
+#ifdef CONFIG_ZPOOL

drivers/block/zram/zram_drv.c static ssize_t comp_algorithm_store(struct device *dev, · line 394

Quoted 10 lines
drivers/block/zram/zram_drv.c +10 -0
+static void zpool_compact(void *pool)
+{
+       zs_compact(pool);
+}
+
+static void zpool_stats(void *pool, struct zs_pool_stats *stats)
+{
+       zs_pool_stats(pool, stats);
+}
+#endif

first, no. this obviously makes using zpool in zram completely pointless.

second, did you test this? the pool you're passing is the zpool, not the zs_pool. quite bad things will happen when this code runs. There is no way to get the zs_pool from the zpool object (that's the point of abstraction, of course).

The fact zpool doesn't have these apis (currently) is one of the reasons against changing zram to use zpool.

Quoted 205 lines
drivers/block/zram/zram_drv.c +71 -0
+
 static ssize_t compact_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t len)
 {
@@ -379,7 +406,11 @@ static ssize_t compact_store(struct device *dev,
        }
 
        meta = zram->meta;
+#ifdef CONFIG_ZPOOL
+       zpool_compact(meta->mem_pool);
+#else
        zs_compact(meta->mem_pool);
+#endif
        up_read(&zram->init_lock);
 
        return len;
@@ -416,8 +447,14 @@ static ssize_t mm_stat_show(struct device *dev,
 
        down_read(&zram->init_lock);
        if (init_done(zram)) {
+#ifdef CONFIG_ZPOOL
+               mem_used = zpool_get_total_size(zram->meta->mem_pool)
+                               >> PAGE_SHIFT;
+               zpool_stats(zram->meta->mem_pool, &pool_stats);
+#else
                mem_used = zs_get_total_pages(zram->meta->mem_pool);
                zs_pool_stats(zram->meta->mem_pool, &pool_stats);
+#endif
        }
 
        orig_size = atomic64_read(&zram->stats.pages_stored);
@@ -490,10 +527,18 @@ static void zram_meta_free(struct zram_meta *meta, u64 disksize)
                if (!handle)
                        continue;
 
+#ifdef CONFIG_ZPOOL
+               zpool_free(meta->mem_pool, handle);
+#else
                zs_free(meta->mem_pool, handle);
+#endif
        }
 
+#ifdef CONFIG_ZPOOL
+       zpool_destroy_pool(meta->mem_pool);
+#else
        zs_destroy_pool(meta->mem_pool);
+#endif
        vfree(meta->table);
        kfree(meta);
 }
@@ -513,7 +558,17 @@ static struct zram_meta *zram_meta_alloc(char *pool_name, u64 disksize)
                goto out_error;
        }
 
+#ifdef CONFIG_ZPOOL
+       if (!zpool_has_pool(ZRAM_ZPOOL_DEFAULT)) {
+               pr_err("zpool %s not available\n", ZRAM_ZPOOL_DEFAULT);
+               goto out_error;
+       }
+
+       meta->mem_pool = zpool_create_pool(ZRAM_ZPOOL_DEFAULT,
+                                       pool_name, 0, NULL);
+#else
        meta->mem_pool = zs_create_pool(pool_name);
+#endif
        if (!meta->mem_pool) {
                pr_err("Error creating memory pool\n");
                goto out_error;
@@ -549,7 +604,11 @@ static void zram_free_page(struct zram *zram, size_t index)
                return;
        }
 
+#ifdef CONFIG_ZPOOL
+       zpool_free(meta->mem_pool, handle);
+#else
        zs_free(meta->mem_pool, handle);
+#endif
 
        atomic64_sub(zram_get_obj_size(meta, index),
                        &zram->stats.compr_data_size);
@@ -577,7 +636,11 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index)
                return 0;
        }
 
+#ifdef CONFIG_ZPOOL
+       cmem = zpool_map_handle(meta->mem_pool, handle, ZPOOL_MM_RO);
+#else
        cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_RO);
+#endif
        if (size == PAGE_SIZE) {
                copy_page(mem, cmem);
        } else {
@@ -586,7 +649,11 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index)
                ret = zcomp_decompress(zstrm, cmem, size, mem);
                zcomp_stream_put(zram->comp);
        }
+#ifdef CONFIG_ZPOOL
+       zpool_unmap_handle(meta->mem_pool, handle);
+#else
        zs_unmap_object(meta->mem_pool, handle);
+#endif
        bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
 
        /* Should NEVER happen. Return bio error if it does. */
@@ -735,20 +802,34 @@ compress_again:
         * from the slow path and handle has already been allocated.
         */
        if (!handle)
+#ifdef CONFIG_ZPOOL
+               ret = zpool_malloc(meta->mem_pool, clen,
+                               __GFP_KSWAPD_RECLAIM |
+                               __GFP_NOWARN |
+                               __GFP_HIGHMEM |
+                               __GFP_MOVABLE, &handle);
+#else
                handle = zs_malloc(meta->mem_pool, clen,
                                __GFP_KSWAPD_RECLAIM |
                                __GFP_NOWARN |
                                __GFP_HIGHMEM |
                                __GFP_MOVABLE);
+#endif
        if (!handle) {
                zcomp_stream_put(zram->comp);
                zstrm = NULL;
 
                atomic64_inc(&zram->stats.writestall);
 
+#ifdef CONFIG_ZPOOL
+               ret = zpool_malloc(meta->mem_pool, clen,
+                               GFP_NOIO | __GFP_HIGHMEM |
+                               __GFP_MOVABLE, &handle);
+#else
                handle = zs_malloc(meta->mem_pool, clen,
                                GFP_NOIO | __GFP_HIGHMEM |
                                __GFP_MOVABLE);
+#endif
                if (handle)
                        goto compress_again;
 
@@ -758,16 +839,28 @@ compress_again:
                goto out;
        }
 
+#ifdef CONFIG_ZPOOL
+       alloced_pages = zpool_get_total_size(meta->mem_pool) >> PAGE_SHIFT;
+#else
        alloced_pages = zs_get_total_pages(meta->mem_pool);
+#endif
        update_used_max(zram, alloced_pages);
 
        if (zram->limit_pages && alloced_pages > zram->limit_pages) {
+#ifdef CONFIG_ZPOOL
+               zpool_free(meta->mem_pool, handle);
+#else
                zs_free(meta->mem_pool, handle);
+#endif
                ret = -ENOMEM;
                goto out;
        }
 
+#ifdef CONFIG_ZPOOL
+       cmem = zpool_map_handle(meta->mem_pool, handle, ZPOOL_MM_WO);
+#else
        cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_WO);
+#endif
 
        if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) {
                src = kmap_atomic(page);
@@ -779,7 +872,11 @@ compress_again:
 
        zcomp_stream_put(zram->comp);
        zstrm = NULL;
+#ifdef CONFIG_ZPOOL
+       zpool_unmap_handle(meta->mem_pool, handle);
+#else
        zs_unmap_object(meta->mem_pool, handle);
+#endif
 
        /*
         * Free memory associated with this sector
drivers/block/zram/zram_drv.h +5 -1
index 74fcf10..68f1222 100644
@@ -17,6 +17,7 @@
 
 #include <linux/rwsem.h>
 #include <linux/zsmalloc.h>
+#include <linux/zpool.h>
 #include <linux/crypto.h>
 
 #include "zcomp.h"
@@ -91,7 +92,11 @@ struct zram_stats {
 
 struct zram_meta {
        struct zram_table_entry *table;
+#ifdef CONFIG_ZPOOL
+       struct zpool *mem_pool;
+#else
        struct zs_pool *mem_pool;
+#endif
 };
 
 struct zram {
--
Quoted 2 lines

1.9.1

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#2 lore raw
Quoted 8 lines · Dan Streetman
Quoted 2 lines · Geliang Tang

This patch adds zpool support for zram, it will allow us to use both the zpool api and directly zsmalloc api in zram.

besides the problems below, this was discussed a while ago and I believe Minchan is still against it, as nobody has so far shown what the benefit to zram would be; zram doesn't need the predictability, or evictability, of zbud or z3fold.

Right.

Geliang, I cannot ack without any *detail* that what's the problem of zram/zsmalloc, why we can't fix it in zsmalloc itself.
The zbud and zsmalloc is otally different design to aim different goal determinism vs efficiency so you can choose what you want between zswap and zram rather than mixing the features.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#3 lore raw

Hello,

Quoted 10 lines · Minchan Kim
Quoted 8 lines · Dan Streetman
Quoted 2 lines · Geliang Tang

This patch adds zpool support for zram, it will allow us to use both the zpool api and directly zsmalloc api in zram.

besides the problems below, this was discussed a while ago and I believe Minchan is still against it, as nobody has so far shown what the benefit to zram would be; zram doesn't need the predictability, or evictability, of zbud or z3fold.

Quoted 7 lines

Right.

Geliang, I cannot ack without any *detail* that what's the problem of zram/zsmalloc, why we can't fix it in zsmalloc itself.
The zbud and zsmalloc is otally different design to aim different goal determinism vs efficiency so you can choose what you want between zswap and zram rather than mixing the features.

I'd also probably Cc Vitaly Wool on this

(http://marc.info/?l=linux-mm&m=146537877415982&w=2)

	-ss

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#4 lore raw
In reply to Dan Streetman

Den 8 juni 2016 6:33 em skrev "Dan Streetman" <[email protected]>:

Quoted 2 lines

On Wed, Jun 8, 2016 at 5:39 AM, Geliang Tang <[email protected]>

Quoted 7 lines · wrote:
Quoted 2 lines

This patch adds zpool support for zram, it will allow us to use both the zpool api and directly zsmalloc api in zram.

besides the problems below, this was discussed a while ago and I believe Minchan is still against it, as nobody has so far shown what the benefit to zram would be; zram doesn't need the predictability, or evictability, of zbud or z3fold.

Well, I believe I have something to say here. z3fold is generally faster than zsmalloc which makes it a better choice for zram sometimes, e.g. when zram device is used for swap. Also, z3fold and zbud do not require MMU so zram over these can be used on small Linux powered MMU-less IoT devices, as opposed to the traditional zram over zsmalloc. Otherwise I do agree with Dan.

Quoted 5 lines
Quoted 5 lines
---
 drivers/block/zram/zram_drv.c | 97

+++++++++++++++++++++++++++++++++++++++++++

Quoted 4 lines

Review

Quoted 4 lines
 drivers/block/zram/zram_drv.h |  5 +++
 2 files changed, 102 insertions(+)
(quoted diff) +0 -0

b/drivers/block/zram/zram_drv.c

Quoted 7 lines
Quoted 7 lines

index 9e2a83c..1f90bd0 100644

drivers/block/zram/zram_drv.c +0 -0
@@ -43,6 +43,11 @@ static const char *default_compressor = "lzo";
 /* Module params (documentation at end) */
 static unsigned int num_devices = 1;
 
Quoted 13 lines

drivers/block/zram/zram_drv.c static const char *default_compressor = "lzo"; · line 49

Quoted 4 lines
drivers/block/zram/zram_drv.c +4 -0
+#ifdef CONFIG_ZPOOL
+/* Compressed storage zpool to use */
+#define ZRAM_ZPOOL_DEFAULT "zsmalloc"
+#endif

It doesn't make sense for zram to conditionally use zpool; either it uses it and thus has 'select ZPOOL' in its Kconfig entry, or it doesn't use it at all.

Quoted 4 lines
drivers/block/zram/zram_drv.c +1 -0
+
 static inline void deprecated_attr_warn(const char *name)
 {
        pr_warn_once("%d (%s) Attribute %s (and others) will be

removed. %s\n",

Quoted 1 line

drivers/block/zram/zram_drv.c static ssize_t mem_used_total_show(struct device

Quoted 1 line
drivers/block/zram/zram_drv.c +0 -0
@@ -228,7 +233,11 @@ static ssize_t mem_used_total_show(struct device

*dev,

Quoted 5 lines

drivers/block/zram/zram_drv.c static ssize_t mem_used_total_show(struct device *dev,

Quoted 5 lines
drivers/block/zram/zram_drv.c +2 -0
        down_read(&zram->init_lock);
        if (init_done(zram)) {
                struct zram_meta *meta = zram->meta;
+#ifdef CONFIG_ZPOOL
+               val = zpool_get_total_size(meta->mem_pool) >>

PAGE_SHIFT;

Quoted 7 lines

drivers/block/zram/zram_drv.c static ssize_t mem_used_max_store(struct device

Quoted 7 lines
drivers/block/zram/zram_drv.c +2 -0
+#else
                val = zs_get_total_pages(meta->mem_pool);
+#endif
        }
        up_read(&zram->init_lock);
 
@@ -296,8 +305,14 @@ static ssize_t mem_used_max_store(struct device

*dev,

Quoted 14 lines
Quoted 14 lines
drivers/block/zram/zram_drv.c +6 -0
        down_read(&zram->init_lock);
        if (init_done(zram)) {
                struct zram_meta *meta = zram->meta;
+#ifdef CONFIG_ZPOOL
+               atomic_long_set(&zram->stats.max_used_pages,
+                               zpool_get_total_size(meta->mem_pool)
+                               >> PAGE_SHIFT);
+#else
                atomic_long_set(&zram->stats.max_used_pages,
                                zs_get_total_pages(meta->mem_pool));
+#endif
        }
        up_read(&zram->init_lock);
 
Quoted 1 line

drivers/block/zram/zram_drv.c static ssize_t comp_algorithm_store(struct device

Quoted 1 line
drivers/block/zram/zram_drv.c +0 -0
@@ -366,6 +381,18 @@ static ssize_t comp_algorithm_store(struct device

*dev,

Quoted 17 lines
Quoted 4 lines
drivers/block/zram/zram_drv.c +1 -0
        return len;
 }
 
+#ifdef CONFIG_ZPOOL

drivers/block/zram/zram_drv.c static ssize_t comp_algorithm_store(struct device *dev, · line 394

Quoted 10 lines
drivers/block/zram/zram_drv.c +10 -0
+static void zpool_compact(void *pool)
+{
+       zs_compact(pool);
+}
+
+static void zpool_stats(void *pool, struct zs_pool_stats *stats)
+{
+       zs_pool_stats(pool, stats);
+}
+#endif

first, no. this obviously makes using zpool in zram completely pointless.

Quoted 11 lines

second, did you test this? the pool you're passing is the zpool, not the zs_pool. quite bad things will happen when this code runs. There is no way to get the zs_pool from the zpool object (that's the point of abstraction, of course).

The fact zpool doesn't have these apis (currently) is one of the reasons against changing zram to use zpool.

Quoted 3 lines
drivers/block/zram/zram_drv.c +1 -0
+
 static ssize_t compact_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t

len)

Quoted 27 lines
Quoted 27 lines
drivers/block/zram/zram_drv.c +10 -0
 {
@@ -379,7 +406,11 @@ static ssize_t compact_store(struct device *dev,
        }
 
        meta = zram->meta;
+#ifdef CONFIG_ZPOOL
+       zpool_compact(meta->mem_pool);
+#else
        zs_compact(meta->mem_pool);
+#endif
        up_read(&zram->init_lock);
 
        return len;
@@ -416,8 +447,14 @@ static ssize_t mm_stat_show(struct device *dev,
 
        down_read(&zram->init_lock);
        if (init_done(zram)) {
+#ifdef CONFIG_ZPOOL
+               mem_used = zpool_get_total_size(zram->meta->mem_pool)
+                               >> PAGE_SHIFT;
+               zpool_stats(zram->meta->mem_pool, &pool_stats);
+#else
                mem_used = zs_get_total_pages(zram->meta->mem_pool);
                zs_pool_stats(zram->meta->mem_pool, &pool_stats);
+#endif
        }
 
Quoted 2 lines

drivers/block/zram/zram_drv.c static void zram_meta_free(struct zram_meta

Quoted 2 lines
drivers/block/zram/zram_drv.c +0 -0
        orig_size = atomic64_read(&zram->stats.pages_stored);
@@ -490,10 +527,18 @@ static void zram_meta_free(struct zram_meta

*meta, u64 disksize)

Quoted 10 lines
Quoted 10 lines
drivers/block/zram/zram_drv.c +4 -0
                if (!handle)
                        continue;
 
+#ifdef CONFIG_ZPOOL
+               zpool_free(meta->mem_pool, handle);
+#else
                zs_free(meta->mem_pool, handle);
+#endif
        }
 
Quoted 9 lines

drivers/block/zram/zram_drv.c static struct zram_meta *zram_meta_alloc(char

Quoted 9 lines
drivers/block/zram/zram_drv.c +4 -0
+#ifdef CONFIG_ZPOOL
+       zpool_destroy_pool(meta->mem_pool);
+#else
        zs_destroy_pool(meta->mem_pool);
+#endif
        vfree(meta->table);
        kfree(meta);
 }
@@ -513,7 +558,17 @@ static struct zram_meta *zram_meta_alloc(char

*pool_name, u64 disksize)

Quoted 18 lines

drivers/block/zram/zram_drv.c static void zram_free_page(struct zram *zram,

Quoted 18 lines
drivers/block/zram/zram_drv.c +10 -0
                goto out_error;
        }
 
+#ifdef CONFIG_ZPOOL
+       if (!zpool_has_pool(ZRAM_ZPOOL_DEFAULT)) {
+               pr_err("zpool %s not available\n", ZRAM_ZPOOL_DEFAULT);
+               goto out_error;
+       }
+
+       meta->mem_pool = zpool_create_pool(ZRAM_ZPOOL_DEFAULT,
+                                       pool_name, 0, NULL);
+#else
        meta->mem_pool = zs_create_pool(pool_name);
+#endif
        if (!meta->mem_pool) {
                pr_err("Error creating memory pool\n");
                goto out_error;
@@ -549,7 +604,11 @@ static void zram_free_page(struct zram *zram,

size_t index)

Quoted 9 lines
Quoted 9 lines
drivers/block/zram/zram_drv.c +4 -0
                return;
        }
 
+#ifdef CONFIG_ZPOOL
+       zpool_free(meta->mem_pool, handle);
+#else
        zs_free(meta->mem_pool, handle);
+#endif
 
Quoted 3 lines

drivers/block/zram/zram_drv.c static int zram_decompress_page(struct zram *zram,

Quoted 3 lines
drivers/block/zram/zram_drv.c +0 -0
        atomic64_sub(zram_get_obj_size(meta, index),
                        &zram->stats.compr_data_size);
@@ -577,7 +636,11 @@ static int zram_decompress_page(struct zram *zram,

char *mem, u32 index)

Quoted 12 lines

drivers/block/zram/zram_drv.c static int zram_decompress_page(struct zram *zram,

Quoted 12 lines
drivers/block/zram/zram_drv.c +4 -0
                return 0;
        }
 
+#ifdef CONFIG_ZPOOL
+       cmem = zpool_map_handle(meta->mem_pool, handle, ZPOOL_MM_RO);
+#else
        cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_RO);
+#endif
        if (size == PAGE_SIZE) {
                copy_page(mem, cmem);
        } else {
@@ -586,7 +649,11 @@ static int zram_decompress_page(struct zram *zram,

char *mem, u32 index)

Quoted 46 lines
Quoted 46 lines
drivers/block/zram/zram_drv.c +18 -0
                ret = zcomp_decompress(zstrm, cmem, size, mem);
                zcomp_stream_put(zram->comp);
        }
+#ifdef CONFIG_ZPOOL
+       zpool_unmap_handle(meta->mem_pool, handle);
+#else
        zs_unmap_object(meta->mem_pool, handle);
+#endif
        bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
 
        /* Should NEVER happen. Return bio error if it does. */
@@ -735,20 +802,34 @@ compress_again:
         * from the slow path and handle has already been allocated.
         */
        if (!handle)
+#ifdef CONFIG_ZPOOL
+               ret = zpool_malloc(meta->mem_pool, clen,
+                               __GFP_KSWAPD_RECLAIM |
+                               __GFP_NOWARN |
+                               __GFP_HIGHMEM |
+                               __GFP_MOVABLE, &handle);
+#else
                handle = zs_malloc(meta->mem_pool, clen,
                                __GFP_KSWAPD_RECLAIM |
                                __GFP_NOWARN |
                                __GFP_HIGHMEM |
                                __GFP_MOVABLE);
+#endif
        if (!handle) {
                zcomp_stream_put(zram->comp);
                zstrm = NULL;
 
                atomic64_inc(&zram->stats.writestall);
 
+#ifdef CONFIG_ZPOOL
+               ret = zpool_malloc(meta->mem_pool, clen,
+                               GFP_NOIO | __GFP_HIGHMEM |
+                               __GFP_MOVABLE, &handle);
+#else
                handle = zs_malloc(meta->mem_pool, clen,
                                GFP_NOIO | __GFP_HIGHMEM |
                                __GFP_MOVABLE);
+#endif
                if (handle)
                        goto compress_again;
 
Quoted 6 lines

drivers/block/zram/zram_drv.c compress_again: · line 843

Quoted 6 lines
drivers/block/zram/zram_drv.c +2 -0
@@ -758,16 +839,28 @@ compress_again:
                goto out;
        }
 
+#ifdef CONFIG_ZPOOL
+       alloced_pages = zpool_get_total_size(meta->mem_pool) >>

PAGE_SHIFT;

Quoted 33 lines
Quoted 33 lines
drivers/block/zram/zram_drv.c +14 -0
+#else
        alloced_pages = zs_get_total_pages(meta->mem_pool);
+#endif
        update_used_max(zram, alloced_pages);
 
        if (zram->limit_pages && alloced_pages > zram->limit_pages) {
+#ifdef CONFIG_ZPOOL
+               zpool_free(meta->mem_pool, handle);
+#else
                zs_free(meta->mem_pool, handle);
+#endif
                ret = -ENOMEM;
                goto out;
        }
 
+#ifdef CONFIG_ZPOOL
+       cmem = zpool_map_handle(meta->mem_pool, handle, ZPOOL_MM_WO);
+#else
        cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_WO);
+#endif
 
        if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) {
                src = kmap_atomic(page);
@@ -779,7 +872,11 @@ compress_again:
 
        zcomp_stream_put(zram->comp);
        zstrm = NULL;
+#ifdef CONFIG_ZPOOL
+       zpool_unmap_handle(meta->mem_pool, handle);
+#else
        zs_unmap_object(meta->mem_pool, handle);
+#endif
 
Quoted 3 lines

Review

Quoted 3 lines
drivers/block/zram/zram_drv.c +0 -0
        /*
         * Free memory associated with this sector
(quoted diff) +0 -0

b/drivers/block/zram/zram_drv.h

Quoted 22 lines
Quoted 22 lines

index 74fcf10..68f1222 100644

drivers/block/zram/zram_drv.h +5 -0
@@ -17,6 +17,7 @@
 
 #include <linux/rwsem.h>
 #include <linux/zsmalloc.h>
+#include <linux/zpool.h>
 #include <linux/crypto.h>
 
 #include "zcomp.h"
@@ -91,7 +92,11 @@ struct zram_stats {
 
 struct zram_meta {
        struct zram_table_entry *table;
+#ifdef CONFIG_ZPOOL
+       struct zpool *mem_pool;
+#else
        struct zs_pool *mem_pool;
+#endif
 };
 
Quoted 4 lines
Quoted 4 lines
drivers/block/zram/zram_drv.h +0 -1
 struct zram {
--

1.9.1

Best regards,
Vitaly

#5 lore raw

PATCH 1/1 zram: add zpool support

[resending to the list since it didn't work before]

Den 8 juni 2016 6:33 em skrev "Dan Streetman" <[email protected]>:

Quoted 9 lines · Geliang Tang
Quoted 2 lines

This patch adds zpool support for zram, it will allow us to use both the zpool api and directly zsmalloc api in zram.

besides the problems below, this was discussed a while ago and I believe Minchan is still against it, as nobody has so far shown what the benefit to zram would be; zram doesn't need the predictability, or evictability, of zbud or z3fold.

Well, I believe I have something to say here. z3fold is generally faster than zsmalloc which makes it a better choice for zram sometimes, e.g. when zram device is used for swap. Also, z3fold and zbud do not require MMU so zram over these can be used on small Linux powered MMU-less IoT devices, as opposed to the traditional zram over zsmalloc. Otherwise I do agree with Dan.

Quoted 285 lines
Quoted 13 lines
---
 drivers/block/zram/zram_drv.c | 97 +++++++++++++++++++++++++++++++++++++++++++
 drivers/block/zram/zram_drv.h |  5 +++
 2 files changed, 102 insertions(+)
drivers/block/zram/zram_drv.c +0 -0
index 9e2a83c..1f90bd0 100644

drivers/block/zram/zram_drv.c static const char *default_compressor = "lzo"; · line 49

Quoted 7 lines
drivers/block/zram/zram_drv.c +4 -0
@@ -43,6 +43,11 @@ static const char *default_compressor = "lzo";
 /* Module params (documentation at end) */
 static unsigned int num_devices = 1;
 
+#ifdef CONFIG_ZPOOL
+/* Compressed storage zpool to use */
+#define ZRAM_ZPOOL_DEFAULT "zsmalloc"
+#endif

It doesn't make sense for zram to conditionally use zpool; either it uses it and thus has 'select ZPOOL' in its Kconfig entry, or it doesn't use it at all.

Quoted 36 lines
drivers/block/zram/zram_drv.c +12 -0
+
 static inline void deprecated_attr_warn(const char *name)
 {
        pr_warn_once("%d (%s) Attribute %s (and others) will be removed. %s\n",
@@ -228,7 +233,11 @@ static ssize_t mem_used_total_show(struct device *dev,
        down_read(&zram->init_lock);
        if (init_done(zram)) {
                struct zram_meta *meta = zram->meta;
+#ifdef CONFIG_ZPOOL
+               val = zpool_get_total_size(meta->mem_pool) >> PAGE_SHIFT;
+#else
                val = zs_get_total_pages(meta->mem_pool);
+#endif
        }
        up_read(&zram->init_lock);
 
@@ -296,8 +305,14 @@ static ssize_t mem_used_max_store(struct device *dev,
        down_read(&zram->init_lock);
        if (init_done(zram)) {
                struct zram_meta *meta = zram->meta;
+#ifdef CONFIG_ZPOOL
+               atomic_long_set(&zram->stats.max_used_pages,
+                               zpool_get_total_size(meta->mem_pool)
+                               >> PAGE_SHIFT);
+#else
                atomic_long_set(&zram->stats.max_used_pages,
                                zs_get_total_pages(meta->mem_pool));
+#endif
        }
        up_read(&zram->init_lock);
 
@@ -366,6 +381,18 @@ static ssize_t comp_algorithm_store(struct device *dev,
        return len;
 }
 
+#ifdef CONFIG_ZPOOL

drivers/block/zram/zram_drv.c static ssize_t comp_algorithm_store(struct device *dev, · line 394

Quoted 10 lines
drivers/block/zram/zram_drv.c +10 -0
+static void zpool_compact(void *pool)
+{
+       zs_compact(pool);
+}
+
+static void zpool_stats(void *pool, struct zs_pool_stats *stats)
+{
+       zs_pool_stats(pool, stats);
+}
+#endif

first, no. this obviously makes using zpool in zram completely pointless.

second, did you test this? the pool you're passing is the zpool, not the zs_pool. quite bad things will happen when this code runs. There is no way to get the zs_pool from the zpool object (that's the point of abstraction, of course).

The fact zpool doesn't have these apis (currently) is one of the reasons against changing zram to use zpool.

Quoted 203 lines
drivers/block/zram/zram_drv.c +71 -0
+
 static ssize_t compact_store(struct device *dev,
                struct device_attribute *attr, const char *buf, size_t len)
 {
@@ -379,7 +406,11 @@ static ssize_t compact_store(struct device *dev,
        }
 
        meta = zram->meta;
+#ifdef CONFIG_ZPOOL
+       zpool_compact(meta->mem_pool);
+#else
        zs_compact(meta->mem_pool);
+#endif
        up_read(&zram->init_lock);
 
        return len;
@@ -416,8 +447,14 @@ static ssize_t mm_stat_show(struct device *dev,
 
        down_read(&zram->init_lock);
        if (init_done(zram)) {
+#ifdef CONFIG_ZPOOL
+               mem_used = zpool_get_total_size(zram->meta->mem_pool)
+                               >> PAGE_SHIFT;
+               zpool_stats(zram->meta->mem_pool, &pool_stats);
+#else
                mem_used = zs_get_total_pages(zram->meta->mem_pool);
                zs_pool_stats(zram->meta->mem_pool, &pool_stats);
+#endif
        }
 
        orig_size = atomic64_read(&zram->stats.pages_stored);
@@ -490,10 +527,18 @@ static void zram_meta_free(struct zram_meta *meta, u64 disksize)
                if (!handle)
                        continue;
 
+#ifdef CONFIG_ZPOOL
+               zpool_free(meta->mem_pool, handle);
+#else
                zs_free(meta->mem_pool, handle);
+#endif
        }
 
+#ifdef CONFIG_ZPOOL
+       zpool_destroy_pool(meta->mem_pool);
+#else
        zs_destroy_pool(meta->mem_pool);
+#endif
        vfree(meta->table);
        kfree(meta);
 }
@@ -513,7 +558,17 @@ static struct zram_meta *zram_meta_alloc(char *pool_name, u64 disksize)
                goto out_error;
        }
 
+#ifdef CONFIG_ZPOOL
+       if (!zpool_has_pool(ZRAM_ZPOOL_DEFAULT)) {
+               pr_err("zpool %s not available\n", ZRAM_ZPOOL_DEFAULT);
+               goto out_error;
+       }
+
+       meta->mem_pool = zpool_create_pool(ZRAM_ZPOOL_DEFAULT,
+                                       pool_name, 0, NULL);
+#else
        meta->mem_pool = zs_create_pool(pool_name);
+#endif
        if (!meta->mem_pool) {
                pr_err("Error creating memory pool\n");
                goto out_error;
@@ -549,7 +604,11 @@ static void zram_free_page(struct zram *zram, size_t index)
                return;
        }
 
+#ifdef CONFIG_ZPOOL
+       zpool_free(meta->mem_pool, handle);
+#else
        zs_free(meta->mem_pool, handle);
+#endif
 
        atomic64_sub(zram_get_obj_size(meta, index),
                        &zram->stats.compr_data_size);
@@ -577,7 +636,11 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index)
                return 0;
        }
 
+#ifdef CONFIG_ZPOOL
+       cmem = zpool_map_handle(meta->mem_pool, handle, ZPOOL_MM_RO);
+#else
        cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_RO);
+#endif
        if (size == PAGE_SIZE) {
                copy_page(mem, cmem);
        } else {
@@ -586,7 +649,11 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index)
                ret = zcomp_decompress(zstrm, cmem, size, mem);
                zcomp_stream_put(zram->comp);
        }
+#ifdef CONFIG_ZPOOL
+       zpool_unmap_handle(meta->mem_pool, handle);
+#else
        zs_unmap_object(meta->mem_pool, handle);
+#endif
        bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
 
        /* Should NEVER happen. Return bio error if it does. */
@@ -735,20 +802,34 @@ compress_again:
         * from the slow path and handle has already been allocated.
         */
        if (!handle)
+#ifdef CONFIG_ZPOOL
+               ret = zpool_malloc(meta->mem_pool, clen,
+                               __GFP_KSWAPD_RECLAIM |
+                               __GFP_NOWARN |
+                               __GFP_HIGHMEM |
+                               __GFP_MOVABLE, &handle);
+#else
                handle = zs_malloc(meta->mem_pool, clen,
                                __GFP_KSWAPD_RECLAIM |
                                __GFP_NOWARN |
                                __GFP_HIGHMEM |
                                __GFP_MOVABLE);
+#endif
        if (!handle) {
                zcomp_stream_put(zram->comp);
                zstrm = NULL;
 
                atomic64_inc(&zram->stats.writestall);
 
+#ifdef CONFIG_ZPOOL
+               ret = zpool_malloc(meta->mem_pool, clen,
+                               GFP_NOIO | __GFP_HIGHMEM |
+                               __GFP_MOVABLE, &handle);
+#else
                handle = zs_malloc(meta->mem_pool, clen,
                                GFP_NOIO | __GFP_HIGHMEM |
                                __GFP_MOVABLE);
+#endif
                if (handle)
                        goto compress_again;
 
@@ -758,16 +839,28 @@ compress_again:
                goto out;
        }
 
+#ifdef CONFIG_ZPOOL
+       alloced_pages = zpool_get_total_size(meta->mem_pool) >> PAGE_SHIFT;
+#else
        alloced_pages = zs_get_total_pages(meta->mem_pool);
+#endif
        update_used_max(zram, alloced_pages);
 
        if (zram->limit_pages && alloced_pages > zram->limit_pages) {
+#ifdef CONFIG_ZPOOL
+               zpool_free(meta->mem_pool, handle);
+#else
                zs_free(meta->mem_pool, handle);
+#endif
                ret = -ENOMEM;
                goto out;
        }
 
+#ifdef CONFIG_ZPOOL
+       cmem = zpool_map_handle(meta->mem_pool, handle, ZPOOL_MM_WO);
+#else
        cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_WO);
+#endif
 
        if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) {
                src = kmap_atomic(page);
@@ -779,7 +872,11 @@ compress_again:
 
        zcomp_stream_put(zram->comp);
        zstrm = NULL;
+#ifdef CONFIG_ZPOOL
+       zpool_unmap_handle(meta->mem_pool, handle);
+#else
        zs_unmap_object(meta->mem_pool, handle);
+#endif
 
        /*
         * Free memory associated with this sector
drivers/block/zram/zram_drv.h +5 -0
index 74fcf10..68f1222 100644
@@ -17,6 +17,7 @@
 
 #include <linux/rwsem.h>
 #include <linux/zsmalloc.h>
+#include <linux/zpool.h>
 #include <linux/crypto.h>
 
 #include "zcomp.h"
@@ -91,7 +92,11 @@ struct zram_stats {
 
 struct zram_meta {
        struct zram_table_entry *table;
+#ifdef CONFIG_ZPOOL
+       struct zpool *mem_pool;
+#else
        struct zs_pool *mem_pool;
+#endif
 };
 
Quoted 4 lines
Quoted 4 lines
drivers/block/zram/zram_drv.h +0 -1
 struct zram {
--

1.9.1

Best regards,
Vitaly

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#6 lore raw

PATCH 1/1 zram: add zpool support v2

In reply to Vitaly Wool
Quoted 21 lines · Vitaly Wool

Den 8 juni 2016 6:33 em skrev "Dan Streetman" <[email protected]>:

Quoted 2 lines

On Wed, Jun 8, 2016 at 5:39 AM, Geliang Tang <[email protected]>

Quoted 8 lines · wrote:
Quoted 2 lines

This patch adds zpool support for zram, it will allow us to use both the zpool api and directly zsmalloc api in zram.

besides the problems below, this was discussed a while ago and I believe Minchan is still against it, as nobody has so far shown what the benefit to zram would be; zram doesn't need the predictability, or evictability, of zbud or z3fold.

Quoted 8 lines

Right.

Geliang, I cannot ack without any *detail* that what's the problem of zram/zsmalloc, why we can't fix it in zsmalloc itself.
The zbud and zsmalloc is otally different design to aim different goal determinism vs efficiency so you can choose what you want between zswap
and zram rather than mixing the features.

Quoted 26 lines

I'd also probably Cc Vitaly Wool on this

Well, I believe I have something to say here. z3fold is generally faster than zsmalloc which makes it a better choice for zram sometimes, e.g. when zram device is used for swap. Also, z3fold and zbud do not require MMU so zram over these can be used on small Linux powered MMU-less IoT devices, as opposed to the traditional zram over zsmalloc. Otherwise I do agree with Dan.

Quoted 17 lines

It doesn't make sense for zram to conditionally use zpool; either it uses it and thus has 'select ZPOOL' in its Kconfig entry, or it doesn't use it at all.

Quoted 1 line

+#endif

first, no. this obviously makes using zpool in zram completely pointless.

second, did you test this? the pool you're passing is the zpool, not the zs_pool. quite bad things will happen when this code runs. There is no way to get the zs_pool from the zpool object (that's the point of abstraction, of course).

The fact zpool doesn't have these apis (currently) is one of the reasons against changing zram to use zpool.

Thank you all for your reply. I updated the patch and I hope this is better.

Geliang Tang (1):
zram: update zram to use zpool

 drivers/block/zram/Kconfig    |  3 ++-
 drivers/block/zram/zram_drv.c | 59 ++++++++++++++++++++++---------------------
 drivers/block/zram/zram_drv.h |  4 +--
 mm/zsmalloc.c                 | 12 +++++----
 4 files changed, 41 insertions(+), 37 deletions(-)
Signature · 7 lines
2.5.5

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#7 lore raw

PATCH 1/1 zram: update zram to use zpool

Change zram to use the zpool api instead of directly using zsmalloc. The zpool api doesn't have zs_compact() and zs_pool_stats() functions. I did the following two things to fix it.
1) I replace zs_compact() with zpool_shrink(), use zpool_shrink() to call zs_compact() in zsmalloc.
2) The 'pages_compacted' attribute is showed in zram by calling zs_pool_stats(). So in order not to call zs_pool_state() I move the attribute to zsmalloc.

---
 drivers/block/zram/Kconfig    |  3 ++-
 drivers/block/zram/zram_drv.c | 59 ++++++++++++++++++++++---------------------
 drivers/block/zram/zram_drv.h |  4 +--
 mm/zsmalloc.c                 | 12 +++++----
 4 files changed, 41 insertions(+), 37 deletions(-)
drivers/block/zram/Kconfig +2 -1
index b8ecba6..6389a5a 100644
@@ -1,6 +1,7 @@
 config ZRAM
 	tristate "Compressed RAM block device support"
-	depends on BLOCK && SYSFS && ZSMALLOC && CRYPTO
+	depends on BLOCK && SYSFS && ZPOOL && CRYPTO
+	select ZSMALLOC
 	select CRYPTO_LZO
 	default n
 	help
drivers/block/zram/zram_drv.c +30 -29

173 lines not on this page · raw message

drivers/block/zram/zram_drv.h +2 -2
index 74fcf10..de3e013 100644
@@ -16,7 +16,7 @@
 #define _ZRAM_DRV_H_
 
 #include <linux/rwsem.h>
-#include <linux/zsmalloc.h>
+#include <linux/zpool.h>
 #include <linux/crypto.h>
 
 #include "zcomp.h"
@@ -91,7 +91,7 @@ struct zram_stats {
 
 struct zram_meta {
 	struct zram_table_entry *table;
-	struct zs_pool *mem_pool;
+	struct zpool *mem_pool;
 };
 
 struct zram {
mm/zsmalloc.c +7 -5
index 6a58edc..56e6439 100644
@@ -421,7 +421,8 @@ static void zs_zpool_free(void *pool, unsigned long handle)
 static int zs_zpool_shrink(void *pool, unsigned int pages,
 			unsigned int *reclaimed)
 {
-	return -EINVAL;
+	zs_compact(pool);
+	return 0;
 }
 
 static void *zs_zpool_map(void *pool, unsigned long handle,
@@ -609,10 +610,10 @@ static int zs_stats_size_show(struct seq_file *s, void *v)
 	unsigned long total_objs = 0, total_used_objs = 0, total_pages = 0;
 	unsigned long total_freeable = 0;
 
-	seq_printf(s, " %5s %5s %11s %12s %13s %10s %10s %16s %8s\n",
+	seq_printf(s, " %5s %5s %11s %12s %13s %10s %10s %16s %8s %15s\n",
 			"class", "size", "almost_full", "almost_empty",
 			"obj_allocated", "obj_used", "pages_used",
-			"pages_per_zspage", "freeable");
+			"pages_per_zspage", "freeable", "pages_compacted");
 
 	for (i = 0; i < zs_size_classes; i++) {
 		class = pool->size_class[i];
@@ -648,10 +649,11 @@ static int zs_stats_size_show(struct seq_file *s, void *v)
 	}
 
 	seq_puts(s, "\n");
-	seq_printf(s, " %5s %5s %11lu %12lu %13lu %10lu %10lu %16s %8lu\n",
+	seq_printf(s, " %5s %5s %11lu %12lu %13lu %10lu %10lu %16s %8lu %15lu\n",
 			"Total", "", total_class_almost_full,
 			total_class_almost_empty, total_objs,
-			total_used_objs, total_pages, "", total_freeable);
+			total_used_objs, total_pages, "", total_freeable,
+			pool->stats.pages_compacted);
 
 	return 0;
 }
Signature · 7 lines
2.5.5

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to [email protected].  For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
#8 lore raw

Re: [PATCH] zram: update zram to use zpool

Quoted 8 lines · Geliang Tang

Change zram to use the zpool api instead of directly using zsmalloc. The zpool api doesn't have zs_compact() and zs_pool_stats() functions. I did the following two things to fix it.
1) I replace zs_compact() with zpool_shrink(), use zpool_shrink() to call zs_compact() in zsmalloc.
2) The 'pages_compacted' attribute is showed in zram by calling zs_pool_stats(). So in order not to call zs_pool_state() I move the attribute to zsmalloc.

I think you're going to have quite a hard time getting a patch like this accepted without doing some convincing that it's really needed, and I don't see any new reasons here. Possibly hard data on how it improves speed or some other metric, plus reasoning on how using zpool is better for zram than keeping its direct link to zsmalloc (meaning, why can't zsmalloc just be improved to perform on par with zpool, on whatever metric you're comparing).

Quoted 239 lines
---
 drivers/block/zram/Kconfig    |  3 ++-
 drivers/block/zram/zram_drv.c | 59 ++++++++++++++++++++++---------------------
 drivers/block/zram/zram_drv.h |  4 +--
 mm/zsmalloc.c                 | 12 +++++----
 4 files changed, 41 insertions(+), 37 deletions(-)
drivers/block/zram/Kconfig +2 -1
index b8ecba6..6389a5a 100644
@@ -1,6 +1,7 @@
 config ZRAM
        tristate "Compressed RAM block device support"
-       depends on BLOCK && SYSFS && ZSMALLOC && CRYPTO
+       depends on BLOCK && SYSFS && ZPOOL && CRYPTO
+       select ZSMALLOC
        select CRYPTO_LZO
        default n
        help
drivers/block/zram/zram_drv.c +30 -29
index 7454cf1..7ee9050 100644
@@ -39,6 +39,7 @@ static DEFINE_MUTEX(zram_index_mutex);
 
 static int zram_major;
 static const char *default_compressor = "lzo";
+static char *default_zpool_type = "zsmalloc";
 
 /* Module params (documentation at end) */
 static unsigned int num_devices = 1;
@@ -228,11 +229,11 @@ static ssize_t mem_used_total_show(struct device *dev,
        down_read(&zram->init_lock);
        if (init_done(zram)) {
                struct zram_meta *meta = zram->meta;
-               val = zs_get_total_pages(meta->mem_pool);
+               val = zpool_get_total_size(meta->mem_pool);
        }
        up_read(&zram->init_lock);
 
-       return scnprintf(buf, PAGE_SIZE, "%llu\n", val << PAGE_SHIFT);
+       return scnprintf(buf, PAGE_SIZE, "%llu\n", val);
 }
 
 static ssize_t mem_limit_show(struct device *dev,
@@ -297,7 +298,7 @@ static ssize_t mem_used_max_store(struct device *dev,
        if (init_done(zram)) {
                struct zram_meta *meta = zram->meta;
                atomic_long_set(&zram->stats.max_used_pages,
-                               zs_get_total_pages(meta->mem_pool));
+                       zpool_get_total_size(meta->mem_pool) >> PAGE_SHIFT);
        }
        up_read(&zram->init_lock);
 
@@ -379,7 +380,7 @@ static ssize_t compact_store(struct device *dev,
        }
 
        meta = zram->meta;
-       zs_compact(meta->mem_pool);
+       zpool_shrink(meta->mem_pool, 0, NULL);
        up_read(&zram->init_lock);
 
        return len;
@@ -407,31 +408,25 @@ static ssize_t mm_stat_show(struct device *dev,
                struct device_attribute *attr, char *buf)
 {
        struct zram *zram = dev_to_zram(dev);
-       struct zs_pool_stats pool_stats;
        u64 orig_size, mem_used = 0;
        long max_used;
        ssize_t ret;
 
-       memset(&pool_stats, 0x00, sizeof(struct zs_pool_stats));
-
        down_read(&zram->init_lock);
-       if (init_done(zram)) {
-               mem_used = zs_get_total_pages(zram->meta->mem_pool);
-               zs_pool_stats(zram->meta->mem_pool, &pool_stats);
-       }
+       if (init_done(zram))
+               mem_used = zpool_get_total_size(zram->meta->mem_pool);
 
        orig_size = atomic64_read(&zram->stats.pages_stored);
        max_used = atomic_long_read(&zram->stats.max_used_pages);
 
        ret = scnprintf(buf, PAGE_SIZE,
-                       "%8llu %8llu %8llu %8lu %8ld %8llu %8lu\n",
+                       "%8llu %8llu %8llu %8lu %8ld %8llu\n",
                        orig_size << PAGE_SHIFT,
                        (u64)atomic64_read(&zram->stats.compr_data_size),
-                       mem_used << PAGE_SHIFT,
+                       mem_used,
                        zram->limit_pages << PAGE_SHIFT,
                        max_used << PAGE_SHIFT,
-                       (u64)atomic64_read(&zram->stats.zero_pages),
-                       pool_stats.pages_compacted);
+                       (u64)atomic64_read(&zram->stats.zero_pages));
        up_read(&zram->init_lock);
 
        return ret;
@@ -490,10 +485,10 @@ static void zram_meta_free(struct zram_meta *meta, u64 disksize)
                if (!handle)
                        continue;
 
-               zs_free(meta->mem_pool, handle);
+               zpool_free(meta->mem_pool, handle);
        }
 
-       zs_destroy_pool(meta->mem_pool);
+       zpool_destroy_pool(meta->mem_pool);
        vfree(meta->table);
        kfree(meta);
 }
@@ -513,7 +508,13 @@ static struct zram_meta *zram_meta_alloc(char *pool_name, u64 disksize)
                goto out_error;
        }
 
-       meta->mem_pool = zs_create_pool(pool_name);
+       if (!zpool_has_pool(default_zpool_type)) {
+               pr_err("zpool %s not available\n", default_zpool_type);
+               goto out_error;
+       }
+
+       meta->mem_pool = zpool_create_pool(default_zpool_type,
+                                          pool_name, 0, NULL);
        if (!meta->mem_pool) {
                pr_err("Error creating memory pool\n");
                goto out_error;
@@ -549,7 +550,7 @@ static void zram_free_page(struct zram *zram, size_t index)
                return;
        }
 
-       zs_free(meta->mem_pool, handle);
+       zpool_free(meta->mem_pool, handle);
 
        atomic64_sub(zram_get_obj_size(meta, index),
                        &zram->stats.compr_data_size);
@@ -577,7 +578,7 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index)
                return 0;
        }
 
-       cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_RO);
+       cmem = zpool_map_handle(meta->mem_pool, handle, ZPOOL_MM_RO);
        if (size == PAGE_SIZE) {
                copy_page(mem, cmem);
        } else {
@@ -586,7 +587,7 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index)
                ret = zcomp_decompress(zstrm, cmem, size, mem);
                zcomp_stream_put(zram->comp);
        }
-       zs_unmap_object(meta->mem_pool, handle);
+       zpool_unmap_handle(meta->mem_pool, handle);
        bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
 
        /* Should NEVER happen. Return bio error if it does. */
@@ -735,20 +736,20 @@ compress_again:
         * from the slow path and handle has already been allocated.
         */
        if (!handle)
-               handle = zs_malloc(meta->mem_pool, clen,
+               ret = zpool_malloc(meta->mem_pool, clen,
                                __GFP_KSWAPD_RECLAIM |
                                __GFP_NOWARN |
                                __GFP_HIGHMEM |
-                               __GFP_MOVABLE);
+                               __GFP_MOVABLE, &handle);
        if (!handle) {
                zcomp_stream_put(zram->comp);
                zstrm = NULL;
 
                atomic64_inc(&zram->stats.writestall);
 
-               handle = zs_malloc(meta->mem_pool, clen,
+               ret = zpool_malloc(meta->mem_pool, clen,
                                GFP_NOIO | __GFP_HIGHMEM |
-                               __GFP_MOVABLE);
+                               __GFP_MOVABLE, &handle);
                if (handle)
                        goto compress_again;
 
@@ -758,16 +759,16 @@ compress_again:
                goto out;
        }
 
-       alloced_pages = zs_get_total_pages(meta->mem_pool);
+       alloced_pages = zpool_get_total_size(meta->mem_pool) >> PAGE_SHIFT;
        update_used_max(zram, alloced_pages);
 
        if (zram->limit_pages && alloced_pages > zram->limit_pages) {
-               zs_free(meta->mem_pool, handle);
+               zpool_free(meta->mem_pool, handle);
                ret = -ENOMEM;
                goto out;
        }
 
-       cmem = zs_map_object(meta->mem_pool, handle, ZS_MM_WO);
+       cmem = zpool_map_handle(meta->mem_pool, handle, ZPOOL_MM_WO);
 
        if ((clen == PAGE_SIZE) && !is_partial_io(bvec)) {
                src = kmap_atomic(page);
@@ -779,7 +780,7 @@ compress_again:
 
        zcomp_stream_put(zram->comp);
        zstrm = NULL;
-       zs_unmap_object(meta->mem_pool, handle);
+       zpool_unmap_handle(meta->mem_pool, handle);
 
        /*
         * Free memory associated with this sector
drivers/block/zram/zram_drv.h +2 -2
index 74fcf10..de3e013 100644
@@ -16,7 +16,7 @@
 #define _ZRAM_DRV_H_
 
 #include <linux/rwsem.h>
-#include <linux/zsmalloc.h>
+#include <linux/zpool.h>
 #include <linux/crypto.h>
 
 #include "zcomp.h"
@@ -91,7 +91,7 @@ struct zram_stats {
 
 struct zram_meta {
        struct zram_table_entry *table;
-       struct zs_pool *mem_pool;
+       struct zpool *mem_pool;
 };
 
 struct zram {
mm/zsmalloc.c +0 -0
index 6a58edc..56e6439 100644

mm/zsmalloc.c static void zs_zpool_free(void *pool, unsigned long handle) · line 425

Quoted 6 lines
mm/zsmalloc.c +2 -1
@@ -421,7 +421,8 @@ static void zs_zpool_free(void *pool, unsigned long handle)
 static int zs_zpool_shrink(void *pool, unsigned int pages,
                        unsigned int *reclaimed)
 {
-       return -EINVAL;
+       zs_compact(pool);
+       return 0;

If we're doing this, I'd like it to be implemented correctly; zs_compact returns the number of pages compacted, so you should set reclaimed to that.

Quoted 31 lines
mm/zsmalloc.c +5 -5
 }
 
 static void *zs_zpool_map(void *pool, unsigned long handle,
@@ -609,10 +610,10 @@ static int zs_stats_size_show(struct seq_file *s, void *v)
        unsigned long total_objs = 0, total_used_objs = 0, total_pages = 0;
        unsigned long total_freeable = 0;
 
-       seq_printf(s, " %5s %5s %11s %12s %13s %10s %10s %16s %8s\n",
+       seq_printf(s, " %5s %5s %11s %12s %13s %10s %10s %16s %8s %15s\n",
                        "class", "size", "almost_full", "almost_empty",
                        "obj_allocated", "obj_used", "pages_used",
-                       "pages_per_zspage", "freeable");
+                       "pages_per_zspage", "freeable", "pages_compacted");
 
        for (i = 0; i < zs_size_classes; i++) {
                class = pool->size_class[i];
@@ -648,10 +649,11 @@ static int zs_stats_size_show(struct seq_file *s, void *v)
        }
 
        seq_puts(s, "\n");
-       seq_printf(s, " %5s %5s %11lu %12lu %13lu %10lu %10lu %16s %8lu\n",
+       seq_printf(s, " %5s %5s %11lu %12lu %13lu %10lu %10lu %16s %8lu %15lu\n",
                        "Total", "", total_class_almost_full,
                        total_class_almost_empty, total_objs,
-                       total_used_objs, total_pages, "", total_freeable);
+                       total_used_objs, total_pages, "", total_freeable,
+                       pool->stats.pages_compacted);
 
        return 0;
 }
--
Quoted 2 lines

2.5.5

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#9 lore raw

Re: [PATCH] zram: update zram to use zpool

In reply to Geliang Tang
url: https://github.com/0day-ci/linux/commits/Geliang-Tang/zram-update-zram-to-use-zpool/20160615-224513 config: sh-allyesconfig (attached as .config) compiler: sh4-linux-gnu-gcc (Debian 5.3.1-8)…

Hi,

[auto build test WARNING on next-20160615]
[cannot apply to v4.7-rc3 v4.7-rc2 v4.7-rc1 v4.7-rc3]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url: https://github.com/0day-ci/linux/commits/Geliang-Tang/zram-update-zram-to-use-zpool/20160615-224513
config: sh-allyesconfig (attached as .config)
compiler: sh4-linux-gnu-gcc (Debian 5.3.1-8) 5.3.1 20160205
reproduce:
wget https://git.kernel.org/cgit/linux/kernel/git/wfg/lkp-tests.git/plain/sbin/make.cross -O ~/bin/make.cross chmod +x ~/bin/make.cross
# save the attached .config to linux build tree
make.cross ARCH=sh

All warnings (new ones prefixed by >>):

warning: (ZRAM) selects ZSMALLOC which has unmet direct dependencies (MMU)

---
0-DAY kernel test infrastructure Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all Intel Corporation

maintainer NAK
#10 lore raw

Re: [PATCH] zram: update zram to use zpool

In reply to Geliang Tang
Quoted 10 lines · Geliang Tang

Change zram to use the zpool api instead of directly using zsmalloc. The zpool api doesn't have zs_compact() and zs_pool_stats() functions. I did the following two things to fix it.
1) I replace zs_compact() with zpool_shrink(), use zpool_shrink() to call zs_compact() in zsmalloc.
2) The 'pages_compacted' attribute is showed in zram by calling zs_pool_stats(). So in order not to call zs_pool_state() I move the attribute to zsmalloc.

NACK.

I already explained why.
http://lkml.kernel.org/r/20160609013411.GA29779@bbox

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#11 lore raw

Re: [PATCH] zram: update zram to use zpool

Hi Minchan,

Quoted 10 lines · Minchan Kim
Quoted 9 lines · Geliang Tang

Change zram to use the zpool api instead of directly using zsmalloc. The zpool api doesn't have zs_compact() and zs_pool_stats() functions. I did the following two things to fix it.
1) I replace zs_compact() with zpool_shrink(), use zpool_shrink() to call zs_compact() in zsmalloc.
2) The 'pages_compacted' attribute is showed in zram by calling zs_pool_stats(). So in order not to call zs_pool_state() I move the attribute to zsmalloc.

Quoted 6 lines
Quoted 1 line

NACK.

I already explained why.
http://lkml.kernel.org/r/20160609013411.GA29779@bbox

This is a fair statement, to a certain extent. I'll let Geliang speak for himself but I am personally interested in this zram extension because I want it to work on MMU-less systems. zsmalloc can not handle that, so I want to be able to use zram over z3fold.

Best regards,
Vitaly

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

#12 lore raw

Re: [PATCH] zram: update zram to use zpool

Quoted 17 lines · Vitaly Wool

Hi Minchan,

Quoted 14 lines · Minchan Kim
Quoted 10 lines · Geliang Tang

Change zram to use the zpool api instead of directly using zsmalloc. The zpool api doesn't have zs_compact() and zs_pool_stats() functions. I did the following two things to fix it.
1) I replace zs_compact() with zpool_shrink(), use zpool_shrink() to call zs_compact() in zsmalloc.
2) The 'pages_compacted' attribute is showed in zram by calling zs_pool_stats(). So in order not to call zs_pool_state() I move the attribute to zsmalloc.

NACK.

Quoted 7 lines
Quoted 2 lines

I already explained why.
http://lkml.kernel.org/r/20160609013411.GA29779@bbox

This is a fair statement, to a certain extent. I'll let Geliang speak for himself but I am personally interested in this zram extension because I want it to work on MMU-less systems. zsmalloc can not handle that, so I want to be able to use zram over z3fold.

I concur with this.

It's also worth pointing out that people can and do use zram for things other than swap, so the assumption that zswap is a viable alternative is not universally correct. In my case for example, I use it on a VM host for temporary storage for transient SSI VM's. Making it more deterministic would be seriously helpful in this case, as it would mean I can more precisely provision resources on this particular system, and could better account for latencies in the testing these transient VM's are used for.

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

maintainer
#13 lore raw

Re: [PATCH] zram: update zram to use zpool

In reply to Vitaly Wool
Quoted 17 lines · Vitaly Wool

Hi Minchan,

Quoted 14 lines · Minchan Kim
Quoted 10 lines · Geliang Tang

Change zram to use the zpool api instead of directly using zsmalloc. The zpool api doesn't have zs_compact() and zs_pool_stats() functions. I did the following two things to fix it.
1) I replace zs_compact() with zpool_shrink(), use zpool_shrink() to call zs_compact() in zsmalloc.
2) The 'pages_compacted' attribute is showed in zram by calling zs_pool_stats(). So in order not to call zs_pool_state() I move the attribute to zsmalloc.

NACK.

Quoted 7 lines
Quoted 2 lines

I already explained why.
http://lkml.kernel.org/r/20160609013411.GA29779@bbox

This is a fair statement, to a certain extent. I'll let Geliang speak for himself but I am personally interested in this zram extension because I want it to work on MMU-less systems. zsmalloc can not handle that, so I want to be able to use zram over z3fold.

Could you tell me more detail? What's the usecase?

Quoted 9 lines

Best regards,
Vitaly

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>

--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>