linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] mm: zpool: add common api for zswap to use zbud/zsmalloc
@ 2014-04-19 15:52 Dan Streetman
  2014-04-19 15:52 ` [PATCH 1/4] mm: zpool: zbud_alloc() minor param change Dan Streetman
                   ` (5 more replies)
  0 siblings, 6 replies; 65+ messages in thread
From: Dan Streetman @ 2014-04-19 15:52 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Weijie Yang, Johannes Weiner, Sergey Senozhatsky,
	Linux-MM, linux-kernel

In order to allow zswap users to choose between zbud and zsmalloc for
the compressed storage pool, this patch set adds a new api "zpool" that
provides an interface to both zbud and zsmalloc.  Only a minor change
to zbud's interface was needed, as detailed in the first patch;
zsmalloc required shrinking to be added and a minor interface change,
as detailed in the second patch.

I believe Seth originally was using zsmalloc for swap, but there were
concerns about how significant the impact of shrinking zsmalloc would
be when zswap had to start reclaiming pages.  That still may be an
issue, but this at least allows users to choose themselves whether
they want a lower-density or higher-density compressed storage medium.
At least for situations where zswap reclaim is never or rarely reached,
it probably makes sense to use the higher density of zsmalloc.

Note this patch series does not change zram to use zpool, although that
change should be possible as well.


Dan Streetman (4):
  mm: zpool: zbud_alloc() minor param change
  mm: zpool: implement zsmalloc shrinking
  mm: zpool: implement common zpool api to zbud/zsmalloc
  mm: zpool: update zswap to use zpool

 drivers/block/zram/zram_drv.c |   2 +-
 include/linux/zbud.h          |   3 +-
 include/linux/zpool.h         | 166 ++++++++++++++++++
 include/linux/zsmalloc.h      |   7 +-
 mm/Kconfig                    |  43 +++--
 mm/Makefile                   |   1 +
 mm/zbud.c                     |  28 ++--
 mm/zpool.c                    | 380 ++++++++++++++++++++++++++++++++++++++++++
 mm/zsmalloc.c                 | 168 +++++++++++++++++--
 mm/zswap.c                    |  70 ++++----
 10 files changed, 787 insertions(+), 81 deletions(-)
 create mode 100644 include/linux/zpool.h
 create mode 100644 mm/zpool.c

-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCH 1/4] mm: zpool: zbud_alloc() minor param change
  2014-04-19 15:52 [PATCH 0/4] mm: zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
@ 2014-04-19 15:52 ` Dan Streetman
  2014-04-19 15:52 ` [PATCH 2/4] mm: zpool: implement zsmalloc shrinking Dan Streetman
                   ` (4 subsequent siblings)
  5 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-04-19 15:52 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Weijie Yang, Johannes Weiner, Sergey Senozhatsky,
	Linux-MM, linux-kernel

Change zbud to store gfp_t flags passed at pool creation to use for
each alloc; this allows the api to be closer to the existing zsmalloc
interface, and the only current zbud user (zswap) uses the same gfp
flags for all allocs.  Update zswap to use changed interface.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>

---
 include/linux/zbud.h |  3 +--
 mm/zbud.c            | 28 +++++++++++++++-------------
 mm/zswap.c           |  6 +++---
 3 files changed, 19 insertions(+), 18 deletions(-)

diff --git a/include/linux/zbud.h b/include/linux/zbud.h
index 2571a5c..50563b6 100644
--- a/include/linux/zbud.h
+++ b/include/linux/zbud.h
@@ -11,8 +11,7 @@ struct zbud_ops {
 
 struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops);
 void zbud_destroy_pool(struct zbud_pool *pool);
-int zbud_alloc(struct zbud_pool *pool, int size, gfp_t gfp,
-	unsigned long *handle);
+int zbud_alloc(struct zbud_pool *pool, int size, unsigned long *handle);
 void zbud_free(struct zbud_pool *pool, unsigned long handle);
 int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries);
 void *zbud_map(struct zbud_pool *pool, unsigned long handle);
diff --git a/mm/zbud.c b/mm/zbud.c
index 9451361..e02f53f 100644
--- a/mm/zbud.c
+++ b/mm/zbud.c
@@ -94,6 +94,7 @@ struct zbud_pool {
 	struct list_head lru;
 	u64 pages_nr;
 	struct zbud_ops *ops;
+	gfp_t gfp;
 };
 
 /*
@@ -193,9 +194,12 @@ static int num_free_chunks(struct zbud_header *zhdr)
 *****************/
 /**
  * zbud_create_pool() - create a new zbud pool
- * @gfp:	gfp flags when allocating the zbud pool structure
+ * @gfp:	gfp flags when growing the pool
  * @ops:	user-defined operations for the zbud pool
  *
+ * gfp should not set __GFP_HIGHMEM as highmem pages cannot be used
+ * as zbud pool pages.
+ *
  * Return: pointer to the new zbud pool or NULL if the metadata allocation
  * failed.
  */
@@ -204,7 +208,9 @@ struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops)
 	struct zbud_pool *pool;
 	int i;
 
-	pool = kmalloc(sizeof(struct zbud_pool), gfp);
+	if (gfp & __GFP_HIGHMEM)
+		return NULL;
+	pool = kmalloc(sizeof(struct zbud_pool), GFP_KERNEL);
 	if (!pool)
 		return NULL;
 	spin_lock_init(&pool->lock);
@@ -214,6 +220,7 @@ struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops)
 	INIT_LIST_HEAD(&pool->lru);
 	pool->pages_nr = 0;
 	pool->ops = ops;
+	pool->gfp = gfp;
 	return pool;
 }
 
@@ -232,7 +239,6 @@ void zbud_destroy_pool(struct zbud_pool *pool)
  * zbud_alloc() - allocates a region of a given size
  * @pool:	zbud pool from which to allocate
  * @size:	size in bytes of the desired allocation
- * @gfp:	gfp flags used if the pool needs to grow
  * @handle:	handle of the new allocation
  *
  * This function will attempt to find a free region in the pool large enough to
@@ -240,22 +246,18 @@ void zbud_destroy_pool(struct zbud_pool *pool)
  * performed first. If no suitable free region is found, then a new page is
  * allocated and added to the pool to satisfy the request.
  *
- * gfp should not set __GFP_HIGHMEM as highmem pages cannot be used
- * as zbud pool pages.
- *
- * Return: 0 if success and handle is set, otherwise -EINVAL if the size or
- * gfp arguments are invalid or -ENOMEM if the pool was unable to allocate
- * a new page.
+ * Return: 0 if success and @handle is set, -ENOSPC if the @size is too large,
+ * -EINVAL if the @size is 0 or less, or -ENOMEM if the pool was unable to
+ * allocate a new page.
  */
-int zbud_alloc(struct zbud_pool *pool, int size, gfp_t gfp,
-			unsigned long *handle)
+int zbud_alloc(struct zbud_pool *pool, int size, unsigned long *handle)
 {
 	int chunks, i, freechunks;
 	struct zbud_header *zhdr = NULL;
 	enum buddy bud;
 	struct page *page;
 
-	if (size <= 0 || gfp & __GFP_HIGHMEM)
+	if (size <= 0)
 		return -EINVAL;
 	if (size > PAGE_SIZE - ZHDR_SIZE_ALIGNED - CHUNK_SIZE)
 		return -ENOSPC;
@@ -279,7 +281,7 @@ int zbud_alloc(struct zbud_pool *pool, int size, gfp_t gfp,
 
 	/* Couldn't find unbuddied zbud page, create new one */
 	spin_unlock(&pool->lock);
-	page = alloc_page(gfp);
+	page = alloc_page(pool->gfp);
 	if (!page)
 		return -ENOMEM;
 	spin_lock(&pool->lock);
diff --git a/mm/zswap.c b/mm/zswap.c
index aeaef0f..1cc6770 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -679,8 +679,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 
 	/* store */
 	len = dlen + sizeof(struct zswap_header);
-	ret = zbud_alloc(zswap_pool, len, __GFP_NORETRY | __GFP_NOWARN,
-		&handle);
+	ret = zbud_alloc(zswap_pool, len, &handle);
 	if (ret == -ENOSPC) {
 		zswap_reject_compress_poor++;
 		goto freepage;
@@ -900,7 +899,8 @@ static int __init init_zswap(void)
 
 	pr_info("loading zswap\n");
 
-	zswap_pool = zbud_create_pool(GFP_KERNEL, &zswap_zbud_ops);
+	zswap_pool = zbud_create_pool(__GFP_NORETRY | __GFP_NOWARN,
+			&zswap_zbud_ops);
 	if (!zswap_pool) {
 		pr_err("zbud pool creation failed\n");
 		goto error;
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 2/4] mm: zpool: implement zsmalloc shrinking
  2014-04-19 15:52 [PATCH 0/4] mm: zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
  2014-04-19 15:52 ` [PATCH 1/4] mm: zpool: zbud_alloc() minor param change Dan Streetman
@ 2014-04-19 15:52 ` Dan Streetman
  2014-04-26  8:37   ` Weijie Yang
  2014-04-19 15:52 ` [PATCH 3/4] mm: zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 65+ messages in thread
From: Dan Streetman @ 2014-04-19 15:52 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Weijie Yang, Johannes Weiner, Sergey Senozhatsky,
	Linux-MM, linux-kernel

Add zs_shrink() and helper functions to zsmalloc.  Update zsmalloc
zs_create_pool() creation function to include ops param that provides
an evict() function for use during shrinking.  Update helper function
fix_fullness_group() to always reinsert changed zspages even if the
fullness group did not change, so they are updated in the fullness
group lru.  Also update zram to use the new zsmalloc pool creation
function but pass NULL as the ops param, since zram does not use
pool shrinking.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>

---

To find all the used objects inside a zspage, I had to do a full scan
of the zspage->freelist for each object, since there's no list of used
objects, and no way to keep a list of used objects without allocating
more memory for each zspage (that I could see).  Of course, by taking
a byte (or really only a bit) out of each object's memory area to use
as a flag, we could just check that instead of scanning ->freelist
for each zspage object, but that would (slightly) reduce the available
size of each zspage object.


 drivers/block/zram/zram_drv.c |   2 +-
 include/linux/zsmalloc.h      |   7 +-
 mm/zsmalloc.c                 | 168 ++++++++++++++++++++++++++++++++++++++----
 3 files changed, 160 insertions(+), 17 deletions(-)

diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 9849b52..dacf343 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -249,7 +249,7 @@ static struct zram_meta *zram_meta_alloc(u64 disksize)
 		goto free_meta;
 	}
 
-	meta->mem_pool = zs_create_pool(GFP_NOIO | __GFP_HIGHMEM);
+	meta->mem_pool = zs_create_pool(GFP_NOIO | __GFP_HIGHMEM, NULL);
 	if (!meta->mem_pool) {
 		pr_err("Error creating memory pool\n");
 		goto free_table;
diff --git a/include/linux/zsmalloc.h b/include/linux/zsmalloc.h
index e44d634..a75ab6e 100644
--- a/include/linux/zsmalloc.h
+++ b/include/linux/zsmalloc.h
@@ -36,11 +36,16 @@ enum zs_mapmode {
 
 struct zs_pool;
 
-struct zs_pool *zs_create_pool(gfp_t flags);
+struct zs_ops {
+	int (*evict)(struct zs_pool *pool, unsigned long handle);
+};
+
+struct zs_pool *zs_create_pool(gfp_t flags, struct zs_ops *ops);
 void zs_destroy_pool(struct zs_pool *pool);
 
 unsigned long zs_malloc(struct zs_pool *pool, size_t size);
 void zs_free(struct zs_pool *pool, unsigned long obj);
+int zs_shrink(struct zs_pool *pool, size_t size);
 
 void *zs_map_object(struct zs_pool *pool, unsigned long handle,
 			enum zs_mapmode mm);
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index 36b4591..b99bec0 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -219,6 +219,8 @@ struct zs_pool {
 	struct size_class size_class[ZS_SIZE_CLASSES];
 
 	gfp_t flags;	/* allocation flags used when growing pool */
+
+	struct zs_ops *ops;
 };
 
 /*
@@ -389,16 +391,14 @@ static enum fullness_group fix_fullness_group(struct zs_pool *pool,
 	BUG_ON(!is_first_page(page));
 
 	get_zspage_mapping(page, &class_idx, &currfg);
-	newfg = get_fullness_group(page);
-	if (newfg == currfg)
-		goto out;
-
 	class = &pool->size_class[class_idx];
+	newfg = get_fullness_group(page);
+	/* Need to do this even if currfg == newfg, to update lru */
 	remove_zspage(page, class, currfg);
 	insert_zspage(page, class, newfg);
-	set_zspage_mapping(page, class_idx, newfg);
+	if (currfg != newfg)
+		set_zspage_mapping(page, class_idx, newfg);
 
-out:
 	return newfg;
 }
 
@@ -438,6 +438,36 @@ static int get_pages_per_zspage(int class_size)
 }
 
 /*
+ * To determine which class to use when shrinking, we find the
+ * first zspage class that is greater than the requested shrink
+ * size, and has at least one zspage.  This returns the class
+ * with the class lock held, or NULL.
+ */
+static struct size_class *get_class_to_shrink(struct zs_pool *pool,
+			size_t size)
+{
+	struct size_class *class;
+	int i;
+	bool in_use, large_enough;
+
+	for (i = 0; i <= ZS_SIZE_CLASSES; i++) {
+		class = &pool->size_class[i];
+
+		spin_lock(&class->lock);
+
+		in_use = class->pages_allocated > 0;
+		large_enough = class->pages_per_zspage * PAGE_SIZE >= size;
+
+		if (in_use && large_enough)
+			return class;
+
+		spin_unlock(&class->lock);
+	}
+
+	return NULL;
+}
+
+/*
  * A single 'zspage' is composed of many system pages which are
  * linked together using fields in struct page. This function finds
  * the first/head page, given any component page of a zspage.
@@ -508,6 +538,48 @@ static unsigned long obj_idx_to_offset(struct page *page,
 	return off + obj_idx * class_size;
 }
 
+static bool obj_handle_is_free(struct page *first_page,
+			struct size_class *class, unsigned long handle)
+{
+	unsigned long obj, idx, offset;
+	struct page *page;
+	struct link_free *link;
+
+	BUG_ON(!is_first_page(first_page));
+
+	obj = (unsigned long)first_page->freelist;
+
+	while (obj) {
+		if (obj == handle)
+			return true;
+
+		obj_handle_to_location(obj, &page, &idx);
+		offset = obj_idx_to_offset(page, idx, class->size);
+
+		link = (struct link_free *)kmap_atomic(page) +
+					offset / sizeof(*link);
+		obj = (unsigned long)link->next;
+		kunmap_atomic(link);
+	}
+
+	return false;
+}
+
+static void obj_free(unsigned long obj, struct page *page, unsigned long offset)
+{
+	struct page *first_page = get_first_page(page);
+	struct link_free *link;
+
+	/* Insert this object in containing zspage's freelist */
+	link = (struct link_free *)((unsigned char *)kmap_atomic(page)
+							+ offset);
+	link->next = first_page->freelist;
+	kunmap_atomic(link);
+	first_page->freelist = (void *)obj;
+
+	first_page->inuse--;
+}
+
 static void reset_page(struct page *page)
 {
 	clear_bit(PG_private, &page->flags);
@@ -651,6 +723,39 @@ cleanup:
 	return first_page;
 }
 
+static int reclaim_zspage(struct zs_pool *pool, struct size_class *class,
+			struct page *first_page)
+{
+	struct page *page = first_page;
+	unsigned long offset = 0, handle, idx, objs;
+	int ret = 0;
+
+	BUG_ON(!is_first_page(page));
+	BUG_ON(!pool->ops);
+	BUG_ON(!pool->ops->evict);
+
+	while (page) {
+		objs = DIV_ROUND_UP(PAGE_SIZE - offset, class->size);
+
+		for (idx = 0; idx < objs; idx++) {
+			handle = (unsigned long)obj_location_to_handle(page,
+									idx);
+			if (!obj_handle_is_free(first_page, class, handle))
+				ret = pool->ops->evict(pool, handle);
+			if (ret)
+				return ret;
+			else
+				obj_free(handle, page, offset);
+		}
+
+		page = get_next_page(page);
+		if (page)
+			offset = page->index;
+	}
+
+	return 0;
+}
+
 static struct page *find_get_zspage(struct size_class *class)
 {
 	int i;
@@ -856,7 +961,7 @@ fail:
  * 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(gfp_t flags, struct zs_ops *ops)
 {
 	int i, ovhd_size;
 	struct zs_pool *pool;
@@ -883,6 +988,7 @@ struct zs_pool *zs_create_pool(gfp_t flags)
 	}
 
 	pool->flags = flags;
+	pool->ops = ops;
 
 	return pool;
 }
@@ -968,7 +1074,6 @@ EXPORT_SYMBOL_GPL(zs_malloc);
 
 void zs_free(struct zs_pool *pool, unsigned long obj)
 {
-	struct link_free *link;
 	struct page *first_page, *f_page;
 	unsigned long f_objidx, f_offset;
 
@@ -988,14 +1093,8 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
 
 	spin_lock(&class->lock);
 
-	/* Insert this object in containing zspage's freelist */
-	link = (struct link_free *)((unsigned char *)kmap_atomic(f_page)
-							+ f_offset);
-	link->next = first_page->freelist;
-	kunmap_atomic(link);
-	first_page->freelist = (void *)obj;
+	obj_free(obj, f_page, f_offset);
 
-	first_page->inuse--;
 	fullness = fix_fullness_group(pool, first_page);
 
 	if (fullness == ZS_EMPTY)
@@ -1008,6 +1107,45 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
 }
 EXPORT_SYMBOL_GPL(zs_free);
 
+int zs_shrink(struct zs_pool *pool, size_t size)
+{
+	struct size_class *class;
+	struct page *first_page;
+	enum fullness_group fullness;
+	int ret;
+
+	if (!pool->ops || !pool->ops->evict)
+		return -EINVAL;
+
+	/* the class is returned locked */
+	class = get_class_to_shrink(pool, size);
+	if (!class)
+		return -ENOENT;
+
+	first_page = find_get_zspage(class);
+	if (!first_page) {
+		spin_unlock(&class->lock);
+		return -ENOENT;
+	}
+
+	ret = reclaim_zspage(pool, class, first_page);
+
+	if (ret) {
+		fullness = fix_fullness_group(pool, first_page);
+
+		if (fullness == ZS_EMPTY)
+			class->pages_allocated -= class->pages_per_zspage;
+	}
+
+	spin_unlock(&class->lock);
+
+	if (!ret || fullness == ZS_EMPTY)
+		free_zspage(first_page);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(zs_shrink);
+
 /**
  * zs_map_object - get address of allocated object from handle.
  * @pool: pool from which the object was allocated
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 3/4] mm: zpool: implement common zpool api to zbud/zsmalloc
  2014-04-19 15:52 [PATCH 0/4] mm: zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
  2014-04-19 15:52 ` [PATCH 1/4] mm: zpool: zbud_alloc() minor param change Dan Streetman
  2014-04-19 15:52 ` [PATCH 2/4] mm: zpool: implement zsmalloc shrinking Dan Streetman
@ 2014-04-19 15:52 ` Dan Streetman
  2014-04-22 10:05   ` Sergey Senozhatsky
  2014-04-19 15:52 ` [PATCH 4/4] mm: zpool: update zswap to use zpool Dan Streetman
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 65+ messages in thread
From: Dan Streetman @ 2014-04-19 15:52 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Weijie Yang, Johannes Weiner, Sergey Senozhatsky,
	Linux-MM, linux-kernel

Add zpool api.

zpool provides an interface for memory storage, typically of compressed
memory.  Users can select what backend to use; currently the only
implementations are zbud, a low density implementation with exactly
two compressed pages per storage page, and zsmalloc, a higher density
implementation with multiple compressed pages per storage page.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
---
 include/linux/zpool.h | 166 ++++++++++++++++++++++
 mm/Kconfig            |  43 +++---
 mm/Makefile           |   1 +
 mm/zpool.c            | 380 ++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 572 insertions(+), 18 deletions(-)
 create mode 100644 include/linux/zpool.h
 create mode 100644 mm/zpool.c

diff --git a/include/linux/zpool.h b/include/linux/zpool.h
new file mode 100644
index 0000000..82d81c6
--- /dev/null
+++ b/include/linux/zpool.h
@@ -0,0 +1,166 @@
+/*
+ * zpool memory storage api
+ *
+ * Copyright (C) 2014 Dan Streetman
+ *
+ * This is a common frontend for the zbud and zsmalloc memory
+ * storage pool implementations.  Typically, this is used to
+ * store compressed memory.
+ */
+
+#ifndef _ZPOOL_H_
+#define _ZPOOL_H_
+
+struct zpool;
+
+struct zpool_ops {
+	int (*evict)(struct zpool *pool, unsigned long handle);
+};
+
+#define ZPOOL_TYPE_ZSMALLOC "zsmalloc"
+#define ZPOOL_TYPE_ZBUD "zbud"
+
+/*
+ * Control how a handle is mapped.  It will be ignored if the
+ * implementation does not support it.  Its use is optional.
+ * Note that this does not refer to memory protection, it
+ * refers to how the memory will be copied in/out if copying
+ * is necessary during mapping; read-write is the safest as
+ * it copies the existing memory in on map, and copies the
+ * changed memory back out on unmap.  Write-only does not copy
+ * in the memory and should only be used for initialization.
+ * If in doubt, use ZPOOL_MM_DEFAULT which is read-write.
+ */
+enum zpool_mapmode {
+	ZPOOL_MM_RW, /* normal read-write mapping */
+	ZPOOL_MM_RO, /* read-only (no copy-out at unmap time) */
+	ZPOOL_MM_WO, /* write-only (no copy-in at map time) */
+
+	ZPOOL_MM_DEFAULT = ZPOOL_MM_RW
+};
+
+/**
+ * zpool_create_pool() - Create a new zpool
+ * @type	The type of the zpool to create (e.g. zbud, zsmalloc)
+ * @flags	What GFP flags should be used when the zpool allocates memory.
+ * @ops		The optional ops callback.
+ * @fallback	If other implementations should be used
+ *
+ * This creates a new zpool of the specified type.  The zpool will use the
+ * given flags when allocating any memory.  If the ops param is NULL, then
+ * the created zpool will not be shrinkable.
+ *
+ * If creation of the implementation @type fails, and @fallback is true,
+ * then other implementation(s) are tried.  If @fallback is false or no
+ * implementations could be created, then NULL is returned.
+ *
+ * Returns: New zpool on success, NULL on failure.
+ */
+struct zpool *zpool_create_pool(char *type, gfp_t flags,
+			struct zpool_ops *ops, bool fallback);
+
+/**
+ * zpool_get_type() - Get the type of the zpool
+ * @pool	The zpool to check
+ *
+ * This returns the type of the pool, which will match one of the
+ * ZPOOL_TYPE_* defined values.  This can be useful after calling
+ * zpool_create_pool() with @fallback set to true.
+ *
+ * Returns: The type of zpool.
+ */
+char *zpool_get_type(struct zpool *pool);
+
+/**
+ * zpool_destroy_pool() - Destroy a zpool
+ * @pool	The zpool to destroy.
+ *
+ * This destroys an existing zpool.  The zpool should not be in use.
+ */
+void zpool_destroy_pool(struct zpool *pool);
+
+/**
+ * zpool_malloc() - Allocate memory
+ * @pool	The zpool to allocate from.
+ * @size	The amount of memory to allocate.
+ * @handle	Pointer to the handle to set
+ *
+ * This allocates the requested amount of memory from the pool.
+ * The provided @handle will be set to the allocated object handle.
+ *
+ * Returns: 0 on success, negative value on error.
+ */
+int zpool_malloc(struct zpool *pool, size_t size, unsigned long *handle);
+
+/**
+ * zpool_free() - Free previously allocated memory
+ * @pool	The zpool that allocated the memory.
+ * @handle	The handle to the memory to free.
+ *
+ * This frees previously allocated memory.  This does not guarantee
+ * that the pool will actually free memory, only that the memory
+ * in the pool will become available for use by the pool.
+ */
+void zpool_free(struct zpool *pool, unsigned long handle);
+
+/**
+ * zpool_shrink() - Shrink the pool size
+ * @pool	The zpool to shrink.
+ * @size	The minimum amount to shrink the pool.
+ *
+ * This attempts to shrink the actual memory size of the pool
+ * by evicting currently used handle(s).  If the pool was
+ * created with no zpool_ops, or the evict call fails for any
+ * of the handles, this will fail.
+ *
+ * Returns: 0 on success, negative value on error/failure.
+ */
+int zpool_shrink(struct zpool *pool, size_t size);
+
+/**
+ * zpool_map_handle() - Map a previously allocated handle into memory
+ * @pool	The zpool that the handle was allocated from
+ * @handle	The handle to map
+ * @mm	How the memory should be mapped
+ *
+ * This maps a previously allocated handle into memory.  The @mm
+ * param indicates to the implemenation how the memory will be
+ * used, i.e. read-only, write-only, read-write.  If the
+ * implementation does not support it, the memory will be treated
+ * as read-write.
+ *
+ * This may hold locks, disable interrupts, and/or preemption,
+ * and the zpool_unmap_handle() must be called to undo those
+ * actions.  The code that uses the mapped handle should complete
+ * its operatons on the mapped handle memory quickly and unmap
+ * as soon as possible.  Multiple handles should not be mapped
+ * concurrently on a cpu.
+ *
+ * Returns: A pointer to the handle's mapped memory area.
+ */
+void *zpool_map_handle(struct zpool *pool, unsigned long handle,
+			enum zpool_mapmode mm);
+
+/**
+ * zpool_unmap_handle() - Unmap a previously mapped handle
+ * @pool	The zpool that the handle was allocated from
+ * @handle	The handle to unmap
+ *
+ * This unmaps a previously mapped handle.  Any locks or other
+ * actions that the implemenation took in zpool_map_handle()
+ * will be undone here.  The memory area returned from
+ * zpool_map_handle() should no longer be used after this.
+ */
+void zpool_unmap_handle(struct zpool *pool, unsigned long handle);
+
+/**
+ * zpool_get_total_size() - The total size of the pool
+ * @pool	The zpool to check
+ *
+ * This returns the total size in bytes of the pool.
+ *
+ * Returns: Total size of the zpool in bytes.
+ */
+u64 zpool_get_total_size(struct zpool *pool);
+
+#endif
diff --git a/mm/Kconfig b/mm/Kconfig
index ebe5880..ed7715c 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -512,21 +512,23 @@ config CMA_DEBUG
 	  processing calls such as dma_alloc_from_contiguous().
 	  This option does not affect warning and error messages.
 
-config ZBUD
-	tristate
-	default n
+config MEM_SOFT_DIRTY
+	bool "Track memory changes"
+	depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
+	select PROC_PAGE_MONITOR
 	help
-	  A special purpose allocator for storing compressed pages.
-	  It is designed to store up to two compressed pages per physical
-	  page.  While this design limits storage density, it has simple and
-	  deterministic reclaim properties that make it preferable to a higher
-	  density approach when reclaim will be used.
+	  This option enables memory changes tracking by introducing a
+	  soft-dirty bit on pte-s. This bit it set when someone writes
+	  into a page just as regular dirty bit, but unlike the latter
+	  it can be cleared by hands.
+
+	  See Documentation/vm/soft-dirty.txt for more details.
 
 config ZSWAP
 	bool "Compressed cache for swap pages (EXPERIMENTAL)"
 	depends on FRONTSWAP && CRYPTO=y
 	select CRYPTO_LZO
-	select ZBUD
+	select ZPOOL
 	default n
 	help
 	  A lightweight compressed cache for swap pages.  It takes
@@ -542,17 +544,22 @@ config ZSWAP
 	  they have not be fully explored on the large set of potential
 	  configurations and workloads that exist.
 
-config MEM_SOFT_DIRTY
-	bool "Track memory changes"
-	depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
-	select PROC_PAGE_MONITOR
+config ZPOOL
+	tristate "Common API for compressed memory storage"
+	default n
 	help
-	  This option enables memory changes tracking by introducing a
-	  soft-dirty bit on pte-s. This bit it set when someone writes
-	  into a page just as regular dirty bit, but unlike the latter
-	  it can be cleared by hands.
+	  Compressed memory storage API.  This allows using either zbud or
+	  zsmalloc.
 
-	  See Documentation/vm/soft-dirty.txt for more details.
+config ZBUD
+	tristate "Low density storage for compressed pages"
+	default n
+	help
+	  A special purpose allocator for storing compressed pages.
+	  It is designed to store up to two compressed pages per physical
+	  page.  While this design limits storage density, it has simple and
+	  deterministic reclaim properties that make it preferable to a higher
+	  density approach when reclaim will be used.
 
 config ZSMALLOC
 	bool "Memory allocator for compressed pages"
diff --git a/mm/Makefile b/mm/Makefile
index 60cacbb..4135f7c 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -60,6 +60,7 @@ obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
 obj-$(CONFIG_CLEANCACHE) += cleancache.o
 obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
 obj-$(CONFIG_PAGE_OWNER) += pageowner.o
+obj-$(CONFIG_ZPOOL)	+= zpool.o
 obj-$(CONFIG_ZBUD)	+= zbud.o
 obj-$(CONFIG_ZSMALLOC)	+= zsmalloc.o
 obj-$(CONFIG_GENERIC_EARLY_IOREMAP) += early_ioremap.o
diff --git a/mm/zpool.c b/mm/zpool.c
new file mode 100644
index 0000000..592cc0d
--- /dev/null
+++ b/mm/zpool.c
@@ -0,0 +1,380 @@
+/*
+ * zpool memory storage api
+ *
+ * Copyright (C) 2014 Dan Streetman
+ *
+ * This is a common frontend for the zbud and zsmalloc memory
+ * storage pool implementations.  Typically, this is used to
+ * store compressed memory.
+ */
+
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/zpool.h>
+#include <linux/zbud.h>
+#include <linux/zsmalloc.h>
+
+struct zpool_imp {
+	void (*destroy)(struct zpool *pool);
+
+	int (*malloc)(struct zpool *pool, size_t size, unsigned long *handle);
+	void (*free)(struct zpool *pool, unsigned long handle);
+
+	int (*shrink)(struct zpool *pool, size_t size);
+
+	void *(*map)(struct zpool *pool, unsigned long handle,
+				enum zpool_mapmode mm);
+	void (*unmap)(struct zpool *pool, unsigned long handle);
+
+	u64 (*total_size)(struct zpool *pool);
+};
+
+struct zpool {
+	char *type;
+
+	union {
+#ifdef CONFIG_ZSMALLOC
+	struct zs_pool *zsmalloc_pool;
+#endif
+#ifdef CONFIG_ZBUD
+	struct zbud_pool *zbud_pool;
+#endif
+	};
+
+	struct zpool_imp *imp;
+	struct zpool_ops *ops;
+
+	struct list_head list;
+};
+
+static LIST_HEAD(zpools);
+static DEFINE_SPINLOCK(zpools_lock);
+
+static int zpool_noop_evict(struct zpool *pool, unsigned long handle)
+{
+	return -EINVAL;
+}
+static struct zpool_ops zpool_noop_ops = {
+	.evict = zpool_noop_evict
+};
+
+
+/* zsmalloc */
+
+#ifdef CONFIG_ZSMALLOC
+
+static void zpool_zsmalloc_destroy(struct zpool *zpool)
+{
+	spin_lock(&zpools_lock);
+	list_del(&zpool->list);
+	spin_unlock(&zpools_lock);
+
+	zs_destroy_pool(zpool->zsmalloc_pool);
+	kfree(zpool);
+}
+
+static int zpool_zsmalloc_malloc(struct zpool *pool, size_t size,
+			unsigned long *handle)
+{
+	*handle = zs_malloc(pool->zsmalloc_pool, size);
+	return *handle ? 0 : -1;
+}
+
+static void zpool_zsmalloc_free(struct zpool *pool, unsigned long handle)
+{
+	zs_free(pool->zsmalloc_pool, handle);
+}
+
+static int zpool_zsmalloc_shrink(struct zpool *pool, size_t size)
+{
+	return zs_shrink(pool->zsmalloc_pool, size);
+}
+
+static void *zpool_zsmalloc_map(struct zpool *pool, unsigned long handle,
+			enum zpool_mapmode zpool_mapmode)
+{
+	enum zs_mapmode zs_mapmode;
+
+	switch (zpool_mapmode) {
+	case ZPOOL_MM_RO:
+		zs_mapmode = ZS_MM_RO; break;
+	case ZPOOL_MM_WO:
+		zs_mapmode = ZS_MM_WO; break;
+	case ZPOOL_MM_RW: /* fallthrough */
+	default:
+		zs_mapmode = ZS_MM_RW; break;
+	}
+	return zs_map_object(pool->zsmalloc_pool, handle, zs_mapmode);
+}
+
+static void zpool_zsmalloc_unmap(struct zpool *pool, unsigned long handle)
+{
+	zs_unmap_object(pool->zsmalloc_pool, handle);
+}
+
+static u64 zpool_zsmalloc_total_size(struct zpool *pool)
+{
+	return zs_get_total_size_bytes(pool->zsmalloc_pool);
+}
+
+static int zpool_zsmalloc_evict(struct zs_pool *zsmalloc_pool,
+			unsigned long handle)
+{
+	struct zpool *zpool;
+
+	spin_lock(&zpools_lock);
+	list_for_each_entry(zpool, &zpools, list) {
+		if (zpool->zsmalloc_pool == zsmalloc_pool) {
+			spin_unlock(&zpools_lock);
+			return zpool->ops->evict(zpool, handle);
+		}
+	}
+	spin_unlock(&zpools_lock);
+	return -EINVAL;
+}
+
+static struct zpool_imp zpool_zsmalloc_imp = {
+	.destroy = zpool_zsmalloc_destroy,
+	.malloc = zpool_zsmalloc_malloc,
+	.free = zpool_zsmalloc_free,
+	.shrink = zpool_zsmalloc_shrink,
+	.map = zpool_zsmalloc_map,
+	.unmap = zpool_zsmalloc_unmap,
+	.total_size = zpool_zsmalloc_total_size
+};
+
+static struct zs_ops zpool_zsmalloc_ops = {
+	.evict = zpool_zsmalloc_evict
+};
+
+static struct zpool *zpool_zsmalloc_create(gfp_t flags, struct zpool_ops *ops)
+{
+	struct zpool *zpool;
+	struct zs_ops *zs_ops = (ops ? &zpool_zsmalloc_ops : NULL);
+
+	zpool = kmalloc(sizeof(*zpool), GFP_KERNEL);
+	if (!zpool)
+		return NULL;
+
+	zpool->zsmalloc_pool = zs_create_pool(flags, zs_ops);
+	if (!zpool->zsmalloc_pool) {
+		kfree(zpool);
+		return NULL;
+	}
+
+	zpool->type = ZPOOL_TYPE_ZSMALLOC;
+	zpool->imp = &zpool_zsmalloc_imp;
+	zpool->ops = (ops ? ops : &zpool_noop_ops);
+	spin_lock(&zpools_lock);
+	list_add(&zpool->list, &zpools);
+	spin_unlock(&zpools_lock);
+
+	return zpool;
+}
+
+#else
+
+static struct zpool *zpool_zsmalloc_create(gfp_t flags, struct zpool_ops *ops)
+{
+	pr_info("zpool: no zsmalloc in this kernel\n");
+	return NULL;
+}
+
+#endif /* CONFIG_ZSMALLOC */
+
+
+/* zbud */
+
+#ifdef CONFIG_ZBUD
+
+static void zpool_zbud_destroy(struct zpool *zpool)
+{
+	spin_lock(&zpools_lock);
+	list_del(&zpool->list);
+	spin_unlock(&zpools_lock);
+
+	zbud_destroy_pool(zpool->zbud_pool);
+	kfree(zpool);
+}
+
+static int zpool_zbud_malloc(struct zpool *pool, size_t size,
+			unsigned long *handle)
+{
+	return zbud_alloc(pool->zbud_pool, size, handle);
+}
+
+static void zpool_zbud_free(struct zpool *pool, unsigned long handle)
+{
+	zbud_free(pool->zbud_pool, handle);
+}
+
+static int zpool_zbud_shrink(struct zpool *pool, size_t size)
+{
+	return zbud_reclaim_page(pool->zbud_pool, 3);
+}
+
+static void *zpool_zbud_map(struct zpool *pool, unsigned long handle,
+			enum zpool_mapmode zpool_mapmode)
+{
+	return zbud_map(pool->zbud_pool, handle);
+}
+
+static void zpool_zbud_unmap(struct zpool *pool, unsigned long handle)
+{
+	zbud_unmap(pool->zbud_pool, handle);
+}
+
+static u64 zpool_zbud_total_size(struct zpool *pool)
+{
+	return zbud_get_pool_size(pool->zbud_pool) * PAGE_SIZE;
+}
+
+static int zpool_zbud_evict(struct zbud_pool *zbud_pool, unsigned long handle)
+{
+	struct zpool *zpool;
+
+	spin_lock(&zpools_lock);
+	list_for_each_entry(zpool, &zpools, list) {
+		if (zpool->zbud_pool == zbud_pool) {
+			spin_unlock(&zpools_lock);
+			return zpool->ops->evict(zpool, handle);
+		}
+	}
+	spin_unlock(&zpools_lock);
+	return -EINVAL;
+}
+
+static struct zpool_imp zpool_zbud_imp = {
+	.destroy = zpool_zbud_destroy,
+	.malloc = zpool_zbud_malloc,
+	.free = zpool_zbud_free,
+	.shrink = zpool_zbud_shrink,
+	.map = zpool_zbud_map,
+	.unmap = zpool_zbud_unmap,
+	.total_size = zpool_zbud_total_size
+};
+
+static struct zbud_ops zpool_zbud_ops = {
+	.evict = zpool_zbud_evict
+};
+
+static struct zpool *zpool_zbud_create(gfp_t flags, struct zpool_ops *ops)
+{
+	struct zpool *zpool;
+	struct zbud_ops *zbud_ops = (ops ? &zpool_zbud_ops : NULL);
+
+	zpool = kmalloc(sizeof(*zpool), GFP_KERNEL);
+	if (!zpool)
+		return NULL;
+
+	zpool->zbud_pool = zbud_create_pool(flags, zbud_ops);
+	if (!zpool->zbud_pool) {
+		kfree(zpool);
+		return NULL;
+	}
+
+	zpool->type = ZPOOL_TYPE_ZBUD;
+	zpool->imp = &zpool_zbud_imp;
+	zpool->ops = (ops ? ops : &zpool_noop_ops);
+	spin_lock(&zpools_lock);
+	list_add(&zpool->list, &zpools);
+	spin_unlock(&zpools_lock);
+
+	return zpool;
+}
+
+#else
+
+static struct zpool *zpool_zbud_create(gfp_t flags, struct zpool_ops *ops)
+{
+	pr_info("zpool: no zbud in this kernel\n");
+	return NULL;
+}
+
+#endif /* CONFIG_ZBUD */
+
+
+struct zpool *zpool_fallback_create(gfp_t flags, struct zpool_ops *ops)
+{
+	struct zpool *pool = NULL;
+
+#ifdef CONFIG_ZSMALLOC
+	pool = zpool_zsmalloc_create(flags, ops);
+	if (pool)
+		return pool;
+	pr_info("zpool: fallback unable to create zsmalloc pool\n");
+#endif
+
+#ifdef CONFIG_ZBUD
+	pool = zpool_zbud_create(flags, ops);
+	if (!pool)
+		pr_info("zpool: fallback unable to create zbud pool\n");
+#endif
+
+	return pool;
+}
+
+struct zpool *zpool_create_pool(char *type, gfp_t flags,
+			struct zpool_ops *ops, bool fallback)
+{
+	struct zpool *pool = NULL;
+
+	if (!strcmp(type, ZPOOL_TYPE_ZSMALLOC))
+		pool = zpool_zsmalloc_create(flags, ops);
+	else if (!strcmp(type, ZPOOL_TYPE_ZBUD))
+		pool = zpool_zbud_create(flags, ops);
+	else
+		pr_err("zpool: unknown type %s\n", type);
+
+	if (!pool && fallback)
+		pool = zpool_fallback_create(flags, ops);
+
+	if (!pool)
+		pr_err("zpool: couldn't create zpool\n");
+
+	return pool;
+}
+
+char *zpool_get_type(struct zpool *pool)
+{
+	return pool->type;
+}
+
+void zpool_destroy_pool(struct zpool *pool)
+{
+	pool->imp->destroy(pool);
+}
+
+int zpool_malloc(struct zpool *pool, size_t size, unsigned long *handle)
+{
+	return pool->imp->malloc(pool, size, handle);
+}
+
+void zpool_free(struct zpool *pool, unsigned long handle)
+{
+	pool->imp->free(pool, handle);
+}
+
+int zpool_shrink(struct zpool *pool, size_t size)
+{
+	return pool->imp->shrink(pool, size);
+}
+
+void *zpool_map_handle(struct zpool *pool, unsigned long handle,
+			enum zpool_mapmode mapmode)
+{
+	return pool->imp->map(pool, handle, mapmode);
+}
+
+void zpool_unmap_handle(struct zpool *pool, unsigned long handle)
+{
+	pool->imp->unmap(pool, handle);
+}
+
+u64 zpool_get_total_size(struct zpool *pool)
+{
+	return pool->imp->total_size(pool);
+}
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 4/4] mm: zpool: update zswap to use zpool
  2014-04-19 15:52 [PATCH 0/4] mm: zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
                   ` (2 preceding siblings ...)
  2014-04-19 15:52 ` [PATCH 3/4] mm: zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
@ 2014-04-19 15:52 ` Dan Streetman
  2014-04-21  2:47 ` [PATCH 0/4] mm: zpool: add common api for zswap to use zbud/zsmalloc Weijie Yang
  2014-05-07 21:51 ` [PATCHv2 0/4] mm/zpool: " Dan Streetman
  5 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-04-19 15:52 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Weijie Yang, Johannes Weiner, Sergey Senozhatsky,
	Linux-MM, linux-kernel

Change zswap to use the zpool api instead of directly using zbud.
Add a boot-time param to allow selecting which zpool implementation
to use, with zbud as the default.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
---
 mm/zswap.c | 70 ++++++++++++++++++++++++++++++++++----------------------------
 1 file changed, 39 insertions(+), 31 deletions(-)

diff --git a/mm/zswap.c b/mm/zswap.c
index 1cc6770..4f4a8ec 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -34,7 +34,7 @@
 #include <linux/swap.h>
 #include <linux/crypto.h>
 #include <linux/mempool.h>
-#include <linux/zbud.h>
+#include <linux/zpool.h>
 
 #include <linux/mm_types.h>
 #include <linux/page-flags.h>
@@ -45,8 +45,8 @@
 /*********************************
 * statistics
 **********************************/
-/* Number of memory pages used by the compressed pool */
-static u64 zswap_pool_pages;
+/* Total bytes used by the compressed storage */
+static u64 zswap_pool_total_size;
 /* The number of compressed pages currently stored in zswap */
 static atomic_t zswap_stored_pages = ATOMIC_INIT(0);
 
@@ -89,8 +89,13 @@ static unsigned int zswap_max_pool_percent = 20;
 module_param_named(max_pool_percent,
 			zswap_max_pool_percent, uint, 0644);
 
-/* zbud_pool is shared by all of zswap backend  */
-static struct zbud_pool *zswap_pool;
+/* Compressed storage to use */
+#define ZSWAP_ZPOOL_DEFAULT ZPOOL_TYPE_ZBUD
+static char *zswap_zpool_type = ZSWAP_ZPOOL_DEFAULT;
+module_param_named(zpool, zswap_zpool_type, charp, 0444);
+
+/* zpool is shared by all of zswap backend  */
+static struct zpool *zswap_pool;
 
 /*********************************
 * compression functions
@@ -168,7 +173,7 @@ static void zswap_comp_exit(void)
  *            be held while changing the refcount.  Since the lock must
  *            be held, there is no reason to also make refcount atomic.
  * offset - the swap offset for the entry.  Index into the red-black tree.
- * handle - zbud allocation handle that stores the compressed page data
+ * handle - zpool allocation handle that stores the compressed page data
  * length - the length in bytes of the compressed page data.  Needed during
  *          decompression
  */
@@ -284,15 +289,15 @@ static void zswap_rb_erase(struct rb_root *root, struct zswap_entry *entry)
 }
 
 /*
- * Carries out the common pattern of freeing and entry's zbud allocation,
+ * Carries out the common pattern of freeing and entry's zpool allocation,
  * freeing the entry itself, and decrementing the number of stored pages.
  */
 static void zswap_free_entry(struct zswap_entry *entry)
 {
-	zbud_free(zswap_pool, entry->handle);
+	zpool_free(zswap_pool, entry->handle);
 	zswap_entry_cache_free(entry);
 	atomic_dec(&zswap_stored_pages);
-	zswap_pool_pages = zbud_get_pool_size(zswap_pool);
+	zswap_pool_total_size = zpool_get_total_size(zswap_pool);
 }
 
 /* caller must hold the tree lock */
@@ -409,7 +414,7 @@ cleanup:
 static bool zswap_is_full(void)
 {
 	return totalram_pages * zswap_max_pool_percent / 100 <
-		zswap_pool_pages;
+		DIV_ROUND_UP(zswap_pool_total_size, PAGE_SIZE);
 }
 
 /*********************************
@@ -525,7 +530,7 @@ static int zswap_get_swap_cache_page(swp_entry_t entry,
  * the swap cache, the compressed version stored by zswap can be
  * freed.
  */
-static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle)
+static int zswap_writeback_entry(struct zpool *pool, unsigned long handle)
 {
 	struct zswap_header *zhdr;
 	swp_entry_t swpentry;
@@ -541,9 +546,9 @@ static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle)
 	};
 
 	/* extract swpentry from data */
-	zhdr = zbud_map(pool, handle);
+	zhdr = zpool_map_handle(pool, handle, ZPOOL_MM_RO);
 	swpentry = zhdr->swpentry; /* here */
-	zbud_unmap(pool, handle);
+	zpool_unmap_handle(pool, handle);
 	tree = zswap_trees[swp_type(swpentry)];
 	offset = swp_offset(swpentry);
 
@@ -573,13 +578,13 @@ static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle)
 	case ZSWAP_SWAPCACHE_NEW: /* page is locked */
 		/* decompress */
 		dlen = PAGE_SIZE;
-		src = (u8 *)zbud_map(zswap_pool, entry->handle) +
-			sizeof(struct zswap_header);
+		src = (u8 *)zpool_map_handle(zswap_pool, entry->handle,
+				ZPOOL_MM_RO) + sizeof(struct zswap_header);
 		dst = kmap_atomic(page);
 		ret = zswap_comp_op(ZSWAP_COMPOP_DECOMPRESS, src,
 				entry->length, dst, &dlen);
 		kunmap_atomic(dst);
-		zbud_unmap(zswap_pool, entry->handle);
+		zpool_unmap_handle(zswap_pool, entry->handle);
 		BUG_ON(ret);
 		BUG_ON(dlen != PAGE_SIZE);
 
@@ -652,7 +657,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 	/* reclaim space if needed */
 	if (zswap_is_full()) {
 		zswap_pool_limit_hit++;
-		if (zbud_reclaim_page(zswap_pool, 8)) {
+		if (zpool_shrink(zswap_pool, PAGE_SIZE)) {
 			zswap_reject_reclaim_fail++;
 			ret = -ENOMEM;
 			goto reject;
@@ -679,7 +684,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 
 	/* store */
 	len = dlen + sizeof(struct zswap_header);
-	ret = zbud_alloc(zswap_pool, len, &handle);
+	ret = zpool_malloc(zswap_pool, len, &handle);
 	if (ret == -ENOSPC) {
 		zswap_reject_compress_poor++;
 		goto freepage;
@@ -688,11 +693,11 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 		zswap_reject_alloc_fail++;
 		goto freepage;
 	}
-	zhdr = zbud_map(zswap_pool, handle);
+	zhdr = zpool_map_handle(zswap_pool, handle, ZPOOL_MM_RW);
 	zhdr->swpentry = swp_entry(type, offset);
 	buf = (u8 *)(zhdr + 1);
 	memcpy(buf, dst, dlen);
-	zbud_unmap(zswap_pool, handle);
+	zpool_unmap_handle(zswap_pool, handle);
 	put_cpu_var(zswap_dstmem);
 
 	/* populate entry */
@@ -715,7 +720,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 
 	/* update stats */
 	atomic_inc(&zswap_stored_pages);
-	zswap_pool_pages = zbud_get_pool_size(zswap_pool);
+	zswap_pool_total_size = zpool_get_total_size(zswap_pool);
 
 	return 0;
 
@@ -751,13 +756,13 @@ static int zswap_frontswap_load(unsigned type, pgoff_t offset,
 
 	/* decompress */
 	dlen = PAGE_SIZE;
-	src = (u8 *)zbud_map(zswap_pool, entry->handle) +
-			sizeof(struct zswap_header);
+	src = (u8 *)zpool_map_handle(zswap_pool, entry->handle,
+			ZPOOL_MM_RO) + sizeof(struct zswap_header);
 	dst = kmap_atomic(page);
 	ret = zswap_comp_op(ZSWAP_COMPOP_DECOMPRESS, src, entry->length,
 		dst, &dlen);
 	kunmap_atomic(dst);
-	zbud_unmap(zswap_pool, entry->handle);
+	zpool_unmap_handle(zswap_pool, entry->handle);
 	BUG_ON(ret);
 
 	spin_lock(&tree->lock);
@@ -810,7 +815,7 @@ static void zswap_frontswap_invalidate_area(unsigned type)
 	zswap_trees[type] = NULL;
 }
 
-static struct zbud_ops zswap_zbud_ops = {
+static struct zpool_ops zswap_zpool_ops = {
 	.evict = zswap_writeback_entry
 };
 
@@ -868,8 +873,8 @@ static int __init zswap_debugfs_init(void)
 			zswap_debugfs_root, &zswap_written_back_pages);
 	debugfs_create_u64("duplicate_entry", S_IRUGO,
 			zswap_debugfs_root, &zswap_duplicate_entry);
-	debugfs_create_u64("pool_pages", S_IRUGO,
-			zswap_debugfs_root, &zswap_pool_pages);
+	debugfs_create_u64("pool_total_size", S_IRUGO,
+			zswap_debugfs_root, &zswap_pool_total_size);
 	debugfs_create_atomic_t("stored_pages", S_IRUGO,
 			zswap_debugfs_root, &zswap_stored_pages);
 
@@ -899,12 +904,15 @@ static int __init init_zswap(void)
 
 	pr_info("loading zswap\n");
 
-	zswap_pool = zbud_create_pool(__GFP_NORETRY | __GFP_NOWARN,
-			&zswap_zbud_ops);
+	zswap_pool = zpool_create_pool(zswap_zpool_type,
+			__GFP_NORETRY | __GFP_NOWARN, &zswap_zpool_ops, true);
 	if (!zswap_pool) {
-		pr_err("zbud pool creation failed\n");
+		pr_err("zpool creation failed\n");
 		goto error;
 	}
+	if (strcmp(zswap_zpool_type, zpool_get_type(zswap_pool)))
+		pr_info("zpool gave us fallback implementation: %s\n",
+				zpool_get_type(zswap_pool));
 
 	if (zswap_entry_cache_create()) {
 		pr_err("entry cache creation failed\n");
@@ -928,7 +936,7 @@ pcpufail:
 compfail:
 	zswap_entry_cache_destory();
 cachefail:
-	zbud_destroy_pool(zswap_pool);
+	zpool_destroy_pool(zswap_pool);
 error:
 	return -ENOMEM;
 }
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* RE: [PATCH 0/4] mm: zpool: add common api for zswap to use zbud/zsmalloc
  2014-04-19 15:52 [PATCH 0/4] mm: zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
                   ` (3 preceding siblings ...)
  2014-04-19 15:52 ` [PATCH 4/4] mm: zpool: update zswap to use zpool Dan Streetman
@ 2014-04-21  2:47 ` Weijie Yang
  2014-05-07 21:51 ` [PATCHv2 0/4] mm/zpool: " Dan Streetman
  5 siblings, 0 replies; 65+ messages in thread
From: Weijie Yang @ 2014-04-21  2:47 UTC (permalink / raw)
  To: 'Dan Streetman', 'Seth Jennings',
	'Minchan Kim', 'Nitin Gupta'
  Cc: 'Andrew Morton', 'Bob Liu',
	'Hugh Dickins', 'Mel Gorman',
	'Rik van Riel', 'Johannes Weiner',
	'Sergey Senozhatsky', 'Linux-MM',
	'linux-kernel'



On Sat, Apr 19, 2014 at 11:52 PM, Dan Streetman <ddstreet@ieee.org> wrote:
> In order to allow zswap users to choose between zbud and zsmalloc for
> the compressed storage pool, this patch set adds a new api "zpool" that
> provides an interface to both zbud and zsmalloc.  Only a minor change
> to zbud's interface was needed, as detailed in the first patch;
> zsmalloc required shrinking to be added and a minor interface change,
> as detailed in the second patch.
> 
> I believe Seth originally was using zsmalloc for swap, but there were
> concerns about how significant the impact of shrinking zsmalloc would
> be when zswap had to start reclaiming pages.  That still may be an
> issue, but this at least allows users to choose themselves whether
> they want a lower-density or higher-density compressed storage medium.
> At least for situations where zswap reclaim is never or rarely reached,
> it probably makes sense to use the higher density of zsmalloc.
> 
> Note this patch series does not change zram to use zpool, although that
> change should be possible as well.

I think this idea is acceptable, because for embedded devices reclaiming is
risky due to its write lifetime. By using zsmalloc, zswap user can not only take
the benefit of higher-density compressed storage but aslo supporting the
GFP_HIGHMEM in 32bit system.

I will pay attention to this patch set and give my opinion after my review.

Thanks for your work

> 
> Dan Streetman (4):
>   mm: zpool: zbud_alloc() minor param change
>   mm: zpool: implement zsmalloc shrinking
>   mm: zpool: implement common zpool api to zbud/zsmalloc
>   mm: zpool: update zswap to use zpool
> 
>  drivers/block/zram/zram_drv.c |   2 +-
>  include/linux/zbud.h          |   3 +-
>  include/linux/zpool.h         | 166 ++++++++++++++++++
>  include/linux/zsmalloc.h      |   7 +-
>  mm/Kconfig                    |  43 +++--
>  mm/Makefile                   |   1 +
>  mm/zbud.c                     |  28 ++--
>  mm/zpool.c                    | 380 ++++++++++++++++++++++++++++++++++++++++++
>  mm/zsmalloc.c                 | 168 +++++++++++++++++--
>  mm/zswap.c                    |  70 ++++----
>  10 files changed, 787 insertions(+), 81 deletions(-)
>  create mode 100644 include/linux/zpool.h
>  create mode 100644 mm/zpool.c
> 
> --
> 1.8.3.1


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 3/4] mm: zpool: implement common zpool api to zbud/zsmalloc
  2014-04-19 15:52 ` [PATCH 3/4] mm: zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
@ 2014-04-22 10:05   ` Sergey Senozhatsky
  2014-04-22 13:43     ` Dan Streetman
  0 siblings, 1 reply; 65+ messages in thread
From: Sergey Senozhatsky @ 2014-04-22 10:05 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Seth Jennings, Minchan Kim, Nitin Gupta, Andrew Morton, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Weijie Yang,
	Johannes Weiner, Sergey Senozhatsky, Linux-MM, linux-kernel

Hello,

On (04/19/14 11:52), Dan Streetman wrote:
> 
> Add zpool api.
> 
> zpool provides an interface for memory storage, typically of compressed
> memory.  Users can select what backend to use; currently the only
> implementations are zbud, a low density implementation with exactly
> two compressed pages per storage page, and zsmalloc, a higher density
> implementation with multiple compressed pages per storage page.
> 
> Signed-off-by: Dan Streetman <ddstreet@ieee.org>
> ---
>  include/linux/zpool.h | 166 ++++++++++++++++++++++
>  mm/Kconfig            |  43 +++---
>  mm/Makefile           |   1 +
>  mm/zpool.c            | 380 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 572 insertions(+), 18 deletions(-)
>  create mode 100644 include/linux/zpool.h
>  create mode 100644 mm/zpool.c
> 
> diff --git a/include/linux/zpool.h b/include/linux/zpool.h
> new file mode 100644
> index 0000000..82d81c6
> --- /dev/null
> +++ b/include/linux/zpool.h
> @@ -0,0 +1,166 @@
> +/*
> + * zpool memory storage api
> + *
> + * Copyright (C) 2014 Dan Streetman
> + *
> + * This is a common frontend for the zbud and zsmalloc memory
> + * storage pool implementations.  Typically, this is used to
> + * store compressed memory.
> + */
> +
> +#ifndef _ZPOOL_H_
> +#define _ZPOOL_H_
> +
> +struct zpool;
> +
> +struct zpool_ops {
> +	int (*evict)(struct zpool *pool, unsigned long handle);
> +};
> +
> +#define ZPOOL_TYPE_ZSMALLOC "zsmalloc"
> +#define ZPOOL_TYPE_ZBUD "zbud"
> +
> +/*
> + * Control how a handle is mapped.  It will be ignored if the
> + * implementation does not support it.  Its use is optional.
> + * Note that this does not refer to memory protection, it
> + * refers to how the memory will be copied in/out if copying
> + * is necessary during mapping; read-write is the safest as
> + * it copies the existing memory in on map, and copies the
> + * changed memory back out on unmap.  Write-only does not copy
> + * in the memory and should only be used for initialization.
> + * If in doubt, use ZPOOL_MM_DEFAULT which is read-write.
> + */
> +enum zpool_mapmode {
> +	ZPOOL_MM_RW, /* normal read-write mapping */
> +	ZPOOL_MM_RO, /* read-only (no copy-out at unmap time) */
> +	ZPOOL_MM_WO, /* write-only (no copy-in at map time) */
> +
> +	ZPOOL_MM_DEFAULT = ZPOOL_MM_RW
> +};
> +
> +/**
> + * zpool_create_pool() - Create a new zpool
> + * @type	The type of the zpool to create (e.g. zbud, zsmalloc)
> + * @flags	What GFP flags should be used when the zpool allocates memory.
> + * @ops		The optional ops callback.
> + * @fallback	If other implementations should be used
> + *
> + * This creates a new zpool of the specified type.  The zpool will use the
> + * given flags when allocating any memory.  If the ops param is NULL, then
> + * the created zpool will not be shrinkable.
> + *
> + * If creation of the implementation @type fails, and @fallback is true,
> + * then other implementation(s) are tried.  If @fallback is false or no
> + * implementations could be created, then NULL is returned.
> + *
> + * Returns: New zpool on success, NULL on failure.
> + */
> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
> +			struct zpool_ops *ops, bool fallback);
> +
> +/**
> + * zpool_get_type() - Get the type of the zpool
> + * @pool	The zpool to check
> + *
> + * This returns the type of the pool, which will match one of the
> + * ZPOOL_TYPE_* defined values.  This can be useful after calling
> + * zpool_create_pool() with @fallback set to true.
> + *
> + * Returns: The type of zpool.
> + */
> +char *zpool_get_type(struct zpool *pool);
> +
> +/**
> + * zpool_destroy_pool() - Destroy a zpool
> + * @pool	The zpool to destroy.
> + *
> + * This destroys an existing zpool.  The zpool should not be in use.
> + */
> +void zpool_destroy_pool(struct zpool *pool);
> +
> +/**
> + * zpool_malloc() - Allocate memory
> + * @pool	The zpool to allocate from.
> + * @size	The amount of memory to allocate.
> + * @handle	Pointer to the handle to set
> + *
> + * This allocates the requested amount of memory from the pool.
> + * The provided @handle will be set to the allocated object handle.
> + *
> + * Returns: 0 on success, negative value on error.
> + */
> +int zpool_malloc(struct zpool *pool, size_t size, unsigned long *handle);
> +
> +/**
> + * zpool_free() - Free previously allocated memory
> + * @pool	The zpool that allocated the memory.
> + * @handle	The handle to the memory to free.
> + *
> + * This frees previously allocated memory.  This does not guarantee
> + * that the pool will actually free memory, only that the memory
> + * in the pool will become available for use by the pool.
> + */
> +void zpool_free(struct zpool *pool, unsigned long handle);
> +
> +/**
> + * zpool_shrink() - Shrink the pool size
> + * @pool	The zpool to shrink.
> + * @size	The minimum amount to shrink the pool.
> + *
> + * This attempts to shrink the actual memory size of the pool
> + * by evicting currently used handle(s).  If the pool was
> + * created with no zpool_ops, or the evict call fails for any
> + * of the handles, this will fail.
> + *
> + * Returns: 0 on success, negative value on error/failure.
> + */
> +int zpool_shrink(struct zpool *pool, size_t size);
> +
> +/**
> + * zpool_map_handle() - Map a previously allocated handle into memory
> + * @pool	The zpool that the handle was allocated from
> + * @handle	The handle to map
> + * @mm	How the memory should be mapped
> + *
> + * This maps a previously allocated handle into memory.  The @mm
> + * param indicates to the implemenation how the memory will be
> + * used, i.e. read-only, write-only, read-write.  If the
> + * implementation does not support it, the memory will be treated
> + * as read-write.
> + *
> + * This may hold locks, disable interrupts, and/or preemption,
> + * and the zpool_unmap_handle() must be called to undo those
> + * actions.  The code that uses the mapped handle should complete
> + * its operatons on the mapped handle memory quickly and unmap
> + * as soon as possible.  Multiple handles should not be mapped
> + * concurrently on a cpu.
> + *
> + * Returns: A pointer to the handle's mapped memory area.
> + */
> +void *zpool_map_handle(struct zpool *pool, unsigned long handle,
> +			enum zpool_mapmode mm);
> +
> +/**
> + * zpool_unmap_handle() - Unmap a previously mapped handle
> + * @pool	The zpool that the handle was allocated from
> + * @handle	The handle to unmap
> + *
> + * This unmaps a previously mapped handle.  Any locks or other
> + * actions that the implemenation took in zpool_map_handle()
> + * will be undone here.  The memory area returned from
> + * zpool_map_handle() should no longer be used after this.
> + */
> +void zpool_unmap_handle(struct zpool *pool, unsigned long handle);
> +
> +/**
> + * zpool_get_total_size() - The total size of the pool
> + * @pool	The zpool to check
> + *
> + * This returns the total size in bytes of the pool.
> + *
> + * Returns: Total size of the zpool in bytes.
> + */
> +u64 zpool_get_total_size(struct zpool *pool);
> +
> +#endif
> diff --git a/mm/Kconfig b/mm/Kconfig
> index ebe5880..ed7715c 100644
> --- a/mm/Kconfig
> +++ b/mm/Kconfig
> @@ -512,21 +512,23 @@ config CMA_DEBUG
>  	  processing calls such as dma_alloc_from_contiguous().
>  	  This option does not affect warning and error messages.
>  
> -config ZBUD
> -	tristate
> -	default n
> +config MEM_SOFT_DIRTY
> +	bool "Track memory changes"
> +	depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
> +	select PROC_PAGE_MONITOR
>  	help
> -	  A special purpose allocator for storing compressed pages.
> -	  It is designed to store up to two compressed pages per physical
> -	  page.  While this design limits storage density, it has simple and
> -	  deterministic reclaim properties that make it preferable to a higher
> -	  density approach when reclaim will be used.
> +	  This option enables memory changes tracking by introducing a
> +	  soft-dirty bit on pte-s. This bit it set when someone writes
> +	  into a page just as regular dirty bit, but unlike the latter
> +	  it can be cleared by hands.
> +
> +	  See Documentation/vm/soft-dirty.txt for more details.
>  
>  config ZSWAP
>  	bool "Compressed cache for swap pages (EXPERIMENTAL)"
>  	depends on FRONTSWAP && CRYPTO=y
>  	select CRYPTO_LZO
> -	select ZBUD
> +	select ZPOOL
>  	default n
>  	help
>  	  A lightweight compressed cache for swap pages.  It takes
> @@ -542,17 +544,22 @@ config ZSWAP
>  	  they have not be fully explored on the large set of potential
>  	  configurations and workloads that exist.
>  
> -config MEM_SOFT_DIRTY
> -	bool "Track memory changes"
> -	depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
> -	select PROC_PAGE_MONITOR
> +config ZPOOL
> +	tristate "Common API for compressed memory storage"
> +	default n
>  	help
> -	  This option enables memory changes tracking by introducing a
> -	  soft-dirty bit on pte-s. This bit it set when someone writes
> -	  into a page just as regular dirty bit, but unlike the latter
> -	  it can be cleared by hands.
> +	  Compressed memory storage API.  This allows using either zbud or
> +	  zsmalloc.
>  
> -	  See Documentation/vm/soft-dirty.txt for more details.
> +config ZBUD
> +	tristate "Low density storage for compressed pages"
> +	default n
> +	help
> +	  A special purpose allocator for storing compressed pages.
> +	  It is designed to store up to two compressed pages per physical
> +	  page.  While this design limits storage density, it has simple and
> +	  deterministic reclaim properties that make it preferable to a higher
> +	  density approach when reclaim will be used.
>  
>  config ZSMALLOC
>  	bool "Memory allocator for compressed pages"
> diff --git a/mm/Makefile b/mm/Makefile
> index 60cacbb..4135f7c 100644
> --- a/mm/Makefile
> +++ b/mm/Makefile
> @@ -60,6 +60,7 @@ obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
>  obj-$(CONFIG_CLEANCACHE) += cleancache.o
>  obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
>  obj-$(CONFIG_PAGE_OWNER) += pageowner.o
> +obj-$(CONFIG_ZPOOL)	+= zpool.o

side note, this fails to apply on linux-next. mm/Makefile does not contain
CONFIG_PAGE_OWNER

https://git.kernel.org/cgit/linux/kernel/git/next/linux-next.git/tree/mm/Makefile?id=refs/tags/next-20140422

what tree this patchset is against of?

	-ss

>  obj-$(CONFIG_ZBUD)	+= zbud.o
>  obj-$(CONFIG_ZSMALLOC)	+= zsmalloc.o
>  obj-$(CONFIG_GENERIC_EARLY_IOREMAP) += early_ioremap.o
> diff --git a/mm/zpool.c b/mm/zpool.c
> new file mode 100644
> index 0000000..592cc0d
> --- /dev/null
> +++ b/mm/zpool.c
> @@ -0,0 +1,380 @@
> +/*
> + * zpool memory storage api
> + *
> + * Copyright (C) 2014 Dan Streetman
> + *
> + * This is a common frontend for the zbud and zsmalloc memory
> + * storage pool implementations.  Typically, this is used to
> + * store compressed memory.
> + */
> +
> +#include <linux/list.h>
> +#include <linux/types.h>
> +#include <linux/mm.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/zpool.h>
> +#include <linux/zbud.h>
> +#include <linux/zsmalloc.h>
> +
> +struct zpool_imp {
> +	void (*destroy)(struct zpool *pool);
> +
> +	int (*malloc)(struct zpool *pool, size_t size, unsigned long *handle);
> +	void (*free)(struct zpool *pool, unsigned long handle);
> +
> +	int (*shrink)(struct zpool *pool, size_t size);
> +
> +	void *(*map)(struct zpool *pool, unsigned long handle,
> +				enum zpool_mapmode mm);
> +	void (*unmap)(struct zpool *pool, unsigned long handle);
> +
> +	u64 (*total_size)(struct zpool *pool);
> +};
> +
> +struct zpool {
> +	char *type;
> +
> +	union {
> +#ifdef CONFIG_ZSMALLOC
> +	struct zs_pool *zsmalloc_pool;
> +#endif
> +#ifdef CONFIG_ZBUD
> +	struct zbud_pool *zbud_pool;
> +#endif
> +	};
> +
> +	struct zpool_imp *imp;
> +	struct zpool_ops *ops;
> +
> +	struct list_head list;
> +};
> +
> +static LIST_HEAD(zpools);
> +static DEFINE_SPINLOCK(zpools_lock);
> +
> +static int zpool_noop_evict(struct zpool *pool, unsigned long handle)
> +{
> +	return -EINVAL;
> +}
> +static struct zpool_ops zpool_noop_ops = {
> +	.evict = zpool_noop_evict
> +};
> +
> +
> +/* zsmalloc */
> +
> +#ifdef CONFIG_ZSMALLOC
> +
> +static void zpool_zsmalloc_destroy(struct zpool *zpool)
> +{
> +	spin_lock(&zpools_lock);
> +	list_del(&zpool->list);
> +	spin_unlock(&zpools_lock);
> +
> +	zs_destroy_pool(zpool->zsmalloc_pool);
> +	kfree(zpool);
> +}
> +
> +static int zpool_zsmalloc_malloc(struct zpool *pool, size_t size,
> +			unsigned long *handle)
> +{
> +	*handle = zs_malloc(pool->zsmalloc_pool, size);
> +	return *handle ? 0 : -1;
> +}
> +
> +static void zpool_zsmalloc_free(struct zpool *pool, unsigned long handle)
> +{
> +	zs_free(pool->zsmalloc_pool, handle);
> +}
> +
> +static int zpool_zsmalloc_shrink(struct zpool *pool, size_t size)
> +{
> +	return zs_shrink(pool->zsmalloc_pool, size);
> +}
> +
> +static void *zpool_zsmalloc_map(struct zpool *pool, unsigned long handle,
> +			enum zpool_mapmode zpool_mapmode)
> +{
> +	enum zs_mapmode zs_mapmode;
> +
> +	switch (zpool_mapmode) {
> +	case ZPOOL_MM_RO:
> +		zs_mapmode = ZS_MM_RO; break;
> +	case ZPOOL_MM_WO:
> +		zs_mapmode = ZS_MM_WO; break;
> +	case ZPOOL_MM_RW: /* fallthrough */
> +	default:
> +		zs_mapmode = ZS_MM_RW; break;
> +	}
> +	return zs_map_object(pool->zsmalloc_pool, handle, zs_mapmode);
> +}
> +
> +static void zpool_zsmalloc_unmap(struct zpool *pool, unsigned long handle)
> +{
> +	zs_unmap_object(pool->zsmalloc_pool, handle);
> +}
> +
> +static u64 zpool_zsmalloc_total_size(struct zpool *pool)
> +{
> +	return zs_get_total_size_bytes(pool->zsmalloc_pool);
> +}
> +
> +static int zpool_zsmalloc_evict(struct zs_pool *zsmalloc_pool,
> +			unsigned long handle)
> +{
> +	struct zpool *zpool;
> +
> +	spin_lock(&zpools_lock);
> +	list_for_each_entry(zpool, &zpools, list) {
> +		if (zpool->zsmalloc_pool == zsmalloc_pool) {
> +			spin_unlock(&zpools_lock);
> +			return zpool->ops->evict(zpool, handle);
> +		}
> +	}
> +	spin_unlock(&zpools_lock);
> +	return -EINVAL;
> +}
> +
> +static struct zpool_imp zpool_zsmalloc_imp = {
> +	.destroy = zpool_zsmalloc_destroy,
> +	.malloc = zpool_zsmalloc_malloc,
> +	.free = zpool_zsmalloc_free,
> +	.shrink = zpool_zsmalloc_shrink,
> +	.map = zpool_zsmalloc_map,
> +	.unmap = zpool_zsmalloc_unmap,
> +	.total_size = zpool_zsmalloc_total_size
> +};
> +
> +static struct zs_ops zpool_zsmalloc_ops = {
> +	.evict = zpool_zsmalloc_evict
> +};
> +
> +static struct zpool *zpool_zsmalloc_create(gfp_t flags, struct zpool_ops *ops)
> +{
> +	struct zpool *zpool;
> +	struct zs_ops *zs_ops = (ops ? &zpool_zsmalloc_ops : NULL);
> +
> +	zpool = kmalloc(sizeof(*zpool), GFP_KERNEL);
> +	if (!zpool)
> +		return NULL;
> +
> +	zpool->zsmalloc_pool = zs_create_pool(flags, zs_ops);
> +	if (!zpool->zsmalloc_pool) {
> +		kfree(zpool);
> +		return NULL;
> +	}
> +
> +	zpool->type = ZPOOL_TYPE_ZSMALLOC;
> +	zpool->imp = &zpool_zsmalloc_imp;
> +	zpool->ops = (ops ? ops : &zpool_noop_ops);
> +	spin_lock(&zpools_lock);
> +	list_add(&zpool->list, &zpools);
> +	spin_unlock(&zpools_lock);
> +
> +	return zpool;
> +}
> +
> +#else
> +
> +static struct zpool *zpool_zsmalloc_create(gfp_t flags, struct zpool_ops *ops)
> +{
> +	pr_info("zpool: no zsmalloc in this kernel\n");
> +	return NULL;
> +}
> +
> +#endif /* CONFIG_ZSMALLOC */
> +
> +
> +/* zbud */
> +
> +#ifdef CONFIG_ZBUD
> +
> +static void zpool_zbud_destroy(struct zpool *zpool)
> +{
> +	spin_lock(&zpools_lock);
> +	list_del(&zpool->list);
> +	spin_unlock(&zpools_lock);
> +
> +	zbud_destroy_pool(zpool->zbud_pool);
> +	kfree(zpool);
> +}
> +
> +static int zpool_zbud_malloc(struct zpool *pool, size_t size,
> +			unsigned long *handle)
> +{
> +	return zbud_alloc(pool->zbud_pool, size, handle);
> +}
> +
> +static void zpool_zbud_free(struct zpool *pool, unsigned long handle)
> +{
> +	zbud_free(pool->zbud_pool, handle);
> +}
> +
> +static int zpool_zbud_shrink(struct zpool *pool, size_t size)
> +{
> +	return zbud_reclaim_page(pool->zbud_pool, 3);
> +}
> +
> +static void *zpool_zbud_map(struct zpool *pool, unsigned long handle,
> +			enum zpool_mapmode zpool_mapmode)
> +{
> +	return zbud_map(pool->zbud_pool, handle);
> +}
> +
> +static void zpool_zbud_unmap(struct zpool *pool, unsigned long handle)
> +{
> +	zbud_unmap(pool->zbud_pool, handle);
> +}
> +
> +static u64 zpool_zbud_total_size(struct zpool *pool)
> +{
> +	return zbud_get_pool_size(pool->zbud_pool) * PAGE_SIZE;
> +}
> +
> +static int zpool_zbud_evict(struct zbud_pool *zbud_pool, unsigned long handle)
> +{
> +	struct zpool *zpool;
> +
> +	spin_lock(&zpools_lock);
> +	list_for_each_entry(zpool, &zpools, list) {
> +		if (zpool->zbud_pool == zbud_pool) {
> +			spin_unlock(&zpools_lock);
> +			return zpool->ops->evict(zpool, handle);
> +		}
> +	}
> +	spin_unlock(&zpools_lock);
> +	return -EINVAL;
> +}
> +
> +static struct zpool_imp zpool_zbud_imp = {
> +	.destroy = zpool_zbud_destroy,
> +	.malloc = zpool_zbud_malloc,
> +	.free = zpool_zbud_free,
> +	.shrink = zpool_zbud_shrink,
> +	.map = zpool_zbud_map,
> +	.unmap = zpool_zbud_unmap,
> +	.total_size = zpool_zbud_total_size
> +};
> +
> +static struct zbud_ops zpool_zbud_ops = {
> +	.evict = zpool_zbud_evict
> +};
> +
> +static struct zpool *zpool_zbud_create(gfp_t flags, struct zpool_ops *ops)
> +{
> +	struct zpool *zpool;
> +	struct zbud_ops *zbud_ops = (ops ? &zpool_zbud_ops : NULL);
> +
> +	zpool = kmalloc(sizeof(*zpool), GFP_KERNEL);
> +	if (!zpool)
> +		return NULL;
> +
> +	zpool->zbud_pool = zbud_create_pool(flags, zbud_ops);
> +	if (!zpool->zbud_pool) {
> +		kfree(zpool);
> +		return NULL;
> +	}
> +
> +	zpool->type = ZPOOL_TYPE_ZBUD;
> +	zpool->imp = &zpool_zbud_imp;
> +	zpool->ops = (ops ? ops : &zpool_noop_ops);
> +	spin_lock(&zpools_lock);
> +	list_add(&zpool->list, &zpools);
> +	spin_unlock(&zpools_lock);
> +
> +	return zpool;
> +}
> +
> +#else
> +
> +static struct zpool *zpool_zbud_create(gfp_t flags, struct zpool_ops *ops)
> +{
> +	pr_info("zpool: no zbud in this kernel\n");
> +	return NULL;
> +}
> +
> +#endif /* CONFIG_ZBUD */
> +
> +
> +struct zpool *zpool_fallback_create(gfp_t flags, struct zpool_ops *ops)
> +{
> +	struct zpool *pool = NULL;
> +
> +#ifdef CONFIG_ZSMALLOC
> +	pool = zpool_zsmalloc_create(flags, ops);
> +	if (pool)
> +		return pool;
> +	pr_info("zpool: fallback unable to create zsmalloc pool\n");
> +#endif
> +
> +#ifdef CONFIG_ZBUD
> +	pool = zpool_zbud_create(flags, ops);
> +	if (!pool)
> +		pr_info("zpool: fallback unable to create zbud pool\n");
> +#endif
> +
> +	return pool;
> +}
> +
> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
> +			struct zpool_ops *ops, bool fallback)
> +{
> +	struct zpool *pool = NULL;
> +
> +	if (!strcmp(type, ZPOOL_TYPE_ZSMALLOC))
> +		pool = zpool_zsmalloc_create(flags, ops);
> +	else if (!strcmp(type, ZPOOL_TYPE_ZBUD))
> +		pool = zpool_zbud_create(flags, ops);
> +	else
> +		pr_err("zpool: unknown type %s\n", type);
> +
> +	if (!pool && fallback)
> +		pool = zpool_fallback_create(flags, ops);
> +
> +	if (!pool)
> +		pr_err("zpool: couldn't create zpool\n");
> +
> +	return pool;
> +}
> +
> +char *zpool_get_type(struct zpool *pool)
> +{
> +	return pool->type;
> +}
> +
> +void zpool_destroy_pool(struct zpool *pool)
> +{
> +	pool->imp->destroy(pool);
> +}
> +
> +int zpool_malloc(struct zpool *pool, size_t size, unsigned long *handle)
> +{
> +	return pool->imp->malloc(pool, size, handle);
> +}
> +
> +void zpool_free(struct zpool *pool, unsigned long handle)
> +{
> +	pool->imp->free(pool, handle);
> +}
> +
> +int zpool_shrink(struct zpool *pool, size_t size)
> +{
> +	return pool->imp->shrink(pool, size);
> +}
> +
> +void *zpool_map_handle(struct zpool *pool, unsigned long handle,
> +			enum zpool_mapmode mapmode)
> +{
> +	return pool->imp->map(pool, handle, mapmode);
> +}
> +
> +void zpool_unmap_handle(struct zpool *pool, unsigned long handle)
> +{
> +	pool->imp->unmap(pool, handle);
> +}
> +
> +u64 zpool_get_total_size(struct zpool *pool)
> +{
> +	return pool->imp->total_size(pool);
> +}
> -- 
> 1.8.3.1
> 

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 3/4] mm: zpool: implement common zpool api to zbud/zsmalloc
  2014-04-22 10:05   ` Sergey Senozhatsky
@ 2014-04-22 13:43     ` Dan Streetman
  0 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-04-22 13:43 UTC (permalink / raw)
  To: Sergey Senozhatsky
  Cc: Seth Jennings, Minchan Kim, Nitin Gupta, Andrew Morton, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Weijie Yang,
	Johannes Weiner, Linux-MM, linux-kernel

On Tue, Apr 22, 2014 at 6:05 AM, Sergey Senozhatsky
<sergey.senozhatsky@gmail.com> wrote:
> Hello,
>> diff --git a/mm/Makefile b/mm/Makefile
>> index 60cacbb..4135f7c 100644
>> --- a/mm/Makefile
>> +++ b/mm/Makefile
>> @@ -60,6 +60,7 @@ obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
>>  obj-$(CONFIG_CLEANCACHE) += cleancache.o
>>  obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
>>  obj-$(CONFIG_PAGE_OWNER) += pageowner.o
>> +obj-$(CONFIG_ZPOOL)  += zpool.o
>
> side note, this fails to apply on linux-next. mm/Makefile does not contain
> CONFIG_PAGE_OWNER
>
> https://git.kernel.org/cgit/linux/kernel/git/next/linux-next.git/tree/mm/Makefile?id=refs/tags/next-20140422
>
> what tree this patchset is against of?

It's against this mmotm tree:
git://git.cmpxchg.org/linux-mmotm.git

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 2/4] mm: zpool: implement zsmalloc shrinking
  2014-04-19 15:52 ` [PATCH 2/4] mm: zpool: implement zsmalloc shrinking Dan Streetman
@ 2014-04-26  8:37   ` Weijie Yang
  2014-04-27  4:13     ` Dan Streetman
  2014-05-02 20:01     ` Seth Jennings
  0 siblings, 2 replies; 65+ messages in thread
From: Weijie Yang @ 2014-04-26  8:37 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Seth Jennings, Minchan Kim, Nitin Gupta, Andrew Morton, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Weijie Yang,
	Johannes Weiner, Sergey Senozhatsky, Linux-MM, linux-kernel

On Sat, Apr 19, 2014 at 11:52 PM, Dan Streetman <ddstreet@ieee.org> wrote:
> Add zs_shrink() and helper functions to zsmalloc.  Update zsmalloc
> zs_create_pool() creation function to include ops param that provides
> an evict() function for use during shrinking.  Update helper function
> fix_fullness_group() to always reinsert changed zspages even if the
> fullness group did not change, so they are updated in the fullness
> group lru.  Also update zram to use the new zsmalloc pool creation
> function but pass NULL as the ops param, since zram does not use
> pool shrinking.
>

I only review the code without test, however, I think this patch is
not acceptable.

The biggest problem is it will call zswap_writeback_entry() under lock,
zswap_writeback_entry() may sleep, so it is a bug. see below

The 3/4 patch has a lot of #ifdef, I don't think it's a good kind of
abstract way.

What about just disable zswap reclaim when using zsmalloc?
There is a long way to optimize writeback reclaim(both zswap and zram) ,
Maybe a small and simple step forward is better.

Regards,

> Signed-off-by: Dan Streetman <ddstreet@ieee.org>
>
> ---
>
> To find all the used objects inside a zspage, I had to do a full scan
> of the zspage->freelist for each object, since there's no list of used
> objects, and no way to keep a list of used objects without allocating
> more memory for each zspage (that I could see).  Of course, by taking
> a byte (or really only a bit) out of each object's memory area to use
> as a flag, we could just check that instead of scanning ->freelist
> for each zspage object, but that would (slightly) reduce the available
> size of each zspage object.
>
>
>  drivers/block/zram/zram_drv.c |   2 +-
>  include/linux/zsmalloc.h      |   7 +-
>  mm/zsmalloc.c                 | 168 ++++++++++++++++++++++++++++++++++++++----
>  3 files changed, 160 insertions(+), 17 deletions(-)
>
> diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
> index 9849b52..dacf343 100644
> --- a/drivers/block/zram/zram_drv.c
> +++ b/drivers/block/zram/zram_drv.c
> @@ -249,7 +249,7 @@ static struct zram_meta *zram_meta_alloc(u64 disksize)
>                 goto free_meta;
>         }
>
> -       meta->mem_pool = zs_create_pool(GFP_NOIO | __GFP_HIGHMEM);
> +       meta->mem_pool = zs_create_pool(GFP_NOIO | __GFP_HIGHMEM, NULL);
>         if (!meta->mem_pool) {
>                 pr_err("Error creating memory pool\n");
>                 goto free_table;
> diff --git a/include/linux/zsmalloc.h b/include/linux/zsmalloc.h
> index e44d634..a75ab6e 100644
> --- a/include/linux/zsmalloc.h
> +++ b/include/linux/zsmalloc.h
> @@ -36,11 +36,16 @@ enum zs_mapmode {
>
>  struct zs_pool;
>
> -struct zs_pool *zs_create_pool(gfp_t flags);
> +struct zs_ops {
> +       int (*evict)(struct zs_pool *pool, unsigned long handle);
> +};
> +
> +struct zs_pool *zs_create_pool(gfp_t flags, struct zs_ops *ops);
>  void zs_destroy_pool(struct zs_pool *pool);
>
>  unsigned long zs_malloc(struct zs_pool *pool, size_t size);
>  void zs_free(struct zs_pool *pool, unsigned long obj);
> +int zs_shrink(struct zs_pool *pool, size_t size);
>
>  void *zs_map_object(struct zs_pool *pool, unsigned long handle,
>                         enum zs_mapmode mm);
> diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
> index 36b4591..b99bec0 100644
> --- a/mm/zsmalloc.c
> +++ b/mm/zsmalloc.c
> @@ -219,6 +219,8 @@ struct zs_pool {
>         struct size_class size_class[ZS_SIZE_CLASSES];
>
>         gfp_t flags;    /* allocation flags used when growing pool */
> +
> +       struct zs_ops *ops;
>  };
>
>  /*
> @@ -389,16 +391,14 @@ static enum fullness_group fix_fullness_group(struct zs_pool *pool,
>         BUG_ON(!is_first_page(page));
>
>         get_zspage_mapping(page, &class_idx, &currfg);
> -       newfg = get_fullness_group(page);
> -       if (newfg == currfg)
> -               goto out;
> -
>         class = &pool->size_class[class_idx];
> +       newfg = get_fullness_group(page);
> +       /* Need to do this even if currfg == newfg, to update lru */
>         remove_zspage(page, class, currfg);
>         insert_zspage(page, class, newfg);
> -       set_zspage_mapping(page, class_idx, newfg);
> +       if (currfg != newfg)
> +               set_zspage_mapping(page, class_idx, newfg);
>
> -out:
>         return newfg;
>  }
>
> @@ -438,6 +438,36 @@ static int get_pages_per_zspage(int class_size)
>  }
>
>  /*
> + * To determine which class to use when shrinking, we find the
> + * first zspage class that is greater than the requested shrink
> + * size, and has at least one zspage.  This returns the class
> + * with the class lock held, or NULL.
> + */
> +static struct size_class *get_class_to_shrink(struct zs_pool *pool,
> +                       size_t size)
> +{
> +       struct size_class *class;
> +       int i;
> +       bool in_use, large_enough;
> +
> +       for (i = 0; i <= ZS_SIZE_CLASSES; i++) {
> +               class = &pool->size_class[i];
> +
> +               spin_lock(&class->lock);
> +
> +               in_use = class->pages_allocated > 0;
> +               large_enough = class->pages_per_zspage * PAGE_SIZE >= size;
> +
> +               if (in_use && large_enough)
> +                       return class;
> +
> +               spin_unlock(&class->lock);
> +       }
> +
> +       return NULL;
> +}
> +
> +/*
>   * A single 'zspage' is composed of many system pages which are
>   * linked together using fields in struct page. This function finds
>   * the first/head page, given any component page of a zspage.
> @@ -508,6 +538,48 @@ static unsigned long obj_idx_to_offset(struct page *page,
>         return off + obj_idx * class_size;
>  }
>
> +static bool obj_handle_is_free(struct page *first_page,
> +                       struct size_class *class, unsigned long handle)
> +{
> +       unsigned long obj, idx, offset;
> +       struct page *page;
> +       struct link_free *link;
> +
> +       BUG_ON(!is_first_page(first_page));
> +
> +       obj = (unsigned long)first_page->freelist;
> +
> +       while (obj) {
> +               if (obj == handle)
> +                       return true;
> +
> +               obj_handle_to_location(obj, &page, &idx);
> +               offset = obj_idx_to_offset(page, idx, class->size);
> +
> +               link = (struct link_free *)kmap_atomic(page) +
> +                                       offset / sizeof(*link);
> +               obj = (unsigned long)link->next;
> +               kunmap_atomic(link);
> +       }
> +
> +       return false;
> +}
> +
> +static void obj_free(unsigned long obj, struct page *page, unsigned long offset)
> +{
> +       struct page *first_page = get_first_page(page);
> +       struct link_free *link;
> +
> +       /* Insert this object in containing zspage's freelist */
> +       link = (struct link_free *)((unsigned char *)kmap_atomic(page)
> +                                                       + offset);
> +       link->next = first_page->freelist;
> +       kunmap_atomic(link);
> +       first_page->freelist = (void *)obj;
> +
> +       first_page->inuse--;
> +}
> +
>  static void reset_page(struct page *page)
>  {
>         clear_bit(PG_private, &page->flags);
> @@ -651,6 +723,39 @@ cleanup:
>         return first_page;
>  }
>
> +static int reclaim_zspage(struct zs_pool *pool, struct size_class *class,
> +                       struct page *first_page)
> +{
> +       struct page *page = first_page;
> +       unsigned long offset = 0, handle, idx, objs;
> +       int ret = 0;
> +
> +       BUG_ON(!is_first_page(page));
> +       BUG_ON(!pool->ops);
> +       BUG_ON(!pool->ops->evict);
> +
> +       while (page) {
> +               objs = DIV_ROUND_UP(PAGE_SIZE - offset, class->size);
> +
> +               for (idx = 0; idx < objs; idx++) {
> +                       handle = (unsigned long)obj_location_to_handle(page,
> +                                                                       idx);
> +                       if (!obj_handle_is_free(first_page, class, handle))
> +                               ret = pool->ops->evict(pool, handle);

call zswap_writeback_entry() under class->lock.

> +                       if (ret)
> +                               return ret;
> +                       else
> +                               obj_free(handle, page, offset);
> +               }
> +
> +               page = get_next_page(page);
> +               if (page)
> +                       offset = page->index;
> +       }
> +
> +       return 0;
> +}
> +
>  static struct page *find_get_zspage(struct size_class *class)
>  {
>         int i;
> @@ -856,7 +961,7 @@ fail:
>   * 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(gfp_t flags, struct zs_ops *ops)
>  {
>         int i, ovhd_size;
>         struct zs_pool *pool;
> @@ -883,6 +988,7 @@ struct zs_pool *zs_create_pool(gfp_t flags)
>         }
>
>         pool->flags = flags;
> +       pool->ops = ops;
>
>         return pool;
>  }
> @@ -968,7 +1074,6 @@ EXPORT_SYMBOL_GPL(zs_malloc);
>
>  void zs_free(struct zs_pool *pool, unsigned long obj)
>  {
> -       struct link_free *link;
>         struct page *first_page, *f_page;
>         unsigned long f_objidx, f_offset;
>
> @@ -988,14 +1093,8 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
>
>         spin_lock(&class->lock);
>
> -       /* Insert this object in containing zspage's freelist */
> -       link = (struct link_free *)((unsigned char *)kmap_atomic(f_page)
> -                                                       + f_offset);
> -       link->next = first_page->freelist;
> -       kunmap_atomic(link);
> -       first_page->freelist = (void *)obj;
> +       obj_free(obj, f_page, f_offset);
>
> -       first_page->inuse--;
>         fullness = fix_fullness_group(pool, first_page);
>
>         if (fullness == ZS_EMPTY)
> @@ -1008,6 +1107,45 @@ void zs_free(struct zs_pool *pool, unsigned long obj)
>  }
>  EXPORT_SYMBOL_GPL(zs_free);
>
> +int zs_shrink(struct zs_pool *pool, size_t size)
> +{
> +       struct size_class *class;
> +       struct page *first_page;
> +       enum fullness_group fullness;
> +       int ret;
> +
> +       if (!pool->ops || !pool->ops->evict)
> +               return -EINVAL;
> +
> +       /* the class is returned locked */
> +       class = get_class_to_shrink(pool, size);
> +       if (!class)
> +               return -ENOENT;
> +
> +       first_page = find_get_zspage(class);
> +       if (!first_page) {
> +               spin_unlock(&class->lock);
> +               return -ENOENT;
> +       }
> +
> +       ret = reclaim_zspage(pool, class, first_page);
> +
> +       if (ret) {
> +               fullness = fix_fullness_group(pool, first_page);
> +
> +               if (fullness == ZS_EMPTY)
> +                       class->pages_allocated -= class->pages_per_zspage;
> +       }
> +
> +       spin_unlock(&class->lock);
> +
> +       if (!ret || fullness == ZS_EMPTY)
> +               free_zspage(first_page);
> +
> +       return ret;
> +}
> +EXPORT_SYMBOL_GPL(zs_shrink);
> +
>  /**
>   * zs_map_object - get address of allocated object from handle.
>   * @pool: pool from which the object was allocated
> --
> 1.8.3.1
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 2/4] mm: zpool: implement zsmalloc shrinking
  2014-04-26  8:37   ` Weijie Yang
@ 2014-04-27  4:13     ` Dan Streetman
  2014-05-02 20:01     ` Seth Jennings
  1 sibling, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-04-27  4:13 UTC (permalink / raw)
  To: Weijie Yang
  Cc: Seth Jennings, Minchan Kim, Nitin Gupta, Andrew Morton, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Weijie Yang,
	Johannes Weiner, Sergey Senozhatsky, Linux-MM, linux-kernel

On Sat, Apr 26, 2014 at 4:37 AM, Weijie Yang <weijie.yang.kh@gmail.com> wrote:
> On Sat, Apr 19, 2014 at 11:52 PM, Dan Streetman <ddstreet@ieee.org> wrote:
>> Add zs_shrink() and helper functions to zsmalloc.  Update zsmalloc
>> zs_create_pool() creation function to include ops param that provides
>> an evict() function for use during shrinking.  Update helper function
>> fix_fullness_group() to always reinsert changed zspages even if the
>> fullness group did not change, so they are updated in the fullness
>> group lru.  Also update zram to use the new zsmalloc pool creation
>> function but pass NULL as the ops param, since zram does not use
>> pool shrinking.
>>
>
> I only review the code without test, however, I think this patch is
> not acceptable.
>
> The biggest problem is it will call zswap_writeback_entry() under lock,
> zswap_writeback_entry() may sleep, so it is a bug. see below

thanks for catching that!

>
> The 3/4 patch has a lot of #ifdef, I don't think it's a good kind of
> abstract way.

it has the #ifdef's because there's no point in compiling in code to
use zbud/zsmalloc if zbud/zsmalloc isn't compiled...what alternative
to #ifdef's would you suggest?  Or are there just specific #ifdefs you
suggest to remove?

>
> What about just disable zswap reclaim when using zsmalloc?
> There is a long way to optimize writeback reclaim(both zswap and zram) ,
> Maybe a small and simple step forward is better.

I think it's possible to just remove the zspage from the class while
under lock, then unlock and reclaim it.  As long as there's a
guarantee that zswap (or any zpool/zsmalloc reclaim user) doesn't
map/access the handle after evict() completes successfully, that
should work.  There does need to be some synchronization between
zs_free() and each handle's eviction though, similar to zbud's
under_reclaim flag.  I'll work on a v2 patch.

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 2/4] mm: zpool: implement zsmalloc shrinking
  2014-04-26  8:37   ` Weijie Yang
  2014-04-27  4:13     ` Dan Streetman
@ 2014-05-02 20:01     ` Seth Jennings
  2014-05-04 20:38       ` Dan Streetman
  1 sibling, 1 reply; 65+ messages in thread
From: Seth Jennings @ 2014-05-02 20:01 UTC (permalink / raw)
  To: Weijie Yang
  Cc: Dan Streetman, Minchan Kim, Nitin Gupta, Andrew Morton, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Weijie Yang,
	Johannes Weiner, Sergey Senozhatsky, Linux-MM, linux-kernel

On Sat, Apr 26, 2014 at 04:37:31PM +0800, Weijie Yang wrote:
> On Sat, Apr 19, 2014 at 11:52 PM, Dan Streetman <ddstreet@ieee.org> wrote:
> > Add zs_shrink() and helper functions to zsmalloc.  Update zsmalloc
> > zs_create_pool() creation function to include ops param that provides
> > an evict() function for use during shrinking.  Update helper function
> > fix_fullness_group() to always reinsert changed zspages even if the
> > fullness group did not change, so they are updated in the fullness
> > group lru.  Also update zram to use the new zsmalloc pool creation
> > function but pass NULL as the ops param, since zram does not use
> > pool shrinking.
> >
> 
> I only review the code without test, however, I think this patch is
> not acceptable.
> 
> The biggest problem is it will call zswap_writeback_entry() under lock,
> zswap_writeback_entry() may sleep, so it is a bug. see below
> 
> The 3/4 patch has a lot of #ifdef, I don't think it's a good kind of
> abstract way.
> 
> What about just disable zswap reclaim when using zsmalloc?

I agree here.  Making a generic allocator layer and zsmalloc reclaim
support should be two different efforts, since zsmalloc reclaim is
fraught with peril.

The generic layer can be done though, as long as you provide a way for
the backend to indicate that it doesn't support reclaim, which just
results in lru-inverse overflow to the swap device at the zswap layer.
Hopefully, if the user overrides the default to use zsmalloc, they
understand the implications and have sized their workload properly.

Also, the fallback logic shouldn't be in this generic layer.  It should
not be transparent to the user.  The user (in this case zswap) should
implement the fallback if they care to have it.  The generic allocator
layer makes it trivial for the user to implement.

Thanks,
Seth

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 2/4] mm: zpool: implement zsmalloc shrinking
  2014-05-02 20:01     ` Seth Jennings
@ 2014-05-04 20:38       ` Dan Streetman
  0 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-05-04 20:38 UTC (permalink / raw)
  To: Seth Jennings
  Cc: Weijie Yang, Minchan Kim, Nitin Gupta, Andrew Morton, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Weijie Yang,
	Johannes Weiner, Sergey Senozhatsky, Linux-MM, linux-kernel

On Fri, May 2, 2014 at 4:01 PM, Seth Jennings <sjennings@variantweb.net> wrote:
> On Sat, Apr 26, 2014 at 04:37:31PM +0800, Weijie Yang wrote:
>> On Sat, Apr 19, 2014 at 11:52 PM, Dan Streetman <ddstreet@ieee.org> wrote:
>> > Add zs_shrink() and helper functions to zsmalloc.  Update zsmalloc
>> > zs_create_pool() creation function to include ops param that provides
>> > an evict() function for use during shrinking.  Update helper function
>> > fix_fullness_group() to always reinsert changed zspages even if the
>> > fullness group did not change, so they are updated in the fullness
>> > group lru.  Also update zram to use the new zsmalloc pool creation
>> > function but pass NULL as the ops param, since zram does not use
>> > pool shrinking.
>> >
>>
>> I only review the code without test, however, I think this patch is
>> not acceptable.
>>
>> The biggest problem is it will call zswap_writeback_entry() under lock,
>> zswap_writeback_entry() may sleep, so it is a bug. see below
>>
>> The 3/4 patch has a lot of #ifdef, I don't think it's a good kind of
>> abstract way.
>>
>> What about just disable zswap reclaim when using zsmalloc?
>
> I agree here.  Making a generic allocator layer and zsmalloc reclaim
> support should be two different efforts, since zsmalloc reclaim is
> fraught with peril.

fair enough - I'm fairly sure it's doable with only minimal changes to
the current patch, but it's certainly true that there's no reason it
has to be done in the same patchset as the generic layer.

I'll remove it from the v2 patchset.

> The generic layer can be done though, as long as you provide a way for
> the backend to indicate that it doesn't support reclaim, which just
> results in lru-inverse overflow to the swap device at the zswap layer.
> Hopefully, if the user overrides the default to use zsmalloc, they
> understand the implications and have sized their workload properly.
>
> Also, the fallback logic shouldn't be in this generic layer.  It should
> not be transparent to the user.  The user (in this case zswap) should
> implement the fallback if they care to have it.  The generic allocator
> layer makes it trivial for the user to implement.

ok, makes sense, certainly when there's currently only 1 user and 2 backends ;-)

>
> Thanks,
> Seth

^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv2 0/4] mm/zpool: add common api for zswap to use zbud/zsmalloc
  2014-04-19 15:52 [PATCH 0/4] mm: zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
                   ` (4 preceding siblings ...)
  2014-04-21  2:47 ` [PATCH 0/4] mm: zpool: add common api for zswap to use zbud/zsmalloc Weijie Yang
@ 2014-05-07 21:51 ` Dan Streetman
  2014-05-07 21:51   ` [PATCHv2 1/4] mm/zbud: zbud_alloc() minor param change Dan Streetman
                     ` (4 more replies)
  5 siblings, 5 replies; 65+ messages in thread
From: Dan Streetman @ 2014-05-07 21:51 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

In order to allow zswap users to choose between zbud and zsmalloc for
the compressed storage pool, this patch set adds a new api "zpool" that
provides an interface to both zbud and zsmalloc.  Only a minor changes
to zbud's interface were needed, as detailed in the first two patches.
This does not implement zsmalloc shrinking (which will be submitted
separately), so when using zsmalloc as the pool type, zpool_shrink()
will always fail.

I believe Seth originally was using zsmalloc for swap, but there were
concerns about how significant the impact of shrinking zsmalloc would
be when zswap had to start reclaiming pages.  That still may be an
issue, but this at least allows users to choose themselves whether
they want a lower-density or higher-density compressed storage medium.
At least for situations where zswap reclaim is never or rarely reached,
it probably makes sense to use the higher density of zsmalloc.

Note this patch series does not change zram to use zpool, although that
change should be possible as well.

This patchset is against git://git.cmpxchg.org/linux-mmotm.git
commit a51cc1787cdef3f17536d6a6dc1edd0e7a85988f

Changes since v1 https://lkml.org/lkml/2014/4/19/97
 -remove zsmalloc shrinking
 -change zbud size param type from unsigned int to size_t
 -remove zpool fallback creation
 -zswap manually falls back to zbud if specified type fails

Dan Streetman (4):
  mm/zbud: zbud_alloc() minor param change
  mm/zbud: change zbud_alloc size type to size_t
  mm/zpool: implement common zpool api to zbud/zsmalloc
  mm/zswap: update zswap to use zpool

 include/linux/zbud.h  |   2 +-
 include/linux/zpool.h | 160 +++++++++++++++++++++++
 mm/Kconfig            |  43 ++++---
 mm/Makefile           |   1 +
 mm/zbud.c             |  30 +++--
 mm/zpool.c            | 349 ++++++++++++++++++++++++++++++++++++++++++++++++++
 mm/zswap.c            |  75 ++++++-----
 7 files changed, 596 insertions(+), 64 deletions(-)
 create mode 100644 include/linux/zpool.h
 create mode 100644 mm/zpool.c

-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv2 1/4] mm/zbud: zbud_alloc() minor param change
  2014-05-07 21:51 ` [PATCHv2 0/4] mm/zpool: " Dan Streetman
@ 2014-05-07 21:51   ` Dan Streetman
  2014-05-09  3:33     ` Seth Jennings
  2014-05-07 21:51   ` [PATCH 2/4] mm/zbud: change zbud_alloc size type to size_t Dan Streetman
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 65+ messages in thread
From: Dan Streetman @ 2014-05-07 21:51 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Change zbud to store gfp_t flags passed at pool creation to use for
each alloc; this allows the api to be closer to the existing zsmalloc
interface, and the only current zbud user (zswap) uses the same gfp
flags for all allocs.  Update zswap to use changed interface.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Cc: Seth Jennings <sjennings@variantweb.net>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

Changes since v1 https://lkml.org/lkml/2014/4/19/98
 -context changes only; zbud_alloc parameter type changed since
  last patch

 include/linux/zbud.h |  2 +-
 mm/zbud.c            | 27 +++++++++++++++------------
 mm/zswap.c           |  6 +++---
 3 files changed, 19 insertions(+), 16 deletions(-)

diff --git a/include/linux/zbud.h b/include/linux/zbud.h
index 13af0d4..0b2534e 100644
--- a/include/linux/zbud.h
+++ b/include/linux/zbud.h
@@ -11,7 +11,7 @@ struct zbud_ops {
 
 struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops);
 void zbud_destroy_pool(struct zbud_pool *pool);
-int zbud_alloc(struct zbud_pool *pool, unsigned int size, gfp_t gfp,
+int zbud_alloc(struct zbud_pool *pool, unsigned int size,
 	unsigned long *handle);
 void zbud_free(struct zbud_pool *pool, unsigned long handle);
 int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries);
diff --git a/mm/zbud.c b/mm/zbud.c
index 01df13a..847c01c 100644
--- a/mm/zbud.c
+++ b/mm/zbud.c
@@ -94,6 +94,7 @@ struct zbud_pool {
 	struct list_head lru;
 	u64 pages_nr;
 	struct zbud_ops *ops;
+	gfp_t gfp;
 };
 
 /*
@@ -193,9 +194,12 @@ static int num_free_chunks(struct zbud_header *zhdr)
 *****************/
 /**
  * zbud_create_pool() - create a new zbud pool
- * @gfp:	gfp flags when allocating the zbud pool structure
+ * @gfp:	gfp flags when growing the pool
  * @ops:	user-defined operations for the zbud pool
  *
+ * gfp should not set __GFP_HIGHMEM as highmem pages cannot be used
+ * as zbud pool pages.
+ *
  * Return: pointer to the new zbud pool or NULL if the metadata allocation
  * failed.
  */
@@ -204,7 +208,9 @@ struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops)
 	struct zbud_pool *pool;
 	int i;
 
-	pool = kmalloc(sizeof(struct zbud_pool), gfp);
+	if (gfp & __GFP_HIGHMEM)
+		return NULL;
+	pool = kmalloc(sizeof(struct zbud_pool), GFP_KERNEL);
 	if (!pool)
 		return NULL;
 	spin_lock_init(&pool->lock);
@@ -214,6 +220,7 @@ struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops)
 	INIT_LIST_HEAD(&pool->lru);
 	pool->pages_nr = 0;
 	pool->ops = ops;
+	pool->gfp = gfp;
 	return pool;
 }
 
@@ -232,7 +239,6 @@ void zbud_destroy_pool(struct zbud_pool *pool)
  * zbud_alloc() - allocates a region of a given size
  * @pool:	zbud pool from which to allocate
  * @size:	size in bytes of the desired allocation
- * @gfp:	gfp flags used if the pool needs to grow
  * @handle:	handle of the new allocation
  *
  * This function will attempt to find a free region in the pool large enough to
@@ -240,14 +246,11 @@ void zbud_destroy_pool(struct zbud_pool *pool)
  * performed first. If no suitable free region is found, then a new page is
  * allocated and added to the pool to satisfy the request.
  *
- * gfp should not set __GFP_HIGHMEM as highmem pages cannot be used
- * as zbud pool pages.
- *
- * Return: 0 if success and handle is set, otherwise -EINVAL if the size or
- * gfp arguments are invalid or -ENOMEM if the pool was unable to allocate
- * a new page.
+ * Return: 0 if success and @handle is set, -ENOSPC if the @size is too large,
+ * -EINVAL if the @size is 0, or -ENOMEM if the pool was unable to
+ * allocate a new page.
  */
-int zbud_alloc(struct zbud_pool *pool, unsigned int size, gfp_t gfp,
+int zbud_alloc(struct zbud_pool *pool, unsigned int size,
 			unsigned long *handle)
 {
 	int chunks, i, freechunks;
@@ -255,7 +258,7 @@ int zbud_alloc(struct zbud_pool *pool, unsigned int size, gfp_t gfp,
 	enum buddy bud;
 	struct page *page;
 
-	if (!size || (gfp & __GFP_HIGHMEM))
+	if (!size)
 		return -EINVAL;
 	if (size > PAGE_SIZE - ZHDR_SIZE_ALIGNED - CHUNK_SIZE)
 		return -ENOSPC;
@@ -279,7 +282,7 @@ int zbud_alloc(struct zbud_pool *pool, unsigned int size, gfp_t gfp,
 
 	/* Couldn't find unbuddied zbud page, create new one */
 	spin_unlock(&pool->lock);
-	page = alloc_page(gfp);
+	page = alloc_page(pool->gfp);
 	if (!page)
 		return -ENOMEM;
 	spin_lock(&pool->lock);
diff --git a/mm/zswap.c b/mm/zswap.c
index aeaef0f..1cc6770 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -679,8 +679,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 
 	/* store */
 	len = dlen + sizeof(struct zswap_header);
-	ret = zbud_alloc(zswap_pool, len, __GFP_NORETRY | __GFP_NOWARN,
-		&handle);
+	ret = zbud_alloc(zswap_pool, len, &handle);
 	if (ret == -ENOSPC) {
 		zswap_reject_compress_poor++;
 		goto freepage;
@@ -900,7 +899,8 @@ static int __init init_zswap(void)
 
 	pr_info("loading zswap\n");
 
-	zswap_pool = zbud_create_pool(GFP_KERNEL, &zswap_zbud_ops);
+	zswap_pool = zbud_create_pool(__GFP_NORETRY | __GFP_NOWARN,
+			&zswap_zbud_ops);
 	if (!zswap_pool) {
 		pr_err("zbud pool creation failed\n");
 		goto error;
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 2/4] mm/zbud: change zbud_alloc size type to size_t
  2014-05-07 21:51 ` [PATCHv2 0/4] mm/zpool: " Dan Streetman
  2014-05-07 21:51   ` [PATCHv2 1/4] mm/zbud: zbud_alloc() minor param change Dan Streetman
@ 2014-05-07 21:51   ` Dan Streetman
  2014-05-09  3:33     ` Seth Jennings
  2014-05-07 21:51   ` [PATCHv2 3/4] mm/zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
                     ` (2 subsequent siblings)
  4 siblings, 1 reply; 65+ messages in thread
From: Dan Streetman @ 2014-05-07 21:51 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Change the type of the zbud_alloc() size param from unsigned int
to size_t.

Technically, this should not make any difference, as the zbud
implementation already restricts the size to well within either
type's limits; but as zsmalloc (and kmalloc) use size_t, and
zpool will use size_t, this brings the size parameter type
in line with zsmalloc/zpool.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Cc: Seth Jennings <sjennings@variantweb.net>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

While the rest of the patches in this set are v2, this is new for
the set; previously a patch to implement zsmalloc shrinking was
here, but that's removed.  This patch instead changes the
zbud_alloc() size parameter type from unsigned int to size_t, to
be the same as the zsmalloc and zpool size param type.

 include/linux/zbud.h | 2 +-
 mm/zbud.c            | 5 ++---
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/include/linux/zbud.h b/include/linux/zbud.h
index 0b2534e..1e9cb57 100644
--- a/include/linux/zbud.h
+++ b/include/linux/zbud.h
@@ -11,7 +11,7 @@ struct zbud_ops {
 
 struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops);
 void zbud_destroy_pool(struct zbud_pool *pool);
-int zbud_alloc(struct zbud_pool *pool, unsigned int size,
+int zbud_alloc(struct zbud_pool *pool, size_t size,
 	unsigned long *handle);
 void zbud_free(struct zbud_pool *pool, unsigned long handle);
 int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries);
diff --git a/mm/zbud.c b/mm/zbud.c
index 847c01c..dd13665 100644
--- a/mm/zbud.c
+++ b/mm/zbud.c
@@ -123,7 +123,7 @@ enum buddy {
 };
 
 /* Converts an allocation size in bytes to size in zbud chunks */
-static int size_to_chunks(int size)
+static int size_to_chunks(size_t size)
 {
 	return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT;
 }
@@ -250,8 +250,7 @@ void zbud_destroy_pool(struct zbud_pool *pool)
  * -EINVAL if the @size is 0, or -ENOMEM if the pool was unable to
  * allocate a new page.
  */
-int zbud_alloc(struct zbud_pool *pool, unsigned int size,
-			unsigned long *handle)
+int zbud_alloc(struct zbud_pool *pool, size_t size, unsigned long *handle)
 {
 	int chunks, i, freechunks;
 	struct zbud_header *zhdr = NULL;
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCHv2 3/4] mm/zpool: implement common zpool api to zbud/zsmalloc
  2014-05-07 21:51 ` [PATCHv2 0/4] mm/zpool: " Dan Streetman
  2014-05-07 21:51   ` [PATCHv2 1/4] mm/zbud: zbud_alloc() minor param change Dan Streetman
  2014-05-07 21:51   ` [PATCH 2/4] mm/zbud: change zbud_alloc size type to size_t Dan Streetman
@ 2014-05-07 21:51   ` Dan Streetman
  2014-05-09  4:13     ` Seth Jennings
  2014-05-07 21:51   ` [PATCHv2 4/4] mm/zswap: update zswap to use zpool Dan Streetman
  2014-05-24 19:06   ` [PATCHv3 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
  4 siblings, 1 reply; 65+ messages in thread
From: Dan Streetman @ 2014-05-07 21:51 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Nitin Gupta, Weijie Yang
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Add zpool api.

zpool provides an interface for memory storage, typically of compressed
memory.  Users can select what backend to use; currently the only
implementations are zbud, a low density implementation with up to
two compressed pages per storage page, and zsmalloc, a higher density
implementation with multiple compressed pages per storage page.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Cc: Seth Jennings <sjennings@variantweb.net>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Nitin Gupta <ngupta@vflare.org>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

Changes since v1 https://lkml.org/lkml/2014/4/19/101
 -add some pr_info() during creation and pr_err() on errors
 -remove zpool code to call zs_shrink(), since zsmalloc shrinking
  was removed from this patchset
 -remove fallback; only specified pool type will be tried
 -pr_fmt() is defined in zpool to prefix zpool: in any pr_XXX() calls

 include/linux/zpool.h | 160 +++++++++++++++++++++++
 mm/Kconfig            |  43 ++++---
 mm/Makefile           |   1 +
 mm/zpool.c            | 349 ++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 535 insertions(+), 18 deletions(-)
 create mode 100644 include/linux/zpool.h
 create mode 100644 mm/zpool.c

diff --git a/include/linux/zpool.h b/include/linux/zpool.h
new file mode 100644
index 0000000..08f5468
--- /dev/null
+++ b/include/linux/zpool.h
@@ -0,0 +1,160 @@
+/*
+ * zpool memory storage api
+ *
+ * Copyright (C) 2014 Dan Streetman
+ *
+ * This is a common frontend for the zbud and zsmalloc memory
+ * storage pool implementations.  Typically, this is used to
+ * store compressed memory.
+ */
+
+#ifndef _ZPOOL_H_
+#define _ZPOOL_H_
+
+struct zpool;
+
+struct zpool_ops {
+	int (*evict)(struct zpool *pool, unsigned long handle);
+};
+
+#define ZPOOL_TYPE_ZSMALLOC "zsmalloc"
+#define ZPOOL_TYPE_ZBUD "zbud"
+
+/*
+ * Control how a handle is mapped.  It will be ignored if the
+ * implementation does not support it.  Its use is optional.
+ * Note that this does not refer to memory protection, it
+ * refers to how the memory will be copied in/out if copying
+ * is necessary during mapping; read-write is the safest as
+ * it copies the existing memory in on map, and copies the
+ * changed memory back out on unmap.  Write-only does not copy
+ * in the memory and should only be used for initialization.
+ * If in doubt, use ZPOOL_MM_DEFAULT which is read-write.
+ */
+enum zpool_mapmode {
+	ZPOOL_MM_RW, /* normal read-write mapping */
+	ZPOOL_MM_RO, /* read-only (no copy-out at unmap time) */
+	ZPOOL_MM_WO, /* write-only (no copy-in at map time) */
+
+	ZPOOL_MM_DEFAULT = ZPOOL_MM_RW
+};
+
+/**
+ * zpool_create_pool() - Create a new zpool
+ * @type	The type of the zpool to create (e.g. zbud, zsmalloc)
+ * @flags	What GFP flags should be used when the zpool allocates memory.
+ * @ops		The optional ops callback.
+ *
+ * This creates a new zpool of the specified type.  The zpool will use the
+ * given flags when allocating any memory.  If the ops param is NULL, then
+ * the created zpool will not be shrinkable.
+ *
+ * Returns: New zpool on success, NULL on failure.
+ */
+struct zpool *zpool_create_pool(char *type, gfp_t flags,
+			struct zpool_ops *ops);
+
+/**
+ * zpool_get_type() - Get the type of the zpool
+ * @pool	The zpool to check
+ *
+ * This returns the type of the pool, which will match one of the
+ * ZPOOL_TYPE_* defined values.
+ *
+ * Returns: The type of zpool.
+ */
+char *zpool_get_type(struct zpool *pool);
+
+/**
+ * zpool_destroy_pool() - Destroy a zpool
+ * @pool	The zpool to destroy.
+ *
+ * This destroys an existing zpool.  The zpool should not be in use.
+ */
+void zpool_destroy_pool(struct zpool *pool);
+
+/**
+ * zpool_malloc() - Allocate memory
+ * @pool	The zpool to allocate from.
+ * @size	The amount of memory to allocate.
+ * @handle	Pointer to the handle to set
+ *
+ * This allocates the requested amount of memory from the pool.
+ * The provided @handle will be set to the allocated object handle.
+ *
+ * Returns: 0 on success, negative value on error.
+ */
+int zpool_malloc(struct zpool *pool, size_t size, unsigned long *handle);
+
+/**
+ * zpool_free() - Free previously allocated memory
+ * @pool	The zpool that allocated the memory.
+ * @handle	The handle to the memory to free.
+ *
+ * This frees previously allocated memory.  This does not guarantee
+ * that the pool will actually free memory, only that the memory
+ * in the pool will become available for use by the pool.
+ */
+void zpool_free(struct zpool *pool, unsigned long handle);
+
+/**
+ * zpool_shrink() - Shrink the pool size
+ * @pool	The zpool to shrink.
+ * @size	The minimum amount to shrink the pool.
+ *
+ * This attempts to shrink the actual memory size of the pool
+ * by evicting currently used handle(s).  If the pool was
+ * created with no zpool_ops, or the evict call fails for any
+ * of the handles, this will fail.
+ *
+ * Returns: 0 on success, negative value on error/failure.
+ */
+int zpool_shrink(struct zpool *pool, size_t size);
+
+/**
+ * zpool_map_handle() - Map a previously allocated handle into memory
+ * @pool	The zpool that the handle was allocated from
+ * @handle	The handle to map
+ * @mm	How the memory should be mapped
+ *
+ * This maps a previously allocated handle into memory.  The @mm
+ * param indicates to the implemenation how the memory will be
+ * used, i.e. read-only, write-only, read-write.  If the
+ * implementation does not support it, the memory will be treated
+ * as read-write.
+ *
+ * This may hold locks, disable interrupts, and/or preemption,
+ * and the zpool_unmap_handle() must be called to undo those
+ * actions.  The code that uses the mapped handle should complete
+ * its operatons on the mapped handle memory quickly and unmap
+ * as soon as possible.  Multiple handles should not be mapped
+ * concurrently on a cpu.
+ *
+ * Returns: A pointer to the handle's mapped memory area.
+ */
+void *zpool_map_handle(struct zpool *pool, unsigned long handle,
+			enum zpool_mapmode mm);
+
+/**
+ * zpool_unmap_handle() - Unmap a previously mapped handle
+ * @pool	The zpool that the handle was allocated from
+ * @handle	The handle to unmap
+ *
+ * This unmaps a previously mapped handle.  Any locks or other
+ * actions that the implemenation took in zpool_map_handle()
+ * will be undone here.  The memory area returned from
+ * zpool_map_handle() should no longer be used after this.
+ */
+void zpool_unmap_handle(struct zpool *pool, unsigned long handle);
+
+/**
+ * zpool_get_total_size() - The total size of the pool
+ * @pool	The zpool to check
+ *
+ * This returns the total size in bytes of the pool.
+ *
+ * Returns: Total size of the zpool in bytes.
+ */
+u64 zpool_get_total_size(struct zpool *pool);
+
+#endif
diff --git a/mm/Kconfig b/mm/Kconfig
index 30cb6cb..bdb4cb2 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -515,21 +515,23 @@ config CMA_DEBUG
 	  processing calls such as dma_alloc_from_contiguous().
 	  This option does not affect warning and error messages.
 
-config ZBUD
-	tristate
-	default n
+config MEM_SOFT_DIRTY
+	bool "Track memory changes"
+	depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
+	select PROC_PAGE_MONITOR
 	help
-	  A special purpose allocator for storing compressed pages.
-	  It is designed to store up to two compressed pages per physical
-	  page.  While this design limits storage density, it has simple and
-	  deterministic reclaim properties that make it preferable to a higher
-	  density approach when reclaim will be used.
+	  This option enables memory changes tracking by introducing a
+	  soft-dirty bit on pte-s. This bit it set when someone writes
+	  into a page just as regular dirty bit, but unlike the latter
+	  it can be cleared by hands.
+
+	  See Documentation/vm/soft-dirty.txt for more details.
 
 config ZSWAP
 	bool "Compressed cache for swap pages (EXPERIMENTAL)"
 	depends on FRONTSWAP && CRYPTO=y
 	select CRYPTO_LZO
-	select ZBUD
+	select ZPOOL
 	default n
 	help
 	  A lightweight compressed cache for swap pages.  It takes
@@ -545,17 +547,22 @@ config ZSWAP
 	  they have not be fully explored on the large set of potential
 	  configurations and workloads that exist.
 
-config MEM_SOFT_DIRTY
-	bool "Track memory changes"
-	depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
-	select PROC_PAGE_MONITOR
+config ZPOOL
+	tristate "Common API for compressed memory storage"
+	default n
 	help
-	  This option enables memory changes tracking by introducing a
-	  soft-dirty bit on pte-s. This bit it set when someone writes
-	  into a page just as regular dirty bit, but unlike the latter
-	  it can be cleared by hands.
+	  Compressed memory storage API.  This allows using either zbud or
+	  zsmalloc.
 
-	  See Documentation/vm/soft-dirty.txt for more details.
+config ZBUD
+	tristate "Low density storage for compressed pages"
+	default n
+	help
+	  A special purpose allocator for storing compressed pages.
+	  It is designed to store up to two compressed pages per physical
+	  page.  While this design limits storage density, it has simple and
+	  deterministic reclaim properties that make it preferable to a higher
+	  density approach when reclaim will be used.
 
 config ZSMALLOC
 	bool "Memory allocator for compressed pages"
diff --git a/mm/Makefile b/mm/Makefile
index 9b75a4d..f64a5d4 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
 obj-$(CONFIG_CLEANCACHE) += cleancache.o
 obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
 obj-$(CONFIG_PAGE_OWNER) += pageowner.o
+obj-$(CONFIG_ZPOOL)	+= zpool.o
 obj-$(CONFIG_ZBUD)	+= zbud.o
 obj-$(CONFIG_ZSMALLOC)	+= zsmalloc.o
 obj-$(CONFIG_GENERIC_EARLY_IOREMAP) += early_ioremap.o
diff --git a/mm/zpool.c b/mm/zpool.c
new file mode 100644
index 0000000..2bda300
--- /dev/null
+++ b/mm/zpool.c
@@ -0,0 +1,349 @@
+/*
+ * zpool memory storage api
+ *
+ * Copyright (C) 2014 Dan Streetman
+ *
+ * This is a common frontend for the zbud and zsmalloc memory
+ * storage pool implementations.  Typically, this is used to
+ * store compressed memory.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/zpool.h>
+#include <linux/zbud.h>
+#include <linux/zsmalloc.h>
+
+struct zpool_imp {
+	void (*destroy)(struct zpool *pool);
+
+	int (*malloc)(struct zpool *pool, size_t size, unsigned long *handle);
+	void (*free)(struct zpool *pool, unsigned long handle);
+
+	int (*shrink)(struct zpool *pool, size_t size);
+
+	void *(*map)(struct zpool *pool, unsigned long handle,
+				enum zpool_mapmode mm);
+	void (*unmap)(struct zpool *pool, unsigned long handle);
+
+	u64 (*total_size)(struct zpool *pool);
+};
+
+struct zpool {
+	char *type;
+
+	union {
+#ifdef CONFIG_ZSMALLOC
+	struct zs_pool *zsmalloc_pool;
+#endif
+#ifdef CONFIG_ZBUD
+	struct zbud_pool *zbud_pool;
+#endif
+	};
+
+	struct zpool_imp *imp;
+	struct zpool_ops *ops;
+
+	struct list_head list;
+};
+
+static LIST_HEAD(zpools);
+static DEFINE_SPINLOCK(zpools_lock);
+
+static int zpool_noop_evict(struct zpool *pool, unsigned long handle)
+{
+	return -EINVAL;
+}
+static struct zpool_ops zpool_noop_ops = {
+	.evict = zpool_noop_evict
+};
+
+
+/* zsmalloc */
+
+#ifdef CONFIG_ZSMALLOC
+
+static void zpool_zsmalloc_destroy(struct zpool *zpool)
+{
+	spin_lock(&zpools_lock);
+	list_del(&zpool->list);
+	spin_unlock(&zpools_lock);
+
+	zs_destroy_pool(zpool->zsmalloc_pool);
+	kfree(zpool);
+}
+
+static int zpool_zsmalloc_malloc(struct zpool *pool, size_t size,
+			unsigned long *handle)
+{
+	*handle = zs_malloc(pool->zsmalloc_pool, size);
+	return *handle ? 0 : -1;
+}
+
+static void zpool_zsmalloc_free(struct zpool *pool, unsigned long handle)
+{
+	zs_free(pool->zsmalloc_pool, handle);
+}
+
+static int zpool_zsmalloc_shrink(struct zpool *pool, size_t size)
+{
+	/* Not yet supported */
+	return -EINVAL;
+}
+
+static void *zpool_zsmalloc_map(struct zpool *pool, unsigned long handle,
+			enum zpool_mapmode zpool_mapmode)
+{
+	enum zs_mapmode zs_mapmode;
+
+	switch (zpool_mapmode) {
+	case ZPOOL_MM_RO:
+		zs_mapmode = ZS_MM_RO; break;
+	case ZPOOL_MM_WO:
+		zs_mapmode = ZS_MM_WO; break;
+	case ZPOOL_MM_RW: /* fallthrough */
+	default:
+		zs_mapmode = ZS_MM_RW; break;
+	}
+	return zs_map_object(pool->zsmalloc_pool, handle, zs_mapmode);
+}
+
+static void zpool_zsmalloc_unmap(struct zpool *pool, unsigned long handle)
+{
+	zs_unmap_object(pool->zsmalloc_pool, handle);
+}
+
+static u64 zpool_zsmalloc_total_size(struct zpool *pool)
+{
+	return zs_get_total_size_bytes(pool->zsmalloc_pool);
+}
+
+static struct zpool_imp zpool_zsmalloc_imp = {
+	.destroy = zpool_zsmalloc_destroy,
+	.malloc = zpool_zsmalloc_malloc,
+	.free = zpool_zsmalloc_free,
+	.shrink = zpool_zsmalloc_shrink,
+	.map = zpool_zsmalloc_map,
+	.unmap = zpool_zsmalloc_unmap,
+	.total_size = zpool_zsmalloc_total_size
+};
+
+static struct zpool *zpool_zsmalloc_create(gfp_t flags, struct zpool_ops *ops)
+{
+	struct zpool *zpool;
+
+	zpool = kmalloc(sizeof(*zpool), GFP_KERNEL);
+	if (!zpool) {
+		pr_err("couldn't create zpool - out of memory\n");
+		return NULL;
+	}
+
+	zpool->zsmalloc_pool = zs_create_pool(flags);
+	if (!zpool->zsmalloc_pool) {
+		kfree(zpool);
+		pr_err("zs_create_pool() failed\n");
+		return NULL;
+	}
+
+	zpool->type = ZPOOL_TYPE_ZSMALLOC;
+	zpool->imp = &zpool_zsmalloc_imp;
+	zpool->ops = &zpool_noop_ops;
+	spin_lock(&zpools_lock);
+	list_add(&zpool->list, &zpools);
+	spin_unlock(&zpools_lock);
+
+	return zpool;
+}
+
+#else
+
+static struct zpool *zpool_zsmalloc_create(gfp_t flags, struct zpool_ops *ops)
+{
+	pr_info("no zsmalloc in this kernel\n");
+	return NULL;
+}
+
+#endif /* CONFIG_ZSMALLOC */
+
+
+/* zbud */
+
+#ifdef CONFIG_ZBUD
+
+static void zpool_zbud_destroy(struct zpool *zpool)
+{
+	spin_lock(&zpools_lock);
+	list_del(&zpool->list);
+	spin_unlock(&zpools_lock);
+
+	zbud_destroy_pool(zpool->zbud_pool);
+	kfree(zpool);
+}
+
+static int zpool_zbud_malloc(struct zpool *pool, size_t size,
+			unsigned long *handle)
+{
+	return zbud_alloc(pool->zbud_pool, size, handle);
+}
+
+static void zpool_zbud_free(struct zpool *pool, unsigned long handle)
+{
+	zbud_free(pool->zbud_pool, handle);
+}
+
+static int zpool_zbud_shrink(struct zpool *pool, size_t size)
+{
+	return zbud_reclaim_page(pool->zbud_pool, 3);
+}
+
+static void *zpool_zbud_map(struct zpool *pool, unsigned long handle,
+			enum zpool_mapmode zpool_mapmode)
+{
+	return zbud_map(pool->zbud_pool, handle);
+}
+
+static void zpool_zbud_unmap(struct zpool *pool, unsigned long handle)
+{
+	zbud_unmap(pool->zbud_pool, handle);
+}
+
+static u64 zpool_zbud_total_size(struct zpool *pool)
+{
+	return zbud_get_pool_size(pool->zbud_pool) * PAGE_SIZE;
+}
+
+static int zpool_zbud_evict(struct zbud_pool *zbud_pool, unsigned long handle)
+{
+	struct zpool *zpool;
+
+	spin_lock(&zpools_lock);
+	list_for_each_entry(zpool, &zpools, list) {
+		if (zpool->zbud_pool == zbud_pool) {
+			spin_unlock(&zpools_lock);
+			return zpool->ops->evict(zpool, handle);
+		}
+	}
+	spin_unlock(&zpools_lock);
+	return -EINVAL;
+}
+
+static struct zpool_imp zpool_zbud_imp = {
+	.destroy = zpool_zbud_destroy,
+	.malloc = zpool_zbud_malloc,
+	.free = zpool_zbud_free,
+	.shrink = zpool_zbud_shrink,
+	.map = zpool_zbud_map,
+	.unmap = zpool_zbud_unmap,
+	.total_size = zpool_zbud_total_size
+};
+
+static struct zbud_ops zpool_zbud_ops = {
+	.evict = zpool_zbud_evict
+};
+
+static struct zpool *zpool_zbud_create(gfp_t flags, struct zpool_ops *ops)
+{
+	struct zpool *zpool;
+	struct zbud_ops *zbud_ops = (ops ? &zpool_zbud_ops : NULL);
+
+	zpool = kmalloc(sizeof(*zpool), GFP_KERNEL);
+	if (!zpool) {
+		pr_err("couldn't create zpool - out of memory\n");
+		return NULL;
+	}
+
+	zpool->zbud_pool = zbud_create_pool(flags, zbud_ops);
+	if (!zpool->zbud_pool) {
+		kfree(zpool);
+		pr_err("zbud_create_pool() failed\n");
+		return NULL;
+	}
+
+	zpool->type = ZPOOL_TYPE_ZBUD;
+	zpool->imp = &zpool_zbud_imp;
+	zpool->ops = (ops ? ops : &zpool_noop_ops);
+	spin_lock(&zpools_lock);
+	list_add(&zpool->list, &zpools);
+	spin_unlock(&zpools_lock);
+
+	return zpool;
+}
+
+#else
+
+static struct zpool *zpool_zbud_create(gfp_t flags, struct zpool_ops *ops)
+{
+	pr_info("no zbud in this kernel\n");
+	return NULL;
+}
+
+#endif /* CONFIG_ZBUD */
+
+
+struct zpool *zpool_create_pool(char *type, gfp_t flags,
+			struct zpool_ops *ops)
+{
+	struct zpool *pool = NULL;
+
+	pr_info("creating pool type %s\n", type);
+
+	if (!strcmp(type, ZPOOL_TYPE_ZSMALLOC))
+		pool = zpool_zsmalloc_create(flags, ops);
+	else if (!strcmp(type, ZPOOL_TYPE_ZBUD))
+		pool = zpool_zbud_create(flags, ops);
+	else
+		pr_err("unknown type %s\n", type);
+
+	if (pool)
+		pr_info("created %s pool\n", type);
+	else
+		pr_err("couldn't create %s pool\n", type);
+
+	return pool;
+}
+
+char *zpool_get_type(struct zpool *pool)
+{
+	return pool->type;
+}
+
+void zpool_destroy_pool(struct zpool *pool)
+{
+	pool->imp->destroy(pool);
+}
+
+int zpool_malloc(struct zpool *pool, size_t size, unsigned long *handle)
+{
+	return pool->imp->malloc(pool, size, handle);
+}
+
+void zpool_free(struct zpool *pool, unsigned long handle)
+{
+	pool->imp->free(pool, handle);
+}
+
+int zpool_shrink(struct zpool *pool, size_t size)
+{
+	return pool->imp->shrink(pool, size);
+}
+
+void *zpool_map_handle(struct zpool *pool, unsigned long handle,
+			enum zpool_mapmode mapmode)
+{
+	return pool->imp->map(pool, handle, mapmode);
+}
+
+void zpool_unmap_handle(struct zpool *pool, unsigned long handle)
+{
+	pool->imp->unmap(pool, handle);
+}
+
+u64 zpool_get_total_size(struct zpool *pool)
+{
+	return pool->imp->total_size(pool);
+}
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCHv2 4/4] mm/zswap: update zswap to use zpool
  2014-05-07 21:51 ` [PATCHv2 0/4] mm/zpool: " Dan Streetman
                     ` (2 preceding siblings ...)
  2014-05-07 21:51   ` [PATCHv2 3/4] mm/zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
@ 2014-05-07 21:51   ` Dan Streetman
  2014-05-24 19:06   ` [PATCHv3 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
  4 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-05-07 21:51 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Nitin Gupta, Weijie Yang
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Change zswap to use the zpool api instead of directly using zbud.
Add a boot-time param to allow selecting which zpool implementation
to use, with zbud as the default.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Cc: Seth Jennings <sjennings@variantweb.net>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

Changes since v1 https://lkml.org/lkml/2014/4/19/102
 -since zpool fallback is removed, manually fall back to zbud if
  specified type fails

 mm/zswap.c | 75 ++++++++++++++++++++++++++++++++++++--------------------------
 1 file changed, 44 insertions(+), 31 deletions(-)

diff --git a/mm/zswap.c b/mm/zswap.c
index 1cc6770..438c8a5 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -34,7 +34,7 @@
 #include <linux/swap.h>
 #include <linux/crypto.h>
 #include <linux/mempool.h>
-#include <linux/zbud.h>
+#include <linux/zpool.h>
 
 #include <linux/mm_types.h>
 #include <linux/page-flags.h>
@@ -45,8 +45,8 @@
 /*********************************
 * statistics
 **********************************/
-/* Number of memory pages used by the compressed pool */
-static u64 zswap_pool_pages;
+/* Total bytes used by the compressed storage */
+static u64 zswap_pool_total_size;
 /* The number of compressed pages currently stored in zswap */
 static atomic_t zswap_stored_pages = ATOMIC_INIT(0);
 
@@ -89,8 +89,13 @@ static unsigned int zswap_max_pool_percent = 20;
 module_param_named(max_pool_percent,
 			zswap_max_pool_percent, uint, 0644);
 
-/* zbud_pool is shared by all of zswap backend  */
-static struct zbud_pool *zswap_pool;
+/* Compressed storage to use */
+#define ZSWAP_ZPOOL_DEFAULT ZPOOL_TYPE_ZBUD
+static char *zswap_zpool_type = ZSWAP_ZPOOL_DEFAULT;
+module_param_named(zpool, zswap_zpool_type, charp, 0444);
+
+/* zpool is shared by all of zswap backend  */
+static struct zpool *zswap_pool;
 
 /*********************************
 * compression functions
@@ -168,7 +173,7 @@ static void zswap_comp_exit(void)
  *            be held while changing the refcount.  Since the lock must
  *            be held, there is no reason to also make refcount atomic.
  * offset - the swap offset for the entry.  Index into the red-black tree.
- * handle - zbud allocation handle that stores the compressed page data
+ * handle - zpool allocation handle that stores the compressed page data
  * length - the length in bytes of the compressed page data.  Needed during
  *          decompression
  */
@@ -284,15 +289,15 @@ static void zswap_rb_erase(struct rb_root *root, struct zswap_entry *entry)
 }
 
 /*
- * Carries out the common pattern of freeing and entry's zbud allocation,
+ * Carries out the common pattern of freeing and entry's zpool allocation,
  * freeing the entry itself, and decrementing the number of stored pages.
  */
 static void zswap_free_entry(struct zswap_entry *entry)
 {
-	zbud_free(zswap_pool, entry->handle);
+	zpool_free(zswap_pool, entry->handle);
 	zswap_entry_cache_free(entry);
 	atomic_dec(&zswap_stored_pages);
-	zswap_pool_pages = zbud_get_pool_size(zswap_pool);
+	zswap_pool_total_size = zpool_get_total_size(zswap_pool);
 }
 
 /* caller must hold the tree lock */
@@ -409,7 +414,7 @@ cleanup:
 static bool zswap_is_full(void)
 {
 	return totalram_pages * zswap_max_pool_percent / 100 <
-		zswap_pool_pages;
+		DIV_ROUND_UP(zswap_pool_total_size, PAGE_SIZE);
 }
 
 /*********************************
@@ -525,7 +530,7 @@ static int zswap_get_swap_cache_page(swp_entry_t entry,
  * the swap cache, the compressed version stored by zswap can be
  * freed.
  */
-static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle)
+static int zswap_writeback_entry(struct zpool *pool, unsigned long handle)
 {
 	struct zswap_header *zhdr;
 	swp_entry_t swpentry;
@@ -541,9 +546,9 @@ static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle)
 	};
 
 	/* extract swpentry from data */
-	zhdr = zbud_map(pool, handle);
+	zhdr = zpool_map_handle(pool, handle, ZPOOL_MM_RO);
 	swpentry = zhdr->swpentry; /* here */
-	zbud_unmap(pool, handle);
+	zpool_unmap_handle(pool, handle);
 	tree = zswap_trees[swp_type(swpentry)];
 	offset = swp_offset(swpentry);
 
@@ -573,13 +578,13 @@ static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle)
 	case ZSWAP_SWAPCACHE_NEW: /* page is locked */
 		/* decompress */
 		dlen = PAGE_SIZE;
-		src = (u8 *)zbud_map(zswap_pool, entry->handle) +
-			sizeof(struct zswap_header);
+		src = (u8 *)zpool_map_handle(zswap_pool, entry->handle,
+				ZPOOL_MM_RO) + sizeof(struct zswap_header);
 		dst = kmap_atomic(page);
 		ret = zswap_comp_op(ZSWAP_COMPOP_DECOMPRESS, src,
 				entry->length, dst, &dlen);
 		kunmap_atomic(dst);
-		zbud_unmap(zswap_pool, entry->handle);
+		zpool_unmap_handle(zswap_pool, entry->handle);
 		BUG_ON(ret);
 		BUG_ON(dlen != PAGE_SIZE);
 
@@ -652,7 +657,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 	/* reclaim space if needed */
 	if (zswap_is_full()) {
 		zswap_pool_limit_hit++;
-		if (zbud_reclaim_page(zswap_pool, 8)) {
+		if (zpool_shrink(zswap_pool, PAGE_SIZE)) {
 			zswap_reject_reclaim_fail++;
 			ret = -ENOMEM;
 			goto reject;
@@ -679,7 +684,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 
 	/* store */
 	len = dlen + sizeof(struct zswap_header);
-	ret = zbud_alloc(zswap_pool, len, &handle);
+	ret = zpool_malloc(zswap_pool, len, &handle);
 	if (ret == -ENOSPC) {
 		zswap_reject_compress_poor++;
 		goto freepage;
@@ -688,11 +693,11 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 		zswap_reject_alloc_fail++;
 		goto freepage;
 	}
-	zhdr = zbud_map(zswap_pool, handle);
+	zhdr = zpool_map_handle(zswap_pool, handle, ZPOOL_MM_RW);
 	zhdr->swpentry = swp_entry(type, offset);
 	buf = (u8 *)(zhdr + 1);
 	memcpy(buf, dst, dlen);
-	zbud_unmap(zswap_pool, handle);
+	zpool_unmap_handle(zswap_pool, handle);
 	put_cpu_var(zswap_dstmem);
 
 	/* populate entry */
@@ -715,7 +720,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 
 	/* update stats */
 	atomic_inc(&zswap_stored_pages);
-	zswap_pool_pages = zbud_get_pool_size(zswap_pool);
+	zswap_pool_total_size = zpool_get_total_size(zswap_pool);
 
 	return 0;
 
@@ -751,13 +756,13 @@ static int zswap_frontswap_load(unsigned type, pgoff_t offset,
 
 	/* decompress */
 	dlen = PAGE_SIZE;
-	src = (u8 *)zbud_map(zswap_pool, entry->handle) +
-			sizeof(struct zswap_header);
+	src = (u8 *)zpool_map_handle(zswap_pool, entry->handle,
+			ZPOOL_MM_RO) + sizeof(struct zswap_header);
 	dst = kmap_atomic(page);
 	ret = zswap_comp_op(ZSWAP_COMPOP_DECOMPRESS, src, entry->length,
 		dst, &dlen);
 	kunmap_atomic(dst);
-	zbud_unmap(zswap_pool, entry->handle);
+	zpool_unmap_handle(zswap_pool, entry->handle);
 	BUG_ON(ret);
 
 	spin_lock(&tree->lock);
@@ -810,7 +815,7 @@ static void zswap_frontswap_invalidate_area(unsigned type)
 	zswap_trees[type] = NULL;
 }
 
-static struct zbud_ops zswap_zbud_ops = {
+static struct zpool_ops zswap_zpool_ops = {
 	.evict = zswap_writeback_entry
 };
 
@@ -868,8 +873,8 @@ static int __init zswap_debugfs_init(void)
 			zswap_debugfs_root, &zswap_written_back_pages);
 	debugfs_create_u64("duplicate_entry", S_IRUGO,
 			zswap_debugfs_root, &zswap_duplicate_entry);
-	debugfs_create_u64("pool_pages", S_IRUGO,
-			zswap_debugfs_root, &zswap_pool_pages);
+	debugfs_create_u64("pool_total_size", S_IRUGO,
+			zswap_debugfs_root, &zswap_pool_total_size);
 	debugfs_create_atomic_t("stored_pages", S_IRUGO,
 			zswap_debugfs_root, &zswap_stored_pages);
 
@@ -894,17 +899,25 @@ static void __exit zswap_debugfs_exit(void) { }
 **********************************/
 static int __init init_zswap(void)
 {
+	gfp_t gfp = __GFP_NORETRY | __GFP_NOWARN;
+
 	if (!zswap_enabled)
 		return 0;
 
 	pr_info("loading zswap\n");
 
-	zswap_pool = zbud_create_pool(__GFP_NORETRY | __GFP_NOWARN,
-			&zswap_zbud_ops);
+	zswap_pool = zpool_create_pool(zswap_zpool_type, gfp, &zswap_zpool_ops);
+	if (!zswap_pool) {
+		pr_info("%s zpool not available\n", zswap_zpool_type);
+		zswap_zpool_type = ZSWAP_ZPOOL_DEFAULT;
+		zswap_pool = zpool_create_pool(zswap_zpool_type, gfp,
+					       &zswap_zpool_ops);
+	}
 	if (!zswap_pool) {
-		pr_err("zbud pool creation failed\n");
+		pr_err("zpool creation failed\n");
 		goto error;
 	}
+	pr_info("using %s pool\n", zswap_zpool_type);
 
 	if (zswap_entry_cache_create()) {
 		pr_err("entry cache creation failed\n");
@@ -928,7 +941,7 @@ pcpufail:
 compfail:
 	zswap_entry_cache_destory();
 cachefail:
-	zbud_destroy_pool(zswap_pool);
+	zpool_destroy_pool(zswap_pool);
 error:
 	return -ENOMEM;
 }
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* Re: [PATCHv2 1/4] mm/zbud: zbud_alloc() minor param change
  2014-05-07 21:51   ` [PATCHv2 1/4] mm/zbud: zbud_alloc() minor param change Dan Streetman
@ 2014-05-09  3:33     ` Seth Jennings
  0 siblings, 0 replies; 65+ messages in thread
From: Seth Jennings @ 2014-05-09  3:33 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Minchan Kim, Weijie Yang, Nitin Gupta, Andrew Morton, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Wed, May 07, 2014 at 05:51:33PM -0400, Dan Streetman wrote:
> Change zbud to store gfp_t flags passed at pool creation to use for
> each alloc; this allows the api to be closer to the existing zsmalloc
> interface, and the only current zbud user (zswap) uses the same gfp
> flags for all allocs.  Update zswap to use changed interface.
> 

Acked-by: Seth Jennings <sjennings@variantweb.net>

> Signed-off-by: Dan Streetman <ddstreet@ieee.org>
> Cc: Seth Jennings <sjennings@variantweb.net>
> Cc: Weijie Yang <weijie.yang@samsung.com>
> ---
> 
> Changes since v1 https://lkml.org/lkml/2014/4/19/98
>  -context changes only; zbud_alloc parameter type changed since
>   last patch
> 
>  include/linux/zbud.h |  2 +-
>  mm/zbud.c            | 27 +++++++++++++++------------
>  mm/zswap.c           |  6 +++---
>  3 files changed, 19 insertions(+), 16 deletions(-)
> 
> diff --git a/include/linux/zbud.h b/include/linux/zbud.h
> index 13af0d4..0b2534e 100644
> --- a/include/linux/zbud.h
> +++ b/include/linux/zbud.h
> @@ -11,7 +11,7 @@ struct zbud_ops {
>  
>  struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops);
>  void zbud_destroy_pool(struct zbud_pool *pool);
> -int zbud_alloc(struct zbud_pool *pool, unsigned int size, gfp_t gfp,
> +int zbud_alloc(struct zbud_pool *pool, unsigned int size,
>  	unsigned long *handle);
>  void zbud_free(struct zbud_pool *pool, unsigned long handle);
>  int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries);
> diff --git a/mm/zbud.c b/mm/zbud.c
> index 01df13a..847c01c 100644
> --- a/mm/zbud.c
> +++ b/mm/zbud.c
> @@ -94,6 +94,7 @@ struct zbud_pool {
>  	struct list_head lru;
>  	u64 pages_nr;
>  	struct zbud_ops *ops;
> +	gfp_t gfp;
>  };
>  
>  /*
> @@ -193,9 +194,12 @@ static int num_free_chunks(struct zbud_header *zhdr)
>  *****************/
>  /**
>   * zbud_create_pool() - create a new zbud pool
> - * @gfp:	gfp flags when allocating the zbud pool structure
> + * @gfp:	gfp flags when growing the pool
>   * @ops:	user-defined operations for the zbud pool
>   *
> + * gfp should not set __GFP_HIGHMEM as highmem pages cannot be used
> + * as zbud pool pages.
> + *
>   * Return: pointer to the new zbud pool or NULL if the metadata allocation
>   * failed.
>   */
> @@ -204,7 +208,9 @@ struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops)
>  	struct zbud_pool *pool;
>  	int i;
>  
> -	pool = kmalloc(sizeof(struct zbud_pool), gfp);
> +	if (gfp & __GFP_HIGHMEM)
> +		return NULL;
> +	pool = kmalloc(sizeof(struct zbud_pool), GFP_KERNEL);
>  	if (!pool)
>  		return NULL;
>  	spin_lock_init(&pool->lock);
> @@ -214,6 +220,7 @@ struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops)
>  	INIT_LIST_HEAD(&pool->lru);
>  	pool->pages_nr = 0;
>  	pool->ops = ops;
> +	pool->gfp = gfp;
>  	return pool;
>  }
>  
> @@ -232,7 +239,6 @@ void zbud_destroy_pool(struct zbud_pool *pool)
>   * zbud_alloc() - allocates a region of a given size
>   * @pool:	zbud pool from which to allocate
>   * @size:	size in bytes of the desired allocation
> - * @gfp:	gfp flags used if the pool needs to grow
>   * @handle:	handle of the new allocation
>   *
>   * This function will attempt to find a free region in the pool large enough to
> @@ -240,14 +246,11 @@ void zbud_destroy_pool(struct zbud_pool *pool)
>   * performed first. If no suitable free region is found, then a new page is
>   * allocated and added to the pool to satisfy the request.
>   *
> - * gfp should not set __GFP_HIGHMEM as highmem pages cannot be used
> - * as zbud pool pages.
> - *
> - * Return: 0 if success and handle is set, otherwise -EINVAL if the size or
> - * gfp arguments are invalid or -ENOMEM if the pool was unable to allocate
> - * a new page.
> + * Return: 0 if success and @handle is set, -ENOSPC if the @size is too large,
> + * -EINVAL if the @size is 0, or -ENOMEM if the pool was unable to
> + * allocate a new page.
>   */
> -int zbud_alloc(struct zbud_pool *pool, unsigned int size, gfp_t gfp,
> +int zbud_alloc(struct zbud_pool *pool, unsigned int size,
>  			unsigned long *handle)
>  {
>  	int chunks, i, freechunks;
> @@ -255,7 +258,7 @@ int zbud_alloc(struct zbud_pool *pool, unsigned int size, gfp_t gfp,
>  	enum buddy bud;
>  	struct page *page;
>  
> -	if (!size || (gfp & __GFP_HIGHMEM))
> +	if (!size)
>  		return -EINVAL;
>  	if (size > PAGE_SIZE - ZHDR_SIZE_ALIGNED - CHUNK_SIZE)
>  		return -ENOSPC;
> @@ -279,7 +282,7 @@ int zbud_alloc(struct zbud_pool *pool, unsigned int size, gfp_t gfp,
>  
>  	/* Couldn't find unbuddied zbud page, create new one */
>  	spin_unlock(&pool->lock);
> -	page = alloc_page(gfp);
> +	page = alloc_page(pool->gfp);
>  	if (!page)
>  		return -ENOMEM;
>  	spin_lock(&pool->lock);
> diff --git a/mm/zswap.c b/mm/zswap.c
> index aeaef0f..1cc6770 100644
> --- a/mm/zswap.c
> +++ b/mm/zswap.c
> @@ -679,8 +679,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
>  
>  	/* store */
>  	len = dlen + sizeof(struct zswap_header);
> -	ret = zbud_alloc(zswap_pool, len, __GFP_NORETRY | __GFP_NOWARN,
> -		&handle);
> +	ret = zbud_alloc(zswap_pool, len, &handle);
>  	if (ret == -ENOSPC) {
>  		zswap_reject_compress_poor++;
>  		goto freepage;
> @@ -900,7 +899,8 @@ static int __init init_zswap(void)
>  
>  	pr_info("loading zswap\n");
>  
> -	zswap_pool = zbud_create_pool(GFP_KERNEL, &zswap_zbud_ops);
> +	zswap_pool = zbud_create_pool(__GFP_NORETRY | __GFP_NOWARN,
> +			&zswap_zbud_ops);
>  	if (!zswap_pool) {
>  		pr_err("zbud pool creation failed\n");
>  		goto error;
> -- 
> 1.8.3.1
> 
> --
> To unsubscribe, send a message with 'unsubscribe linux-mm' in
> the body to majordomo@kvack.org.  For more info on Linux MM,
> see: http://www.linux-mm.org/ .
> Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 2/4] mm/zbud: change zbud_alloc size type to size_t
  2014-05-07 21:51   ` [PATCH 2/4] mm/zbud: change zbud_alloc size type to size_t Dan Streetman
@ 2014-05-09  3:33     ` Seth Jennings
  0 siblings, 0 replies; 65+ messages in thread
From: Seth Jennings @ 2014-05-09  3:33 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Minchan Kim, Weijie Yang, Nitin Gupta, Andrew Morton, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Wed, May 07, 2014 at 05:51:34PM -0400, Dan Streetman wrote:
> Change the type of the zbud_alloc() size param from unsigned int
> to size_t.
> 
> Technically, this should not make any difference, as the zbud
> implementation already restricts the size to well within either
> type's limits; but as zsmalloc (and kmalloc) use size_t, and
> zpool will use size_t, this brings the size parameter type
> in line with zsmalloc/zpool.

Acked-by: Seth Jennings <sjennings@variantweb.net>

> 
> Signed-off-by: Dan Streetman <ddstreet@ieee.org>
> Cc: Seth Jennings <sjennings@variantweb.net>
> Cc: Weijie Yang <weijie.yang@samsung.com>
> ---
> 
> While the rest of the patches in this set are v2, this is new for
> the set; previously a patch to implement zsmalloc shrinking was
> here, but that's removed.  This patch instead changes the
> zbud_alloc() size parameter type from unsigned int to size_t, to
> be the same as the zsmalloc and zpool size param type.
> 
>  include/linux/zbud.h | 2 +-
>  mm/zbud.c            | 5 ++---
>  2 files changed, 3 insertions(+), 4 deletions(-)
> 
> diff --git a/include/linux/zbud.h b/include/linux/zbud.h
> index 0b2534e..1e9cb57 100644
> --- a/include/linux/zbud.h
> +++ b/include/linux/zbud.h
> @@ -11,7 +11,7 @@ struct zbud_ops {
>  
>  struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops);
>  void zbud_destroy_pool(struct zbud_pool *pool);
> -int zbud_alloc(struct zbud_pool *pool, unsigned int size,
> +int zbud_alloc(struct zbud_pool *pool, size_t size,
>  	unsigned long *handle);
>  void zbud_free(struct zbud_pool *pool, unsigned long handle);
>  int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries);
> diff --git a/mm/zbud.c b/mm/zbud.c
> index 847c01c..dd13665 100644
> --- a/mm/zbud.c
> +++ b/mm/zbud.c
> @@ -123,7 +123,7 @@ enum buddy {
>  };
>  
>  /* Converts an allocation size in bytes to size in zbud chunks */
> -static int size_to_chunks(int size)
> +static int size_to_chunks(size_t size)
>  {
>  	return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT;
>  }
> @@ -250,8 +250,7 @@ void zbud_destroy_pool(struct zbud_pool *pool)
>   * -EINVAL if the @size is 0, or -ENOMEM if the pool was unable to
>   * allocate a new page.
>   */
> -int zbud_alloc(struct zbud_pool *pool, unsigned int size,
> -			unsigned long *handle)
> +int zbud_alloc(struct zbud_pool *pool, size_t size, unsigned long *handle)
>  {
>  	int chunks, i, freechunks;
>  	struct zbud_header *zhdr = NULL;
> -- 
> 1.8.3.1
> 

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv2 3/4] mm/zpool: implement common zpool api to zbud/zsmalloc
  2014-05-07 21:51   ` [PATCHv2 3/4] mm/zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
@ 2014-05-09  4:13     ` Seth Jennings
  2014-05-10 16:06       ` Dan Streetman
  0 siblings, 1 reply; 65+ messages in thread
From: Seth Jennings @ 2014-05-09  4:13 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Minchan Kim, Nitin Gupta, Weijie Yang, Andrew Morton, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Wed, May 07, 2014 at 05:51:35PM -0400, Dan Streetman wrote:
> Add zpool api.
> 
> zpool provides an interface for memory storage, typically of compressed
> memory.  Users can select what backend to use; currently the only
> implementations are zbud, a low density implementation with up to
> two compressed pages per storage page, and zsmalloc, a higher density
> implementation with multiple compressed pages per storage page.

This is the wrong design methinks.  There are a bunch of #ifdefs for each
driver in the zpool.c code.  Available drivers (zbud, zsmalloc) should
register _up_ to the zpool layer.  That way the zpool layer doesn't have
to add a bunch of new code for each driver.

This zpool layer should be _really_ thin, stateless from the user point
of view, basically just wrapping the driver ops call.

New functions for driver registration:
zpool_register_driver()
zpool_unregister_driver()

Something like this (note the "void *" type of the pool):

struct zpool_driver_ops {
	void (*destroy)(void *pool);
	int (*malloc)(void *pool, size_t size, unsigned long *handle);
	....
}

Each driver can cast the void *pool to the driver pool type on the
driver side.

struct zpool_driver {
	char *driver_name;
	struct zpool_driver *ops;
}

Then drivers create a struct zpool_driver suitable for them and register
with zpool_register_driver().

struct zpool {
	void *driver_pool;
	struct zpool_driver *driver;
}

zpool_create() is:

struct zpool *zpool_create(char *driver_name, gfp_t flags, void *ops)
{
	[search for backend driver with name driver_name]
	[alloc new zpool]
	zpool->driver = driver;
	zpool->driver_pool = driver->ops->create(flags, ops);
	return zpool;
}

A user function like zpool_free() is just:

void zpool_free(struct zpool *pool, unsigned long handle)
{
	pool->driver->free(pool->driver_pool, handle);
}

Hopefully this makes sense.  Obviously, I didn't rewrite this whole
thing to see how it works end to end so there may be some pitfalls I'm
not considering.

Seth

> 
> Signed-off-by: Dan Streetman <ddstreet@ieee.org>
> Cc: Seth Jennings <sjennings@variantweb.net>
> Cc: Minchan Kim <minchan@kernel.org>
> Cc: Nitin Gupta <ngupta@vflare.org>
> Cc: Weijie Yang <weijie.yang@samsung.com>
> ---
> 
> Changes since v1 https://lkml.org/lkml/2014/4/19/101
>  -add some pr_info() during creation and pr_err() on errors
>  -remove zpool code to call zs_shrink(), since zsmalloc shrinking
>   was removed from this patchset
>  -remove fallback; only specified pool type will be tried
>  -pr_fmt() is defined in zpool to prefix zpool: in any pr_XXX() calls
> 
>  include/linux/zpool.h | 160 +++++++++++++++++++++++
>  mm/Kconfig            |  43 ++++---
>  mm/Makefile           |   1 +
>  mm/zpool.c            | 349 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 535 insertions(+), 18 deletions(-)
>  create mode 100644 include/linux/zpool.h
>  create mode 100644 mm/zpool.c
> 
> diff --git a/include/linux/zpool.h b/include/linux/zpool.h
> new file mode 100644
> index 0000000..08f5468
> --- /dev/null
> +++ b/include/linux/zpool.h
> @@ -0,0 +1,160 @@
> +/*
> + * zpool memory storage api
> + *
> + * Copyright (C) 2014 Dan Streetman
> + *
> + * This is a common frontend for the zbud and zsmalloc memory
> + * storage pool implementations.  Typically, this is used to
> + * store compressed memory.
> + */
> +
> +#ifndef _ZPOOL_H_
> +#define _ZPOOL_H_
> +
> +struct zpool;
> +
> +struct zpool_ops {
> +	int (*evict)(struct zpool *pool, unsigned long handle);
> +};
> +
> +#define ZPOOL_TYPE_ZSMALLOC "zsmalloc"
> +#define ZPOOL_TYPE_ZBUD "zbud"
> +
> +/*
> + * Control how a handle is mapped.  It will be ignored if the
> + * implementation does not support it.  Its use is optional.
> + * Note that this does not refer to memory protection, it
> + * refers to how the memory will be copied in/out if copying
> + * is necessary during mapping; read-write is the safest as
> + * it copies the existing memory in on map, and copies the
> + * changed memory back out on unmap.  Write-only does not copy
> + * in the memory and should only be used for initialization.
> + * If in doubt, use ZPOOL_MM_DEFAULT which is read-write.
> + */
> +enum zpool_mapmode {
> +	ZPOOL_MM_RW, /* normal read-write mapping */
> +	ZPOOL_MM_RO, /* read-only (no copy-out at unmap time) */
> +	ZPOOL_MM_WO, /* write-only (no copy-in at map time) */
> +
> +	ZPOOL_MM_DEFAULT = ZPOOL_MM_RW
> +};
> +
> +/**
> + * zpool_create_pool() - Create a new zpool
> + * @type	The type of the zpool to create (e.g. zbud, zsmalloc)
> + * @flags	What GFP flags should be used when the zpool allocates memory.
> + * @ops		The optional ops callback.
> + *
> + * This creates a new zpool of the specified type.  The zpool will use the
> + * given flags when allocating any memory.  If the ops param is NULL, then
> + * the created zpool will not be shrinkable.
> + *
> + * Returns: New zpool on success, NULL on failure.
> + */
> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
> +			struct zpool_ops *ops);
> +
> +/**
> + * zpool_get_type() - Get the type of the zpool
> + * @pool	The zpool to check
> + *
> + * This returns the type of the pool, which will match one of the
> + * ZPOOL_TYPE_* defined values.
> + *
> + * Returns: The type of zpool.
> + */
> +char *zpool_get_type(struct zpool *pool);
> +
> +/**
> + * zpool_destroy_pool() - Destroy a zpool
> + * @pool	The zpool to destroy.
> + *
> + * This destroys an existing zpool.  The zpool should not be in use.
> + */
> +void zpool_destroy_pool(struct zpool *pool);
> +
> +/**
> + * zpool_malloc() - Allocate memory
> + * @pool	The zpool to allocate from.
> + * @size	The amount of memory to allocate.
> + * @handle	Pointer to the handle to set
> + *
> + * This allocates the requested amount of memory from the pool.
> + * The provided @handle will be set to the allocated object handle.
> + *
> + * Returns: 0 on success, negative value on error.
> + */
> +int zpool_malloc(struct zpool *pool, size_t size, unsigned long *handle);
> +
> +/**
> + * zpool_free() - Free previously allocated memory
> + * @pool	The zpool that allocated the memory.
> + * @handle	The handle to the memory to free.
> + *
> + * This frees previously allocated memory.  This does not guarantee
> + * that the pool will actually free memory, only that the memory
> + * in the pool will become available for use by the pool.
> + */
> +void zpool_free(struct zpool *pool, unsigned long handle);
> +
> +/**
> + * zpool_shrink() - Shrink the pool size
> + * @pool	The zpool to shrink.
> + * @size	The minimum amount to shrink the pool.
> + *
> + * This attempts to shrink the actual memory size of the pool
> + * by evicting currently used handle(s).  If the pool was
> + * created with no zpool_ops, or the evict call fails for any
> + * of the handles, this will fail.
> + *
> + * Returns: 0 on success, negative value on error/failure.
> + */
> +int zpool_shrink(struct zpool *pool, size_t size);
> +
> +/**
> + * zpool_map_handle() - Map a previously allocated handle into memory
> + * @pool	The zpool that the handle was allocated from
> + * @handle	The handle to map
> + * @mm	How the memory should be mapped
> + *
> + * This maps a previously allocated handle into memory.  The @mm
> + * param indicates to the implemenation how the memory will be
> + * used, i.e. read-only, write-only, read-write.  If the
> + * implementation does not support it, the memory will be treated
> + * as read-write.
> + *
> + * This may hold locks, disable interrupts, and/or preemption,
> + * and the zpool_unmap_handle() must be called to undo those
> + * actions.  The code that uses the mapped handle should complete
> + * its operatons on the mapped handle memory quickly and unmap
> + * as soon as possible.  Multiple handles should not be mapped
> + * concurrently on a cpu.
> + *
> + * Returns: A pointer to the handle's mapped memory area.
> + */
> +void *zpool_map_handle(struct zpool *pool, unsigned long handle,
> +			enum zpool_mapmode mm);
> +
> +/**
> + * zpool_unmap_handle() - Unmap a previously mapped handle
> + * @pool	The zpool that the handle was allocated from
> + * @handle	The handle to unmap
> + *
> + * This unmaps a previously mapped handle.  Any locks or other
> + * actions that the implemenation took in zpool_map_handle()
> + * will be undone here.  The memory area returned from
> + * zpool_map_handle() should no longer be used after this.
> + */
> +void zpool_unmap_handle(struct zpool *pool, unsigned long handle);
> +
> +/**
> + * zpool_get_total_size() - The total size of the pool
> + * @pool	The zpool to check
> + *
> + * This returns the total size in bytes of the pool.
> + *
> + * Returns: Total size of the zpool in bytes.
> + */
> +u64 zpool_get_total_size(struct zpool *pool);
> +
> +#endif
> diff --git a/mm/Kconfig b/mm/Kconfig
> index 30cb6cb..bdb4cb2 100644
> --- a/mm/Kconfig
> +++ b/mm/Kconfig
> @@ -515,21 +515,23 @@ config CMA_DEBUG
>  	  processing calls such as dma_alloc_from_contiguous().
>  	  This option does not affect warning and error messages.
>  
> -config ZBUD
> -	tristate
> -	default n
> +config MEM_SOFT_DIRTY
> +	bool "Track memory changes"
> +	depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
> +	select PROC_PAGE_MONITOR
>  	help
> -	  A special purpose allocator for storing compressed pages.
> -	  It is designed to store up to two compressed pages per physical
> -	  page.  While this design limits storage density, it has simple and
> -	  deterministic reclaim properties that make it preferable to a higher
> -	  density approach when reclaim will be used.
> +	  This option enables memory changes tracking by introducing a
> +	  soft-dirty bit on pte-s. This bit it set when someone writes
> +	  into a page just as regular dirty bit, but unlike the latter
> +	  it can be cleared by hands.
> +
> +	  See Documentation/vm/soft-dirty.txt for more details.
>  
>  config ZSWAP
>  	bool "Compressed cache for swap pages (EXPERIMENTAL)"
>  	depends on FRONTSWAP && CRYPTO=y
>  	select CRYPTO_LZO
> -	select ZBUD
> +	select ZPOOL
>  	default n
>  	help
>  	  A lightweight compressed cache for swap pages.  It takes
> @@ -545,17 +547,22 @@ config ZSWAP
>  	  they have not be fully explored on the large set of potential
>  	  configurations and workloads that exist.
>  
> -config MEM_SOFT_DIRTY
> -	bool "Track memory changes"
> -	depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
> -	select PROC_PAGE_MONITOR
> +config ZPOOL
> +	tristate "Common API for compressed memory storage"
> +	default n
>  	help
> -	  This option enables memory changes tracking by introducing a
> -	  soft-dirty bit on pte-s. This bit it set when someone writes
> -	  into a page just as regular dirty bit, but unlike the latter
> -	  it can be cleared by hands.
> +	  Compressed memory storage API.  This allows using either zbud or
> +	  zsmalloc.
>  
> -	  See Documentation/vm/soft-dirty.txt for more details.
> +config ZBUD
> +	tristate "Low density storage for compressed pages"
> +	default n
> +	help
> +	  A special purpose allocator for storing compressed pages.
> +	  It is designed to store up to two compressed pages per physical
> +	  page.  While this design limits storage density, it has simple and
> +	  deterministic reclaim properties that make it preferable to a higher
> +	  density approach when reclaim will be used.
>  
>  config ZSMALLOC
>  	bool "Memory allocator for compressed pages"
> diff --git a/mm/Makefile b/mm/Makefile
> index 9b75a4d..f64a5d4 100644
> --- a/mm/Makefile
> +++ b/mm/Makefile
> @@ -61,6 +61,7 @@ obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
>  obj-$(CONFIG_CLEANCACHE) += cleancache.o
>  obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
>  obj-$(CONFIG_PAGE_OWNER) += pageowner.o
> +obj-$(CONFIG_ZPOOL)	+= zpool.o
>  obj-$(CONFIG_ZBUD)	+= zbud.o
>  obj-$(CONFIG_ZSMALLOC)	+= zsmalloc.o
>  obj-$(CONFIG_GENERIC_EARLY_IOREMAP) += early_ioremap.o
> diff --git a/mm/zpool.c b/mm/zpool.c
> new file mode 100644
> index 0000000..2bda300
> --- /dev/null
> +++ b/mm/zpool.c
> @@ -0,0 +1,349 @@
> +/*
> + * zpool memory storage api
> + *
> + * Copyright (C) 2014 Dan Streetman
> + *
> + * This is a common frontend for the zbud and zsmalloc memory
> + * storage pool implementations.  Typically, this is used to
> + * store compressed memory.
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/list.h>
> +#include <linux/types.h>
> +#include <linux/mm.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/zpool.h>
> +#include <linux/zbud.h>
> +#include <linux/zsmalloc.h>
> +
> +struct zpool_imp {
> +	void (*destroy)(struct zpool *pool);
> +
> +	int (*malloc)(struct zpool *pool, size_t size, unsigned long *handle);
> +	void (*free)(struct zpool *pool, unsigned long handle);
> +
> +	int (*shrink)(struct zpool *pool, size_t size);
> +
> +	void *(*map)(struct zpool *pool, unsigned long handle,
> +				enum zpool_mapmode mm);
> +	void (*unmap)(struct zpool *pool, unsigned long handle);
> +
> +	u64 (*total_size)(struct zpool *pool);
> +};
> +
> +struct zpool {
> +	char *type;
> +
> +	union {
> +#ifdef CONFIG_ZSMALLOC
> +	struct zs_pool *zsmalloc_pool;
> +#endif
> +#ifdef CONFIG_ZBUD
> +	struct zbud_pool *zbud_pool;
> +#endif
> +	};
> +
> +	struct zpool_imp *imp;
> +	struct zpool_ops *ops;
> +
> +	struct list_head list;
> +};
> +
> +static LIST_HEAD(zpools);
> +static DEFINE_SPINLOCK(zpools_lock);
> +
> +static int zpool_noop_evict(struct zpool *pool, unsigned long handle)
> +{
> +	return -EINVAL;
> +}
> +static struct zpool_ops zpool_noop_ops = {
> +	.evict = zpool_noop_evict
> +};
> +
> +
> +/* zsmalloc */
> +
> +#ifdef CONFIG_ZSMALLOC
> +
> +static void zpool_zsmalloc_destroy(struct zpool *zpool)
> +{
> +	spin_lock(&zpools_lock);
> +	list_del(&zpool->list);
> +	spin_unlock(&zpools_lock);
> +
> +	zs_destroy_pool(zpool->zsmalloc_pool);
> +	kfree(zpool);
> +}
> +
> +static int zpool_zsmalloc_malloc(struct zpool *pool, size_t size,
> +			unsigned long *handle)
> +{
> +	*handle = zs_malloc(pool->zsmalloc_pool, size);
> +	return *handle ? 0 : -1;
> +}
> +
> +static void zpool_zsmalloc_free(struct zpool *pool, unsigned long handle)
> +{
> +	zs_free(pool->zsmalloc_pool, handle);
> +}
> +
> +static int zpool_zsmalloc_shrink(struct zpool *pool, size_t size)
> +{
> +	/* Not yet supported */
> +	return -EINVAL;
> +}
> +
> +static void *zpool_zsmalloc_map(struct zpool *pool, unsigned long handle,
> +			enum zpool_mapmode zpool_mapmode)
> +{
> +	enum zs_mapmode zs_mapmode;
> +
> +	switch (zpool_mapmode) {
> +	case ZPOOL_MM_RO:
> +		zs_mapmode = ZS_MM_RO; break;
> +	case ZPOOL_MM_WO:
> +		zs_mapmode = ZS_MM_WO; break;
> +	case ZPOOL_MM_RW: /* fallthrough */
> +	default:
> +		zs_mapmode = ZS_MM_RW; break;
> +	}
> +	return zs_map_object(pool->zsmalloc_pool, handle, zs_mapmode);
> +}
> +
> +static void zpool_zsmalloc_unmap(struct zpool *pool, unsigned long handle)
> +{
> +	zs_unmap_object(pool->zsmalloc_pool, handle);
> +}
> +
> +static u64 zpool_zsmalloc_total_size(struct zpool *pool)
> +{
> +	return zs_get_total_size_bytes(pool->zsmalloc_pool);
> +}
> +
> +static struct zpool_imp zpool_zsmalloc_imp = {
> +	.destroy = zpool_zsmalloc_destroy,
> +	.malloc = zpool_zsmalloc_malloc,
> +	.free = zpool_zsmalloc_free,
> +	.shrink = zpool_zsmalloc_shrink,
> +	.map = zpool_zsmalloc_map,
> +	.unmap = zpool_zsmalloc_unmap,
> +	.total_size = zpool_zsmalloc_total_size
> +};
> +
> +static struct zpool *zpool_zsmalloc_create(gfp_t flags, struct zpool_ops *ops)
> +{
> +	struct zpool *zpool;
> +
> +	zpool = kmalloc(sizeof(*zpool), GFP_KERNEL);
> +	if (!zpool) {
> +		pr_err("couldn't create zpool - out of memory\n");
> +		return NULL;
> +	}
> +
> +	zpool->zsmalloc_pool = zs_create_pool(flags);
> +	if (!zpool->zsmalloc_pool) {
> +		kfree(zpool);
> +		pr_err("zs_create_pool() failed\n");
> +		return NULL;
> +	}
> +
> +	zpool->type = ZPOOL_TYPE_ZSMALLOC;
> +	zpool->imp = &zpool_zsmalloc_imp;
> +	zpool->ops = &zpool_noop_ops;
> +	spin_lock(&zpools_lock);
> +	list_add(&zpool->list, &zpools);
> +	spin_unlock(&zpools_lock);
> +
> +	return zpool;
> +}
> +
> +#else
> +
> +static struct zpool *zpool_zsmalloc_create(gfp_t flags, struct zpool_ops *ops)
> +{
> +	pr_info("no zsmalloc in this kernel\n");
> +	return NULL;
> +}
> +
> +#endif /* CONFIG_ZSMALLOC */
> +
> +
> +/* zbud */
> +
> +#ifdef CONFIG_ZBUD
> +
> +static void zpool_zbud_destroy(struct zpool *zpool)
> +{
> +	spin_lock(&zpools_lock);
> +	list_del(&zpool->list);
> +	spin_unlock(&zpools_lock);
> +
> +	zbud_destroy_pool(zpool->zbud_pool);
> +	kfree(zpool);
> +}
> +
> +static int zpool_zbud_malloc(struct zpool *pool, size_t size,
> +			unsigned long *handle)
> +{
> +	return zbud_alloc(pool->zbud_pool, size, handle);
> +}
> +
> +static void zpool_zbud_free(struct zpool *pool, unsigned long handle)
> +{
> +	zbud_free(pool->zbud_pool, handle);
> +}
> +
> +static int zpool_zbud_shrink(struct zpool *pool, size_t size)
> +{
> +	return zbud_reclaim_page(pool->zbud_pool, 3);
> +}
> +
> +static void *zpool_zbud_map(struct zpool *pool, unsigned long handle,
> +			enum zpool_mapmode zpool_mapmode)
> +{
> +	return zbud_map(pool->zbud_pool, handle);
> +}
> +
> +static void zpool_zbud_unmap(struct zpool *pool, unsigned long handle)
> +{
> +	zbud_unmap(pool->zbud_pool, handle);
> +}
> +
> +static u64 zpool_zbud_total_size(struct zpool *pool)
> +{
> +	return zbud_get_pool_size(pool->zbud_pool) * PAGE_SIZE;
> +}
> +
> +static int zpool_zbud_evict(struct zbud_pool *zbud_pool, unsigned long handle)
> +{
> +	struct zpool *zpool;
> +
> +	spin_lock(&zpools_lock);
> +	list_for_each_entry(zpool, &zpools, list) {
> +		if (zpool->zbud_pool == zbud_pool) {
> +			spin_unlock(&zpools_lock);
> +			return zpool->ops->evict(zpool, handle);
> +		}
> +	}
> +	spin_unlock(&zpools_lock);
> +	return -EINVAL;
> +}
> +
> +static struct zpool_imp zpool_zbud_imp = {
> +	.destroy = zpool_zbud_destroy,
> +	.malloc = zpool_zbud_malloc,
> +	.free = zpool_zbud_free,
> +	.shrink = zpool_zbud_shrink,
> +	.map = zpool_zbud_map,
> +	.unmap = zpool_zbud_unmap,
> +	.total_size = zpool_zbud_total_size
> +};
> +
> +static struct zbud_ops zpool_zbud_ops = {
> +	.evict = zpool_zbud_evict
> +};
> +
> +static struct zpool *zpool_zbud_create(gfp_t flags, struct zpool_ops *ops)
> +{
> +	struct zpool *zpool;
> +	struct zbud_ops *zbud_ops = (ops ? &zpool_zbud_ops : NULL);
> +
> +	zpool = kmalloc(sizeof(*zpool), GFP_KERNEL);
> +	if (!zpool) {
> +		pr_err("couldn't create zpool - out of memory\n");
> +		return NULL;
> +	}
> +
> +	zpool->zbud_pool = zbud_create_pool(flags, zbud_ops);
> +	if (!zpool->zbud_pool) {
> +		kfree(zpool);
> +		pr_err("zbud_create_pool() failed\n");
> +		return NULL;
> +	}
> +
> +	zpool->type = ZPOOL_TYPE_ZBUD;
> +	zpool->imp = &zpool_zbud_imp;
> +	zpool->ops = (ops ? ops : &zpool_noop_ops);
> +	spin_lock(&zpools_lock);
> +	list_add(&zpool->list, &zpools);
> +	spin_unlock(&zpools_lock);
> +
> +	return zpool;
> +}
> +
> +#else
> +
> +static struct zpool *zpool_zbud_create(gfp_t flags, struct zpool_ops *ops)
> +{
> +	pr_info("no zbud in this kernel\n");
> +	return NULL;
> +}
> +
> +#endif /* CONFIG_ZBUD */
> +
> +
> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
> +			struct zpool_ops *ops)
> +{
> +	struct zpool *pool = NULL;
> +
> +	pr_info("creating pool type %s\n", type);
> +
> +	if (!strcmp(type, ZPOOL_TYPE_ZSMALLOC))
> +		pool = zpool_zsmalloc_create(flags, ops);
> +	else if (!strcmp(type, ZPOOL_TYPE_ZBUD))
> +		pool = zpool_zbud_create(flags, ops);
> +	else
> +		pr_err("unknown type %s\n", type);
> +
> +	if (pool)
> +		pr_info("created %s pool\n", type);
> +	else
> +		pr_err("couldn't create %s pool\n", type);
> +
> +	return pool;
> +}
> +
> +char *zpool_get_type(struct zpool *pool)
> +{
> +	return pool->type;
> +}
> +
> +void zpool_destroy_pool(struct zpool *pool)
> +{
> +	pool->imp->destroy(pool);
> +}
> +
> +int zpool_malloc(struct zpool *pool, size_t size, unsigned long *handle)
> +{
> +	return pool->imp->malloc(pool, size, handle);
> +}
> +
> +void zpool_free(struct zpool *pool, unsigned long handle)
> +{
> +	pool->imp->free(pool, handle);
> +}
> +
> +int zpool_shrink(struct zpool *pool, size_t size)
> +{
> +	return pool->imp->shrink(pool, size);
> +}
> +
> +void *zpool_map_handle(struct zpool *pool, unsigned long handle,
> +			enum zpool_mapmode mapmode)
> +{
> +	return pool->imp->map(pool, handle, mapmode);
> +}
> +
> +void zpool_unmap_handle(struct zpool *pool, unsigned long handle)
> +{
> +	pool->imp->unmap(pool, handle);
> +}
> +
> +u64 zpool_get_total_size(struct zpool *pool)
> +{
> +	return pool->imp->total_size(pool);
> +}
> -- 
> 1.8.3.1
> 

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv2 3/4] mm/zpool: implement common zpool api to zbud/zsmalloc
  2014-05-09  4:13     ` Seth Jennings
@ 2014-05-10 16:06       ` Dan Streetman
  0 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-05-10 16:06 UTC (permalink / raw)
  To: Seth Jennings
  Cc: Minchan Kim, Nitin Gupta, Weijie Yang, Andrew Morton, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Fri, May 9, 2014 at 12:13 AM, Seth Jennings <sjennings@variantweb.net> wrote:
> On Wed, May 07, 2014 at 05:51:35PM -0400, Dan Streetman wrote:
>> Add zpool api.
>>
>> zpool provides an interface for memory storage, typically of compressed
>> memory.  Users can select what backend to use; currently the only
>> implementations are zbud, a low density implementation with up to
>> two compressed pages per storage page, and zsmalloc, a higher density
>> implementation with multiple compressed pages per storage page.
>
> This is the wrong design methinks.  There are a bunch of #ifdefs for each
> driver in the zpool.c code.  Available drivers (zbud, zsmalloc) should
> register _up_ to the zpool layer.  That way the zpool layer doesn't have
> to add a bunch of new code for each driver.
>
> This zpool layer should be _really_ thin, stateless from the user point
> of view, basically just wrapping the driver ops call.

Ok, yeah that does make sense.  The ops param and evict callback might
be a bit difficult but i'll get to that last :-)


>
> New functions for driver registration:
> zpool_register_driver()
> zpool_unregister_driver()
>
> Something like this (note the "void *" type of the pool):
>
> struct zpool_driver_ops {
>         void (*destroy)(void *pool);
>         int (*malloc)(void *pool, size_t size, unsigned long *handle);
>         ....
> }
>
> Each driver can cast the void *pool to the driver pool type on the
> driver side.
>
> struct zpool_driver {
>         char *driver_name;
>         struct zpool_driver *ops;
> }
>
> Then drivers create a struct zpool_driver suitable for them and register
> with zpool_register_driver().
>
> struct zpool {
>         void *driver_pool;
>         struct zpool_driver *driver;
> }
>
> zpool_create() is:
>
> struct zpool *zpool_create(char *driver_name, gfp_t flags, void *ops)
> {
>         [search for backend driver with name driver_name]
>         [alloc new zpool]
>         zpool->driver = driver;
>         zpool->driver_pool = driver->ops->create(flags, ops);
>         return zpool;
> }
>
> A user function like zpool_free() is just:
>
> void zpool_free(struct zpool *pool, unsigned long handle)
> {
>         pool->driver->free(pool->driver_pool, handle);
> }
>
> Hopefully this makes sense.  Obviously, I didn't rewrite this whole
> thing to see how it works end to end so there may be some pitfalls I'm
> not considering.
>
> Seth
>
>>
>> Signed-off-by: Dan Streetman <ddstreet@ieee.org>
>> Cc: Seth Jennings <sjennings@variantweb.net>
>> Cc: Minchan Kim <minchan@kernel.org>
>> Cc: Nitin Gupta <ngupta@vflare.org>
>> Cc: Weijie Yang <weijie.yang@samsung.com>
>> ---
>>
>> Changes since v1 https://lkml.org/lkml/2014/4/19/101
>>  -add some pr_info() during creation and pr_err() on errors
>>  -remove zpool code to call zs_shrink(), since zsmalloc shrinking
>>   was removed from this patchset
>>  -remove fallback; only specified pool type will be tried
>>  -pr_fmt() is defined in zpool to prefix zpool: in any pr_XXX() calls
>>
>>  include/linux/zpool.h | 160 +++++++++++++++++++++++
>>  mm/Kconfig            |  43 ++++---
>>  mm/Makefile           |   1 +
>>  mm/zpool.c            | 349 ++++++++++++++++++++++++++++++++++++++++++++++++++
>>  4 files changed, 535 insertions(+), 18 deletions(-)
>>  create mode 100644 include/linux/zpool.h
>>  create mode 100644 mm/zpool.c
>>
>> diff --git a/include/linux/zpool.h b/include/linux/zpool.h
>> new file mode 100644
>> index 0000000..08f5468
>> --- /dev/null
>> +++ b/include/linux/zpool.h
>> @@ -0,0 +1,160 @@
>> +/*
>> + * zpool memory storage api
>> + *
>> + * Copyright (C) 2014 Dan Streetman
>> + *
>> + * This is a common frontend for the zbud and zsmalloc memory
>> + * storage pool implementations.  Typically, this is used to
>> + * store compressed memory.
>> + */
>> +
>> +#ifndef _ZPOOL_H_
>> +#define _ZPOOL_H_
>> +
>> +struct zpool;
>> +
>> +struct zpool_ops {
>> +     int (*evict)(struct zpool *pool, unsigned long handle);
>> +};
>> +
>> +#define ZPOOL_TYPE_ZSMALLOC "zsmalloc"
>> +#define ZPOOL_TYPE_ZBUD "zbud"
>> +
>> +/*
>> + * Control how a handle is mapped.  It will be ignored if the
>> + * implementation does not support it.  Its use is optional.
>> + * Note that this does not refer to memory protection, it
>> + * refers to how the memory will be copied in/out if copying
>> + * is necessary during mapping; read-write is the safest as
>> + * it copies the existing memory in on map, and copies the
>> + * changed memory back out on unmap.  Write-only does not copy
>> + * in the memory and should only be used for initialization.
>> + * If in doubt, use ZPOOL_MM_DEFAULT which is read-write.
>> + */
>> +enum zpool_mapmode {
>> +     ZPOOL_MM_RW, /* normal read-write mapping */
>> +     ZPOOL_MM_RO, /* read-only (no copy-out at unmap time) */
>> +     ZPOOL_MM_WO, /* write-only (no copy-in at map time) */
>> +
>> +     ZPOOL_MM_DEFAULT = ZPOOL_MM_RW
>> +};
>> +
>> +/**
>> + * zpool_create_pool() - Create a new zpool
>> + * @type     The type of the zpool to create (e.g. zbud, zsmalloc)
>> + * @flags    What GFP flags should be used when the zpool allocates memory.
>> + * @ops              The optional ops callback.
>> + *
>> + * This creates a new zpool of the specified type.  The zpool will use the
>> + * given flags when allocating any memory.  If the ops param is NULL, then
>> + * the created zpool will not be shrinkable.
>> + *
>> + * Returns: New zpool on success, NULL on failure.
>> + */
>> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
>> +                     struct zpool_ops *ops);
>> +
>> +/**
>> + * zpool_get_type() - Get the type of the zpool
>> + * @pool     The zpool to check
>> + *
>> + * This returns the type of the pool, which will match one of the
>> + * ZPOOL_TYPE_* defined values.
>> + *
>> + * Returns: The type of zpool.
>> + */
>> +char *zpool_get_type(struct zpool *pool);
>> +
>> +/**
>> + * zpool_destroy_pool() - Destroy a zpool
>> + * @pool     The zpool to destroy.
>> + *
>> + * This destroys an existing zpool.  The zpool should not be in use.
>> + */
>> +void zpool_destroy_pool(struct zpool *pool);
>> +
>> +/**
>> + * zpool_malloc() - Allocate memory
>> + * @pool     The zpool to allocate from.
>> + * @size     The amount of memory to allocate.
>> + * @handle   Pointer to the handle to set
>> + *
>> + * This allocates the requested amount of memory from the pool.
>> + * The provided @handle will be set to the allocated object handle.
>> + *
>> + * Returns: 0 on success, negative value on error.
>> + */
>> +int zpool_malloc(struct zpool *pool, size_t size, unsigned long *handle);
>> +
>> +/**
>> + * zpool_free() - Free previously allocated memory
>> + * @pool     The zpool that allocated the memory.
>> + * @handle   The handle to the memory to free.
>> + *
>> + * This frees previously allocated memory.  This does not guarantee
>> + * that the pool will actually free memory, only that the memory
>> + * in the pool will become available for use by the pool.
>> + */
>> +void zpool_free(struct zpool *pool, unsigned long handle);
>> +
>> +/**
>> + * zpool_shrink() - Shrink the pool size
>> + * @pool     The zpool to shrink.
>> + * @size     The minimum amount to shrink the pool.
>> + *
>> + * This attempts to shrink the actual memory size of the pool
>> + * by evicting currently used handle(s).  If the pool was
>> + * created with no zpool_ops, or the evict call fails for any
>> + * of the handles, this will fail.
>> + *
>> + * Returns: 0 on success, negative value on error/failure.
>> + */
>> +int zpool_shrink(struct zpool *pool, size_t size);
>> +
>> +/**
>> + * zpool_map_handle() - Map a previously allocated handle into memory
>> + * @pool     The zpool that the handle was allocated from
>> + * @handle   The handle to map
>> + * @mm       How the memory should be mapped
>> + *
>> + * This maps a previously allocated handle into memory.  The @mm
>> + * param indicates to the implemenation how the memory will be
>> + * used, i.e. read-only, write-only, read-write.  If the
>> + * implementation does not support it, the memory will be treated
>> + * as read-write.
>> + *
>> + * This may hold locks, disable interrupts, and/or preemption,
>> + * and the zpool_unmap_handle() must be called to undo those
>> + * actions.  The code that uses the mapped handle should complete
>> + * its operatons on the mapped handle memory quickly and unmap
>> + * as soon as possible.  Multiple handles should not be mapped
>> + * concurrently on a cpu.
>> + *
>> + * Returns: A pointer to the handle's mapped memory area.
>> + */
>> +void *zpool_map_handle(struct zpool *pool, unsigned long handle,
>> +                     enum zpool_mapmode mm);
>> +
>> +/**
>> + * zpool_unmap_handle() - Unmap a previously mapped handle
>> + * @pool     The zpool that the handle was allocated from
>> + * @handle   The handle to unmap
>> + *
>> + * This unmaps a previously mapped handle.  Any locks or other
>> + * actions that the implemenation took in zpool_map_handle()
>> + * will be undone here.  The memory area returned from
>> + * zpool_map_handle() should no longer be used after this.
>> + */
>> +void zpool_unmap_handle(struct zpool *pool, unsigned long handle);
>> +
>> +/**
>> + * zpool_get_total_size() - The total size of the pool
>> + * @pool     The zpool to check
>> + *
>> + * This returns the total size in bytes of the pool.
>> + *
>> + * Returns: Total size of the zpool in bytes.
>> + */
>> +u64 zpool_get_total_size(struct zpool *pool);
>> +
>> +#endif
>> diff --git a/mm/Kconfig b/mm/Kconfig
>> index 30cb6cb..bdb4cb2 100644
>> --- a/mm/Kconfig
>> +++ b/mm/Kconfig
>> @@ -515,21 +515,23 @@ config CMA_DEBUG
>>         processing calls such as dma_alloc_from_contiguous().
>>         This option does not affect warning and error messages.
>>
>> -config ZBUD
>> -     tristate
>> -     default n
>> +config MEM_SOFT_DIRTY
>> +     bool "Track memory changes"
>> +     depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
>> +     select PROC_PAGE_MONITOR
>>       help
>> -       A special purpose allocator for storing compressed pages.
>> -       It is designed to store up to two compressed pages per physical
>> -       page.  While this design limits storage density, it has simple and
>> -       deterministic reclaim properties that make it preferable to a higher
>> -       density approach when reclaim will be used.
>> +       This option enables memory changes tracking by introducing a
>> +       soft-dirty bit on pte-s. This bit it set when someone writes
>> +       into a page just as regular dirty bit, but unlike the latter
>> +       it can be cleared by hands.
>> +
>> +       See Documentation/vm/soft-dirty.txt for more details.
>>
>>  config ZSWAP
>>       bool "Compressed cache for swap pages (EXPERIMENTAL)"
>>       depends on FRONTSWAP && CRYPTO=y
>>       select CRYPTO_LZO
>> -     select ZBUD
>> +     select ZPOOL
>>       default n
>>       help
>>         A lightweight compressed cache for swap pages.  It takes
>> @@ -545,17 +547,22 @@ config ZSWAP
>>         they have not be fully explored on the large set of potential
>>         configurations and workloads that exist.
>>
>> -config MEM_SOFT_DIRTY
>> -     bool "Track memory changes"
>> -     depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
>> -     select PROC_PAGE_MONITOR
>> +config ZPOOL
>> +     tristate "Common API for compressed memory storage"
>> +     default n
>>       help
>> -       This option enables memory changes tracking by introducing a
>> -       soft-dirty bit on pte-s. This bit it set when someone writes
>> -       into a page just as regular dirty bit, but unlike the latter
>> -       it can be cleared by hands.
>> +       Compressed memory storage API.  This allows using either zbud or
>> +       zsmalloc.
>>
>> -       See Documentation/vm/soft-dirty.txt for more details.
>> +config ZBUD
>> +     tristate "Low density storage for compressed pages"
>> +     default n
>> +     help
>> +       A special purpose allocator for storing compressed pages.
>> +       It is designed to store up to two compressed pages per physical
>> +       page.  While this design limits storage density, it has simple and
>> +       deterministic reclaim properties that make it preferable to a higher
>> +       density approach when reclaim will be used.
>>
>>  config ZSMALLOC
>>       bool "Memory allocator for compressed pages"
>> diff --git a/mm/Makefile b/mm/Makefile
>> index 9b75a4d..f64a5d4 100644
>> --- a/mm/Makefile
>> +++ b/mm/Makefile
>> @@ -61,6 +61,7 @@ obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
>>  obj-$(CONFIG_CLEANCACHE) += cleancache.o
>>  obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
>>  obj-$(CONFIG_PAGE_OWNER) += pageowner.o
>> +obj-$(CONFIG_ZPOOL)  += zpool.o
>>  obj-$(CONFIG_ZBUD)   += zbud.o
>>  obj-$(CONFIG_ZSMALLOC)       += zsmalloc.o
>>  obj-$(CONFIG_GENERIC_EARLY_IOREMAP) += early_ioremap.o
>> diff --git a/mm/zpool.c b/mm/zpool.c
>> new file mode 100644
>> index 0000000..2bda300
>> --- /dev/null
>> +++ b/mm/zpool.c
>> @@ -0,0 +1,349 @@
>> +/*
>> + * zpool memory storage api
>> + *
>> + * Copyright (C) 2014 Dan Streetman
>> + *
>> + * This is a common frontend for the zbud and zsmalloc memory
>> + * storage pool implementations.  Typically, this is used to
>> + * store compressed memory.
>> + */
>> +
>> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>> +
>> +#include <linux/list.h>
>> +#include <linux/types.h>
>> +#include <linux/mm.h>
>> +#include <linux/slab.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/zpool.h>
>> +#include <linux/zbud.h>
>> +#include <linux/zsmalloc.h>
>> +
>> +struct zpool_imp {
>> +     void (*destroy)(struct zpool *pool);
>> +
>> +     int (*malloc)(struct zpool *pool, size_t size, unsigned long *handle);
>> +     void (*free)(struct zpool *pool, unsigned long handle);
>> +
>> +     int (*shrink)(struct zpool *pool, size_t size);
>> +
>> +     void *(*map)(struct zpool *pool, unsigned long handle,
>> +                             enum zpool_mapmode mm);
>> +     void (*unmap)(struct zpool *pool, unsigned long handle);
>> +
>> +     u64 (*total_size)(struct zpool *pool);
>> +};
>> +
>> +struct zpool {
>> +     char *type;
>> +
>> +     union {
>> +#ifdef CONFIG_ZSMALLOC
>> +     struct zs_pool *zsmalloc_pool;
>> +#endif
>> +#ifdef CONFIG_ZBUD
>> +     struct zbud_pool *zbud_pool;
>> +#endif
>> +     };
>> +
>> +     struct zpool_imp *imp;
>> +     struct zpool_ops *ops;
>> +
>> +     struct list_head list;
>> +};
>> +
>> +static LIST_HEAD(zpools);
>> +static DEFINE_SPINLOCK(zpools_lock);
>> +
>> +static int zpool_noop_evict(struct zpool *pool, unsigned long handle)
>> +{
>> +     return -EINVAL;
>> +}
>> +static struct zpool_ops zpool_noop_ops = {
>> +     .evict = zpool_noop_evict
>> +};
>> +
>> +
>> +/* zsmalloc */
>> +
>> +#ifdef CONFIG_ZSMALLOC
>> +
>> +static void zpool_zsmalloc_destroy(struct zpool *zpool)
>> +{
>> +     spin_lock(&zpools_lock);
>> +     list_del(&zpool->list);
>> +     spin_unlock(&zpools_lock);
>> +
>> +     zs_destroy_pool(zpool->zsmalloc_pool);
>> +     kfree(zpool);
>> +}
>> +
>> +static int zpool_zsmalloc_malloc(struct zpool *pool, size_t size,
>> +                     unsigned long *handle)
>> +{
>> +     *handle = zs_malloc(pool->zsmalloc_pool, size);
>> +     return *handle ? 0 : -1;
>> +}
>> +
>> +static void zpool_zsmalloc_free(struct zpool *pool, unsigned long handle)
>> +{
>> +     zs_free(pool->zsmalloc_pool, handle);
>> +}
>> +
>> +static int zpool_zsmalloc_shrink(struct zpool *pool, size_t size)
>> +{
>> +     /* Not yet supported */
>> +     return -EINVAL;
>> +}
>> +
>> +static void *zpool_zsmalloc_map(struct zpool *pool, unsigned long handle,
>> +                     enum zpool_mapmode zpool_mapmode)
>> +{
>> +     enum zs_mapmode zs_mapmode;
>> +
>> +     switch (zpool_mapmode) {
>> +     case ZPOOL_MM_RO:
>> +             zs_mapmode = ZS_MM_RO; break;
>> +     case ZPOOL_MM_WO:
>> +             zs_mapmode = ZS_MM_WO; break;
>> +     case ZPOOL_MM_RW: /* fallthrough */
>> +     default:
>> +             zs_mapmode = ZS_MM_RW; break;
>> +     }
>> +     return zs_map_object(pool->zsmalloc_pool, handle, zs_mapmode);
>> +}
>> +
>> +static void zpool_zsmalloc_unmap(struct zpool *pool, unsigned long handle)
>> +{
>> +     zs_unmap_object(pool->zsmalloc_pool, handle);
>> +}
>> +
>> +static u64 zpool_zsmalloc_total_size(struct zpool *pool)
>> +{
>> +     return zs_get_total_size_bytes(pool->zsmalloc_pool);
>> +}
>> +
>> +static struct zpool_imp zpool_zsmalloc_imp = {
>> +     .destroy = zpool_zsmalloc_destroy,
>> +     .malloc = zpool_zsmalloc_malloc,
>> +     .free = zpool_zsmalloc_free,
>> +     .shrink = zpool_zsmalloc_shrink,
>> +     .map = zpool_zsmalloc_map,
>> +     .unmap = zpool_zsmalloc_unmap,
>> +     .total_size = zpool_zsmalloc_total_size
>> +};
>> +
>> +static struct zpool *zpool_zsmalloc_create(gfp_t flags, struct zpool_ops *ops)
>> +{
>> +     struct zpool *zpool;
>> +
>> +     zpool = kmalloc(sizeof(*zpool), GFP_KERNEL);
>> +     if (!zpool) {
>> +             pr_err("couldn't create zpool - out of memory\n");
>> +             return NULL;
>> +     }
>> +
>> +     zpool->zsmalloc_pool = zs_create_pool(flags);
>> +     if (!zpool->zsmalloc_pool) {
>> +             kfree(zpool);
>> +             pr_err("zs_create_pool() failed\n");
>> +             return NULL;
>> +     }
>> +
>> +     zpool->type = ZPOOL_TYPE_ZSMALLOC;
>> +     zpool->imp = &zpool_zsmalloc_imp;
>> +     zpool->ops = &zpool_noop_ops;
>> +     spin_lock(&zpools_lock);
>> +     list_add(&zpool->list, &zpools);
>> +     spin_unlock(&zpools_lock);
>> +
>> +     return zpool;
>> +}
>> +
>> +#else
>> +
>> +static struct zpool *zpool_zsmalloc_create(gfp_t flags, struct zpool_ops *ops)
>> +{
>> +     pr_info("no zsmalloc in this kernel\n");
>> +     return NULL;
>> +}
>> +
>> +#endif /* CONFIG_ZSMALLOC */
>> +
>> +
>> +/* zbud */
>> +
>> +#ifdef CONFIG_ZBUD
>> +
>> +static void zpool_zbud_destroy(struct zpool *zpool)
>> +{
>> +     spin_lock(&zpools_lock);
>> +     list_del(&zpool->list);
>> +     spin_unlock(&zpools_lock);
>> +
>> +     zbud_destroy_pool(zpool->zbud_pool);
>> +     kfree(zpool);
>> +}
>> +
>> +static int zpool_zbud_malloc(struct zpool *pool, size_t size,
>> +                     unsigned long *handle)
>> +{
>> +     return zbud_alloc(pool->zbud_pool, size, handle);
>> +}
>> +
>> +static void zpool_zbud_free(struct zpool *pool, unsigned long handle)
>> +{
>> +     zbud_free(pool->zbud_pool, handle);
>> +}
>> +
>> +static int zpool_zbud_shrink(struct zpool *pool, size_t size)
>> +{
>> +     return zbud_reclaim_page(pool->zbud_pool, 3);
>> +}
>> +
>> +static void *zpool_zbud_map(struct zpool *pool, unsigned long handle,
>> +                     enum zpool_mapmode zpool_mapmode)
>> +{
>> +     return zbud_map(pool->zbud_pool, handle);
>> +}
>> +
>> +static void zpool_zbud_unmap(struct zpool *pool, unsigned long handle)
>> +{
>> +     zbud_unmap(pool->zbud_pool, handle);
>> +}
>> +
>> +static u64 zpool_zbud_total_size(struct zpool *pool)
>> +{
>> +     return zbud_get_pool_size(pool->zbud_pool) * PAGE_SIZE;
>> +}
>> +
>> +static int zpool_zbud_evict(struct zbud_pool *zbud_pool, unsigned long handle)
>> +{
>> +     struct zpool *zpool;
>> +
>> +     spin_lock(&zpools_lock);
>> +     list_for_each_entry(zpool, &zpools, list) {
>> +             if (zpool->zbud_pool == zbud_pool) {
>> +                     spin_unlock(&zpools_lock);
>> +                     return zpool->ops->evict(zpool, handle);
>> +             }
>> +     }
>> +     spin_unlock(&zpools_lock);
>> +     return -EINVAL;
>> +}
>> +
>> +static struct zpool_imp zpool_zbud_imp = {
>> +     .destroy = zpool_zbud_destroy,
>> +     .malloc = zpool_zbud_malloc,
>> +     .free = zpool_zbud_free,
>> +     .shrink = zpool_zbud_shrink,
>> +     .map = zpool_zbud_map,
>> +     .unmap = zpool_zbud_unmap,
>> +     .total_size = zpool_zbud_total_size
>> +};
>> +
>> +static struct zbud_ops zpool_zbud_ops = {
>> +     .evict = zpool_zbud_evict
>> +};
>> +
>> +static struct zpool *zpool_zbud_create(gfp_t flags, struct zpool_ops *ops)
>> +{
>> +     struct zpool *zpool;
>> +     struct zbud_ops *zbud_ops = (ops ? &zpool_zbud_ops : NULL);
>> +
>> +     zpool = kmalloc(sizeof(*zpool), GFP_KERNEL);
>> +     if (!zpool) {
>> +             pr_err("couldn't create zpool - out of memory\n");
>> +             return NULL;
>> +     }
>> +
>> +     zpool->zbud_pool = zbud_create_pool(flags, zbud_ops);
>> +     if (!zpool->zbud_pool) {
>> +             kfree(zpool);
>> +             pr_err("zbud_create_pool() failed\n");
>> +             return NULL;
>> +     }
>> +
>> +     zpool->type = ZPOOL_TYPE_ZBUD;
>> +     zpool->imp = &zpool_zbud_imp;
>> +     zpool->ops = (ops ? ops : &zpool_noop_ops);
>> +     spin_lock(&zpools_lock);
>> +     list_add(&zpool->list, &zpools);
>> +     spin_unlock(&zpools_lock);
>> +
>> +     return zpool;
>> +}
>> +
>> +#else
>> +
>> +static struct zpool *zpool_zbud_create(gfp_t flags, struct zpool_ops *ops)
>> +{
>> +     pr_info("no zbud in this kernel\n");
>> +     return NULL;
>> +}
>> +
>> +#endif /* CONFIG_ZBUD */
>> +
>> +
>> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
>> +                     struct zpool_ops *ops)
>> +{
>> +     struct zpool *pool = NULL;
>> +
>> +     pr_info("creating pool type %s\n", type);
>> +
>> +     if (!strcmp(type, ZPOOL_TYPE_ZSMALLOC))
>> +             pool = zpool_zsmalloc_create(flags, ops);
>> +     else if (!strcmp(type, ZPOOL_TYPE_ZBUD))
>> +             pool = zpool_zbud_create(flags, ops);
>> +     else
>> +             pr_err("unknown type %s\n", type);
>> +
>> +     if (pool)
>> +             pr_info("created %s pool\n", type);
>> +     else
>> +             pr_err("couldn't create %s pool\n", type);
>> +
>> +     return pool;
>> +}
>> +
>> +char *zpool_get_type(struct zpool *pool)
>> +{
>> +     return pool->type;
>> +}
>> +
>> +void zpool_destroy_pool(struct zpool *pool)
>> +{
>> +     pool->imp->destroy(pool);
>> +}
>> +
>> +int zpool_malloc(struct zpool *pool, size_t size, unsigned long *handle)
>> +{
>> +     return pool->imp->malloc(pool, size, handle);
>> +}
>> +
>> +void zpool_free(struct zpool *pool, unsigned long handle)
>> +{
>> +     pool->imp->free(pool, handle);
>> +}
>> +
>> +int zpool_shrink(struct zpool *pool, size_t size)
>> +{
>> +     return pool->imp->shrink(pool, size);
>> +}
>> +
>> +void *zpool_map_handle(struct zpool *pool, unsigned long handle,
>> +                     enum zpool_mapmode mapmode)
>> +{
>> +     return pool->imp->map(pool, handle, mapmode);
>> +}
>> +
>> +void zpool_unmap_handle(struct zpool *pool, unsigned long handle)
>> +{
>> +     pool->imp->unmap(pool, handle);
>> +}
>> +
>> +u64 zpool_get_total_size(struct zpool *pool)
>> +{
>> +     return pool->imp->total_size(pool);
>> +}
>> --
>> 1.8.3.1
>>

^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv3 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc
  2014-05-07 21:51 ` [PATCHv2 0/4] mm/zpool: " Dan Streetman
                     ` (3 preceding siblings ...)
  2014-05-07 21:51   ` [PATCHv2 4/4] mm/zswap: update zswap to use zpool Dan Streetman
@ 2014-05-24 19:06   ` Dan Streetman
  2014-05-24 19:06     ` [PATCHv2 1/6] mm/zbud: zbud_alloc() minor param change Dan Streetman
                       ` (7 more replies)
  4 siblings, 8 replies; 65+ messages in thread
From: Dan Streetman @ 2014-05-24 19:06 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

In order to allow zswap users to choose between zbud and zsmalloc for
the compressed storage pool, this patch set adds a new api "zpool" that
provides an interface to both zbud and zsmalloc.  Only minor changes
to zbud's interface were needed.  This does not include implementing
shrinking in zsmalloc, which will be sent separately.

I believe Seth originally was using zsmalloc for swap, but there were
concerns about how significant the impact of shrinking zsmalloc would
be when zswap had to start reclaiming pages.  That still may be an
issue, but this at least allows users to choose themselves whether
they want a lower-density or higher-density compressed storage medium.
At least for situations where zswap reclaim is never or rarely reached,
it probably makes sense to use the higher density of zsmalloc.

Note this patch set does not change zram to use zpool, although that
change should be possible as well.

---

Changes since v2 : https://lkml.org/lkml/2014/5/7/927
  -Change zpool to use driver registration instead of hardcoding
   implementations
  -Add module use counting in zbud/zsmalloc

Changes since v1 https://lkml.org/lkml/2014/4/19/97
 -remove zsmalloc shrinking
 -change zbud size param type from unsigned int to size_t
 -remove zpool fallback creation
 -zswap manually falls back to zbud if specified type fails


Dan Streetman (6):
  mm/zbud: zbud_alloc() minor param change
  mm/zbud: change zbud_alloc size type to size_t
  mm/zpool: implement common zpool api to zbud/zsmalloc
  mm/zpool: zbud/zsmalloc implement zpool
  mm/zpool: update zswap to use zpool
  mm/zpool: prevent zbud/zsmalloc from unloading when used

 include/linux/zbud.h  |   2 +-
 include/linux/zpool.h | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++
 mm/Kconfig            |  43 +++++-----
 mm/Makefile           |   1 +
 mm/zbud.c             | 113 ++++++++++++++++++++++----
 mm/zpool.c            | 197 ++++++++++++++++++++++++++++++++++++++++++++++
 mm/zsmalloc.c         |  86 ++++++++++++++++++++
 mm/zswap.c            |  76 ++++++++++--------
 8 files changed, 668 insertions(+), 64 deletions(-)
 create mode 100644 include/linux/zpool.h
 create mode 100644 mm/zpool.c

-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv2 1/6] mm/zbud: zbud_alloc() minor param change
  2014-05-24 19:06   ` [PATCHv3 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
@ 2014-05-24 19:06     ` Dan Streetman
  2014-05-24 19:06     ` [PATCH 2/6] mm/zbud: change zbud_alloc size type to size_t Dan Streetman
                       ` (6 subsequent siblings)
  7 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-05-24 19:06 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Change zbud to store gfp_t flags passed at pool creation to use for
each alloc; this allows the api to be closer to the existing zsmalloc
interface, and the only current zbud user (zswap) uses the same gfp
flags for all allocs.  Update zswap to use changed interface.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Acked-by: Seth Jennings <sjennings@variantweb.net>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

No change since v2 : https://lkml.org/lkml/2014/5/7/727

Changes since v1 https://lkml.org/lkml/2014/4/19/98
 -context changes only; zbud_alloc parameter type changed since
  last patch

 include/linux/zbud.h |  2 +-
 mm/zbud.c            | 27 +++++++++++++++------------
 mm/zswap.c           |  6 +++---
 3 files changed, 19 insertions(+), 16 deletions(-)

diff --git a/include/linux/zbud.h b/include/linux/zbud.h
index 13af0d4..0b2534e 100644
--- a/include/linux/zbud.h
+++ b/include/linux/zbud.h
@@ -11,7 +11,7 @@ struct zbud_ops {
 
 struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops);
 void zbud_destroy_pool(struct zbud_pool *pool);
-int zbud_alloc(struct zbud_pool *pool, unsigned int size, gfp_t gfp,
+int zbud_alloc(struct zbud_pool *pool, unsigned int size,
 	unsigned long *handle);
 void zbud_free(struct zbud_pool *pool, unsigned long handle);
 int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries);
diff --git a/mm/zbud.c b/mm/zbud.c
index 01df13a..847c01c 100644
--- a/mm/zbud.c
+++ b/mm/zbud.c
@@ -94,6 +94,7 @@ struct zbud_pool {
 	struct list_head lru;
 	u64 pages_nr;
 	struct zbud_ops *ops;
+	gfp_t gfp;
 };
 
 /*
@@ -193,9 +194,12 @@ static int num_free_chunks(struct zbud_header *zhdr)
 *****************/
 /**
  * zbud_create_pool() - create a new zbud pool
- * @gfp:	gfp flags when allocating the zbud pool structure
+ * @gfp:	gfp flags when growing the pool
  * @ops:	user-defined operations for the zbud pool
  *
+ * gfp should not set __GFP_HIGHMEM as highmem pages cannot be used
+ * as zbud pool pages.
+ *
  * Return: pointer to the new zbud pool or NULL if the metadata allocation
  * failed.
  */
@@ -204,7 +208,9 @@ struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops)
 	struct zbud_pool *pool;
 	int i;
 
-	pool = kmalloc(sizeof(struct zbud_pool), gfp);
+	if (gfp & __GFP_HIGHMEM)
+		return NULL;
+	pool = kmalloc(sizeof(struct zbud_pool), GFP_KERNEL);
 	if (!pool)
 		return NULL;
 	spin_lock_init(&pool->lock);
@@ -214,6 +220,7 @@ struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops)
 	INIT_LIST_HEAD(&pool->lru);
 	pool->pages_nr = 0;
 	pool->ops = ops;
+	pool->gfp = gfp;
 	return pool;
 }
 
@@ -232,7 +239,6 @@ void zbud_destroy_pool(struct zbud_pool *pool)
  * zbud_alloc() - allocates a region of a given size
  * @pool:	zbud pool from which to allocate
  * @size:	size in bytes of the desired allocation
- * @gfp:	gfp flags used if the pool needs to grow
  * @handle:	handle of the new allocation
  *
  * This function will attempt to find a free region in the pool large enough to
@@ -240,14 +246,11 @@ void zbud_destroy_pool(struct zbud_pool *pool)
  * performed first. If no suitable free region is found, then a new page is
  * allocated and added to the pool to satisfy the request.
  *
- * gfp should not set __GFP_HIGHMEM as highmem pages cannot be used
- * as zbud pool pages.
- *
- * Return: 0 if success and handle is set, otherwise -EINVAL if the size or
- * gfp arguments are invalid or -ENOMEM if the pool was unable to allocate
- * a new page.
+ * Return: 0 if success and @handle is set, -ENOSPC if the @size is too large,
+ * -EINVAL if the @size is 0, or -ENOMEM if the pool was unable to
+ * allocate a new page.
  */
-int zbud_alloc(struct zbud_pool *pool, unsigned int size, gfp_t gfp,
+int zbud_alloc(struct zbud_pool *pool, unsigned int size,
 			unsigned long *handle)
 {
 	int chunks, i, freechunks;
@@ -255,7 +258,7 @@ int zbud_alloc(struct zbud_pool *pool, unsigned int size, gfp_t gfp,
 	enum buddy bud;
 	struct page *page;
 
-	if (!size || (gfp & __GFP_HIGHMEM))
+	if (!size)
 		return -EINVAL;
 	if (size > PAGE_SIZE - ZHDR_SIZE_ALIGNED - CHUNK_SIZE)
 		return -ENOSPC;
@@ -279,7 +282,7 @@ int zbud_alloc(struct zbud_pool *pool, unsigned int size, gfp_t gfp,
 
 	/* Couldn't find unbuddied zbud page, create new one */
 	spin_unlock(&pool->lock);
-	page = alloc_page(gfp);
+	page = alloc_page(pool->gfp);
 	if (!page)
 		return -ENOMEM;
 	spin_lock(&pool->lock);
diff --git a/mm/zswap.c b/mm/zswap.c
index aeaef0f..1cc6770 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -679,8 +679,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 
 	/* store */
 	len = dlen + sizeof(struct zswap_header);
-	ret = zbud_alloc(zswap_pool, len, __GFP_NORETRY | __GFP_NOWARN,
-		&handle);
+	ret = zbud_alloc(zswap_pool, len, &handle);
 	if (ret == -ENOSPC) {
 		zswap_reject_compress_poor++;
 		goto freepage;
@@ -900,7 +899,8 @@ static int __init init_zswap(void)
 
 	pr_info("loading zswap\n");
 
-	zswap_pool = zbud_create_pool(GFP_KERNEL, &zswap_zbud_ops);
+	zswap_pool = zbud_create_pool(__GFP_NORETRY | __GFP_NOWARN,
+			&zswap_zbud_ops);
 	if (!zswap_pool) {
 		pr_err("zbud pool creation failed\n");
 		goto error;
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 2/6] mm/zbud: change zbud_alloc size type to size_t
  2014-05-24 19:06   ` [PATCHv3 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
  2014-05-24 19:06     ` [PATCHv2 1/6] mm/zbud: zbud_alloc() minor param change Dan Streetman
@ 2014-05-24 19:06     ` Dan Streetman
  2014-05-24 19:06     ` [PATCHv3 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
                       ` (5 subsequent siblings)
  7 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-05-24 19:06 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Change the type of the zbud_alloc() size param from unsigned int
to size_t.

Technically, this should not make any difference, as the zbud
implementation already restricts the size to well within either
type's limits; but as zsmalloc (and kmalloc) use size_t, and
zpool will use size_t, this brings the size parameter type
in line with zsmalloc/zpool.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Acked-by: Seth Jennings <sjennings@variantweb.net>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

No change since v1 : https://lkml.org/lkml/2014/5/7/757

 include/linux/zbud.h | 2 +-
 mm/zbud.c            | 5 ++---
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/include/linux/zbud.h b/include/linux/zbud.h
index 0b2534e..1e9cb57 100644
--- a/include/linux/zbud.h
+++ b/include/linux/zbud.h
@@ -11,7 +11,7 @@ struct zbud_ops {
 
 struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops);
 void zbud_destroy_pool(struct zbud_pool *pool);
-int zbud_alloc(struct zbud_pool *pool, unsigned int size,
+int zbud_alloc(struct zbud_pool *pool, size_t size,
 	unsigned long *handle);
 void zbud_free(struct zbud_pool *pool, unsigned long handle);
 int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries);
diff --git a/mm/zbud.c b/mm/zbud.c
index 847c01c..dd13665 100644
--- a/mm/zbud.c
+++ b/mm/zbud.c
@@ -123,7 +123,7 @@ enum buddy {
 };
 
 /* Converts an allocation size in bytes to size in zbud chunks */
-static int size_to_chunks(int size)
+static int size_to_chunks(size_t size)
 {
 	return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT;
 }
@@ -250,8 +250,7 @@ void zbud_destroy_pool(struct zbud_pool *pool)
  * -EINVAL if the @size is 0, or -ENOMEM if the pool was unable to
  * allocate a new page.
  */
-int zbud_alloc(struct zbud_pool *pool, unsigned int size,
-			unsigned long *handle)
+int zbud_alloc(struct zbud_pool *pool, size_t size, unsigned long *handle)
 {
 	int chunks, i, freechunks;
 	struct zbud_header *zhdr = NULL;
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCHv3 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc
  2014-05-24 19:06   ` [PATCHv3 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
  2014-05-24 19:06     ` [PATCHv2 1/6] mm/zbud: zbud_alloc() minor param change Dan Streetman
  2014-05-24 19:06     ` [PATCH 2/6] mm/zbud: change zbud_alloc size type to size_t Dan Streetman
@ 2014-05-24 19:06     ` Dan Streetman
  2014-05-27 22:06       ` Seth Jennings
  2014-05-24 19:06     ` [PATCH 4/6] mm/zpool: zbud/zsmalloc implement zpool Dan Streetman
                       ` (4 subsequent siblings)
  7 siblings, 1 reply; 65+ messages in thread
From: Dan Streetman @ 2014-05-24 19:06 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Add zpool api.

zpool provides an interface for memory storage, typically of compressed
memory.  Users can select what backend to use; currently the only
implementations are zbud, a low density implementation with up to
two compressed pages per storage page, and zsmalloc, a higher density
implementation with multiple compressed pages per storage page.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Cc: Seth Jennings <sjennings@variantweb.net>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Nitin Gupta <ngupta@vflare.org>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

Note this patch set is against the mmotm tree at
git://git.cmpxchg.org/linux-mmotm.git
This patch may need context changes to the -next or other trees.

Changes since v2 : https://lkml.org/lkml/2014/5/7/733
  -Remove hardcoded zbud/zsmalloc implementations
  -Add driver (un)register functions

Changes since v1 https://lkml.org/lkml/2014/4/19/101
 -add some pr_info() during creation and pr_err() on errors
 -remove zpool code to call zs_shrink(), since zsmalloc shrinking
  was removed from this patchset
 -remove fallback; only specified pool type will be tried
 -pr_fmt() is defined in zpool to prefix zpool: in any pr_XXX() calls


 include/linux/zpool.h | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++
 mm/Kconfig            |  41 ++++++----
 mm/Makefile           |   1 +
 mm/zpool.c            | 197 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 436 insertions(+), 17 deletions(-)
 create mode 100644 include/linux/zpool.h
 create mode 100644 mm/zpool.c

diff --git a/include/linux/zpool.h b/include/linux/zpool.h
new file mode 100644
index 0000000..699ac9b
--- /dev/null
+++ b/include/linux/zpool.h
@@ -0,0 +1,214 @@
+/*
+ * zpool memory storage api
+ *
+ * Copyright (C) 2014 Dan Streetman
+ *
+ * This is a common frontend for the zbud and zsmalloc memory
+ * storage pool implementations.  Typically, this is used to
+ * store compressed memory.
+ */
+
+#ifndef _ZPOOL_H_
+#define _ZPOOL_H_
+
+struct zpool;
+
+struct zpool_ops {
+	int (*evict)(struct zpool *pool, unsigned long handle);
+};
+
+/*
+ * Control how a handle is mapped.  It will be ignored if the
+ * implementation does not support it.  Its use is optional.
+ * Note that this does not refer to memory protection, it
+ * refers to how the memory will be copied in/out if copying
+ * is necessary during mapping; read-write is the safest as
+ * it copies the existing memory in on map, and copies the
+ * changed memory back out on unmap.  Write-only does not copy
+ * in the memory and should only be used for initialization.
+ * If in doubt, use ZPOOL_MM_DEFAULT which is read-write.
+ */
+enum zpool_mapmode {
+	ZPOOL_MM_RW, /* normal read-write mapping */
+	ZPOOL_MM_RO, /* read-only (no copy-out at unmap time) */
+	ZPOOL_MM_WO, /* write-only (no copy-in at map time) */
+
+	ZPOOL_MM_DEFAULT = ZPOOL_MM_RW
+};
+
+/**
+ * zpool_create_pool() - Create a new zpool
+ * @type	The type of the zpool to create (e.g. zbud, zsmalloc)
+ * @flags	What GFP flags should be used when the zpool allocates memory.
+ * @ops		The optional ops callback.
+ *
+ * This creates a new zpool of the specified type.  The zpool will use the
+ * given flags when allocating any memory.  If the ops param is NULL, then
+ * the created zpool will not be shrinkable.
+ *
+ * Returns: New zpool on success, NULL on failure.
+ */
+struct zpool *zpool_create_pool(char *type, gfp_t flags,
+			struct zpool_ops *ops);
+
+/**
+ * zpool_get_type() - Get the type of the zpool
+ * @pool	The zpool to check
+ *
+ * This returns the type of the pool.
+ *
+ * Returns: The type of zpool.
+ */
+char *zpool_get_type(struct zpool *pool);
+
+/**
+ * zpool_destroy_pool() - Destroy a zpool
+ * @pool	The zpool to destroy.
+ *
+ * This destroys an existing zpool.  The zpool should not be in use.
+ */
+void zpool_destroy_pool(struct zpool *pool);
+
+/**
+ * zpool_malloc() - Allocate memory
+ * @pool	The zpool to allocate from.
+ * @size	The amount of memory to allocate.
+ * @handle	Pointer to the handle to set
+ *
+ * This allocates the requested amount of memory from the pool.
+ * The provided @handle will be set to the allocated object handle.
+ *
+ * Returns: 0 on success, negative value on error.
+ */
+int zpool_malloc(struct zpool *pool, size_t size, unsigned long *handle);
+
+/**
+ * zpool_free() - Free previously allocated memory
+ * @pool	The zpool that allocated the memory.
+ * @handle	The handle to the memory to free.
+ *
+ * This frees previously allocated memory.  This does not guarantee
+ * that the pool will actually free memory, only that the memory
+ * in the pool will become available for use by the pool.
+ */
+void zpool_free(struct zpool *pool, unsigned long handle);
+
+/**
+ * zpool_shrink() - Shrink the pool size
+ * @pool	The zpool to shrink.
+ * @size	The minimum amount to shrink the pool.
+ *
+ * This attempts to shrink the actual memory size of the pool
+ * by evicting currently used handle(s).  If the pool was
+ * created with no zpool_ops, or the evict call fails for any
+ * of the handles, this will fail.
+ *
+ * Returns: 0 on success, negative value on error/failure.
+ */
+int zpool_shrink(struct zpool *pool, size_t size);
+
+/**
+ * zpool_map_handle() - Map a previously allocated handle into memory
+ * @pool	The zpool that the handle was allocated from
+ * @handle	The handle to map
+ * @mm	How the memory should be mapped
+ *
+ * This maps a previously allocated handle into memory.  The @mm
+ * param indicates to the implemenation how the memory will be
+ * used, i.e. read-only, write-only, read-write.  If the
+ * implementation does not support it, the memory will be treated
+ * as read-write.
+ *
+ * This may hold locks, disable interrupts, and/or preemption,
+ * and the zpool_unmap_handle() must be called to undo those
+ * actions.  The code that uses the mapped handle should complete
+ * its operatons on the mapped handle memory quickly and unmap
+ * as soon as possible.  Multiple handles should not be mapped
+ * concurrently on a cpu.
+ *
+ * Returns: A pointer to the handle's mapped memory area.
+ */
+void *zpool_map_handle(struct zpool *pool, unsigned long handle,
+			enum zpool_mapmode mm);
+
+/**
+ * zpool_unmap_handle() - Unmap a previously mapped handle
+ * @pool	The zpool that the handle was allocated from
+ * @handle	The handle to unmap
+ *
+ * This unmaps a previously mapped handle.  Any locks or other
+ * actions that the implemenation took in zpool_map_handle()
+ * will be undone here.  The memory area returned from
+ * zpool_map_handle() should no longer be used after this.
+ */
+void zpool_unmap_handle(struct zpool *pool, unsigned long handle);
+
+/**
+ * zpool_get_total_size() - The total size of the pool
+ * @pool	The zpool to check
+ *
+ * This returns the total size in bytes of the pool.
+ *
+ * Returns: Total size of the zpool in bytes.
+ */
+u64 zpool_get_total_size(struct zpool *pool);
+
+
+/**
+ * struct zpool_driver - driver implementation for zpool
+ * @type:	name of the driver.
+ * @list:	entry in the list of zpool drivers.
+ * @create:	create a new pool.
+ * @destroy:	destroy a pool.
+ * @malloc:	allocate mem from a pool.
+ * @free:	free mem from a pool.
+ * @shrink:	shrink the pool.
+ * @map:	map a handle.
+ * @unmap:	unmap a handle.
+ * @total_size:	get total size of a pool.
+ *
+ * This is created by a zpool implementation and registered
+ * with zpool.
+ */
+struct zpool_driver {
+	char *type;
+	struct list_head list;
+
+	void *(*create)(gfp_t gfp, struct zpool_ops *ops);
+	void (*destroy)(void *pool);
+
+	int (*malloc)(void *pool, size_t size, unsigned long *handle);
+	void (*free)(void *pool, unsigned long handle);
+
+	int (*shrink)(void *pool, size_t size);
+
+	void *(*map)(void *pool, unsigned long handle,
+				enum zpool_mapmode mm);
+	void (*unmap)(void *pool, unsigned long handle);
+
+	u64 (*total_size)(void *pool);
+};
+
+/**
+ * zpool_register_driver() - register a zpool implementation.
+ * @driver:	driver to register
+ */
+void zpool_register_driver(struct zpool_driver *driver);
+
+/**
+ * zpool_unregister_driver() - unregister a zpool implementation.
+ * @driver:	driver to unregister.
+ */
+void zpool_unregister_driver(struct zpool_driver *driver);
+
+/**
+ * zpool_evict() - evict callback from a zpool implementation.
+ * @pool:	pool to evict from.
+ * @handle:	handle to evict.
+ *
+ * This can be used by zpool implementations to call the
+ * user's evict zpool_ops struct evict callback.
+ */
+int zpool_evict(void *pool, unsigned long handle);
+
+#endif
diff --git a/mm/Kconfig b/mm/Kconfig
index 7511b4a..00f7720 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -515,15 +515,17 @@ config CMA_DEBUG
 	  processing calls such as dma_alloc_from_contiguous().
 	  This option does not affect warning and error messages.
 
-config ZBUD
-	tristate
-	default n
+config MEM_SOFT_DIRTY
+	bool "Track memory changes"
+	depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
+	select PROC_PAGE_MONITOR
 	help
-	  A special purpose allocator for storing compressed pages.
-	  It is designed to store up to two compressed pages per physical
-	  page.  While this design limits storage density, it has simple and
-	  deterministic reclaim properties that make it preferable to a higher
-	  density approach when reclaim will be used.
+	  This option enables memory changes tracking by introducing a
+	  soft-dirty bit on pte-s. This bit it set when someone writes
+	  into a page just as regular dirty bit, but unlike the latter
+	  it can be cleared by hands.
+
+	  See Documentation/vm/soft-dirty.txt for more details.
 
 config ZSWAP
 	bool "Compressed cache for swap pages (EXPERIMENTAL)"
@@ -545,17 +547,22 @@ config ZSWAP
 	  they have not be fully explored on the large set of potential
 	  configurations and workloads that exist.
 
-config MEM_SOFT_DIRTY
-	bool "Track memory changes"
-	depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
-	select PROC_PAGE_MONITOR
+config ZPOOL
+	tristate "Common API for compressed memory storage"
+	default n
 	help
-	  This option enables memory changes tracking by introducing a
-	  soft-dirty bit on pte-s. This bit it set when someone writes
-	  into a page just as regular dirty bit, but unlike the latter
-	  it can be cleared by hands.
+	  Compressed memory storage API.  This allows using either zbud or
+	  zsmalloc.
 
-	  See Documentation/vm/soft-dirty.txt for more details.
+config ZBUD
+	tristate "Low density storage for compressed pages"
+	default n
+	help
+	  A special purpose allocator for storing compressed pages.
+	  It is designed to store up to two compressed pages per physical
+	  page.  While this design limits storage density, it has simple and
+	  deterministic reclaim properties that make it preferable to a higher
+	  density approach when reclaim will be used.
 
 config ZSMALLOC
 	tristate "Memory allocator for compressed pages"
diff --git a/mm/Makefile b/mm/Makefile
index 2b6fff2..759db04 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
 obj-$(CONFIG_CLEANCACHE) += cleancache.o
 obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
 obj-$(CONFIG_PAGE_OWNER) += pageowner.o
+obj-$(CONFIG_ZPOOL)	+= zpool.o
 obj-$(CONFIG_ZBUD)	+= zbud.o
 obj-$(CONFIG_ZSMALLOC)	+= zsmalloc.o
 obj-$(CONFIG_GENERIC_EARLY_IOREMAP) += early_ioremap.o
diff --git a/mm/zpool.c b/mm/zpool.c
new file mode 100644
index 0000000..89ed71f
--- /dev/null
+++ b/mm/zpool.c
@@ -0,0 +1,197 @@
+/*
+ * zpool memory storage api
+ *
+ * Copyright (C) 2014 Dan Streetman
+ *
+ * This is a common frontend for memory storage pool implementations.
+ * Typically, this is used to store compressed memory.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/zpool.h>
+
+struct zpool {
+	char *type;
+
+	struct zpool_driver *driver;
+	void *pool;
+	struct zpool_ops *ops;
+
+	struct list_head list;
+};
+
+static LIST_HEAD(drivers_head);
+static DEFINE_SPINLOCK(drivers_lock);
+
+static LIST_HEAD(pools_head);
+static DEFINE_SPINLOCK(pools_lock);
+
+void zpool_register_driver(struct zpool_driver *driver)
+{
+	spin_lock(&drivers_lock);
+	list_add(&driver->list, &drivers_head);
+	spin_unlock(&drivers_lock);
+}
+EXPORT_SYMBOL(zpool_register_driver);
+
+void zpool_unregister_driver(struct zpool_driver *driver)
+{
+	spin_lock(&drivers_lock);
+	list_del(&driver->list);
+	spin_unlock(&drivers_lock);
+}
+EXPORT_SYMBOL(zpool_unregister_driver);
+
+int zpool_evict(void *pool, unsigned long handle)
+{
+	struct zpool *zpool;
+
+	spin_lock(&pools_lock);
+	list_for_each_entry(zpool, &pools_head, list) {
+		if (zpool->pool == pool) {
+			spin_unlock(&pools_lock);
+			if (!zpool->ops || !zpool->ops->evict)
+				return -EINVAL;
+			return zpool->ops->evict(zpool, handle);
+		}
+	}
+	spin_unlock(&pools_lock);
+
+	return -ENOENT;
+}
+EXPORT_SYMBOL(zpool_evict);
+
+static struct zpool_driver *zpool_get_driver(char *type)
+{
+	struct zpool_driver *driver;
+
+	assert_spin_locked(&drivers_lock);
+	list_for_each_entry(driver, &drivers_head, list) {
+		if (!strcmp(driver->type, type))
+			return driver;
+	}
+
+	return NULL;
+}
+
+struct zpool *zpool_create_pool(char *type, gfp_t flags,
+			struct zpool_ops *ops)
+{
+	struct zpool_driver *driver;
+	struct zpool *zpool;
+
+	pr_info("creating pool type %s\n", type);
+
+	spin_lock(&drivers_lock);
+	driver = zpool_get_driver(type);
+	spin_unlock(&drivers_lock);
+
+	if (!driver) {
+		request_module(type);
+		spin_lock(&drivers_lock);
+		driver = zpool_get_driver(type);
+		spin_unlock(&drivers_lock);
+	}
+
+	if (!driver) {
+		pr_err("no driver for type %s\n", type);
+		return NULL;
+	}
+
+	zpool = kmalloc(sizeof(*zpool), GFP_KERNEL);
+	if (!zpool) {
+		pr_err("couldn't create zpool - out of memory\n");
+		return NULL;
+	}
+
+	zpool->type = driver->type;
+	zpool->driver = driver;
+	zpool->pool = driver->create(flags, ops);
+	zpool->ops = ops;
+
+	if (!zpool->pool) {
+		pr_err("couldn't create %s pool\n", type);
+		kfree(zpool);
+		return NULL;
+	}
+
+	pr_info("created %s pool\n", type);
+
+	spin_lock(&pools_lock);
+	list_add(&zpool->list, &pools_head);
+	spin_unlock(&pools_lock);
+
+	return zpool;
+}
+
+void zpool_destroy_pool(struct zpool *zpool)
+{
+	pr_info("destroying pool type %s\n", zpool->type);
+
+	spin_lock(&pools_lock);
+	list_del(&zpool->list);
+	spin_unlock(&pools_lock);
+	zpool->driver->destroy(zpool->pool);
+	kfree(zpool);
+}
+
+char *zpool_get_type(struct zpool *zpool)
+{
+	return zpool->type;
+}
+
+int zpool_malloc(struct zpool *zpool, size_t size, unsigned long *handle)
+{
+	return zpool->driver->malloc(zpool->pool, size, handle);
+}
+
+void zpool_free(struct zpool *zpool, unsigned long handle)
+{
+	zpool->driver->free(zpool->pool, handle);
+}
+
+int zpool_shrink(struct zpool *zpool, size_t size)
+{
+	return zpool->driver->shrink(zpool->pool, size);
+}
+
+void *zpool_map_handle(struct zpool *zpool, unsigned long handle,
+			enum zpool_mapmode mapmode)
+{
+	return zpool->driver->map(zpool->pool, handle, mapmode);
+}
+
+void zpool_unmap_handle(struct zpool *zpool, unsigned long handle)
+{
+	zpool->driver->unmap(zpool->pool, handle);
+}
+
+u64 zpool_get_total_size(struct zpool *zpool)
+{
+	return zpool->driver->total_size(zpool->pool);
+}
+
+static int __init init_zpool(void)
+{
+	pr_info("loaded\n");
+	return 0;
+}
+
+static void __exit exit_zpool(void)
+{
+	pr_info("unloaded\n");
+}
+
+module_init(init_zpool);
+module_exit(exit_zpool);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
+MODULE_DESCRIPTION("Common API for compressed memory storage");
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 4/6] mm/zpool: zbud/zsmalloc implement zpool
  2014-05-24 19:06   ` [PATCHv3 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
                       ` (2 preceding siblings ...)
  2014-05-24 19:06     ` [PATCHv3 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
@ 2014-05-24 19:06     ` Dan Streetman
  2014-05-24 19:06     ` [PATCHv3 5/6] mm/zpool: update zswap to use zpool Dan Streetman
                       ` (3 subsequent siblings)
  7 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-05-24 19:06 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Update zbud and zsmalloc to implement the zpool api.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Cc: Seth Jennings <sjennings@variantweb.net>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Nitin Gupta <ngupta@vflare.org>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

New for this patch set.

 mm/zbud.c     | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 mm/zsmalloc.c | 81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 159 insertions(+)

diff --git a/mm/zbud.c b/mm/zbud.c
index dd13665..8a72cb1 100644
--- a/mm/zbud.c
+++ b/mm/zbud.c
@@ -51,6 +51,7 @@
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/zbud.h>
+#include <linux/zpool.h>
 
 /*****************
  * Structures
@@ -114,6 +115,74 @@ struct zbud_header {
 };
 
 /*****************
+ * zpool
+ ****************/
+
+#ifdef CONFIG_ZPOOL
+
+static int zbud_zpool_evict(struct zbud_pool *pool, unsigned long handle)
+{
+	return zpool_evict(pool, handle);
+}
+
+static struct zbud_ops zbud_zpool_ops = {
+	.evict =	zbud_zpool_evict
+};
+
+static void *zbud_zpool_create(gfp_t gfp, struct zpool_ops *zpool_ops)
+{
+	return zbud_create_pool(gfp, &zbud_zpool_ops);
+}
+
+void zbud_zpool_destroy(void *pool)
+{
+	zbud_destroy_pool(pool);
+}
+
+int zbud_zpool_malloc(void *pool, size_t size, unsigned long *handle)
+{
+	return zbud_alloc(pool, size, handle);
+}
+void zbud_zpool_free(void *pool, unsigned long handle)
+{
+	zbud_free(pool, handle);
+}
+
+int zbud_zpool_shrink(void *pool, size_t size)
+{
+	return zbud_reclaim_page(pool, 8);
+}
+
+void *zbud_zpool_map(void *pool, unsigned long handle,
+			enum zpool_mapmode mm)
+{
+	return zbud_map(pool, handle);
+}
+void zbud_zpool_unmap(void *pool, unsigned long handle)
+{
+	zbud_unmap(pool, handle);
+}
+
+u64 zbud_zpool_total_size(void *pool)
+{
+	return zbud_get_pool_size(pool) * PAGE_SIZE;
+}
+
+static struct zpool_driver zbud_zpool_driver = {
+	.type =		"zbud",
+	.create =	zbud_zpool_create,
+	.destroy =	zbud_zpool_destroy,
+	.malloc =	zbud_zpool_malloc,
+	.free =		zbud_zpool_free,
+	.shrink =	zbud_zpool_shrink,
+	.map =		zbud_zpool_map,
+	.unmap =	zbud_zpool_unmap,
+	.total_size =	zbud_zpool_total_size,
+};
+
+#endif /* CONFIG_ZPOOL */
+
+/*****************
  * Helpers
 *****************/
 /* Just to make the code easier to read */
@@ -513,11 +582,20 @@ static int __init init_zbud(void)
 	/* Make sure the zbud header will fit in one chunk */
 	BUILD_BUG_ON(sizeof(struct zbud_header) > ZHDR_SIZE_ALIGNED);
 	pr_info("loaded\n");
+
+#ifdef CONFIG_ZPOOL
+	zpool_register_driver(&zbud_zpool_driver);
+#endif
+
 	return 0;
 }
 
 static void __exit exit_zbud(void)
 {
+#ifdef CONFIG_ZPOOL
+	zpool_unregister_driver(&zbud_zpool_driver);
+#endif
+
 	pr_info("unloaded\n");
 }
 
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index fe78189..07c3130 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -92,6 +92,7 @@
 #include <linux/spinlock.h>
 #include <linux/types.h>
 #include <linux/zsmalloc.h>
+#include <linux/zpool.h>
 
 /*
  * This must be power of 2 and greater than of equal to sizeof(link_free).
@@ -240,6 +241,78 @@ struct mapping_area {
 	enum zs_mapmode vm_mm; /* mapping mode */
 };
 
+/* zpool driver */
+
+#ifdef CONFIG_ZPOOL
+
+static void *zs_zpool_create(gfp_t gfp, struct zpool_ops *zpool_ops)
+{
+	return zs_create_pool(gfp);
+}
+
+void zs_zpool_destroy(void *pool)
+{
+	zs_destroy_pool(pool);
+}
+
+int zs_zpool_malloc(void *pool, size_t size, unsigned long *handle)
+{
+	*handle = zs_malloc(pool, size);
+	return *handle ? 0 : -1;
+}
+void zs_zpool_free(void *pool, unsigned long handle)
+{
+	zs_free(pool, handle);
+}
+
+int zs_zpool_shrink(void *pool, size_t size)
+{
+	return -EINVAL;
+}
+
+void *zs_zpool_map(void *pool, unsigned long handle,
+			enum zpool_mapmode mm)
+{
+	enum zs_mapmode zs_mm;
+
+	switch (mm) {
+	case ZPOOL_MM_RO:
+		zs_mm = ZS_MM_RO;
+		break;
+	case ZPOOL_MM_WO:
+		zs_mm = ZS_MM_WO;
+		break;
+	case ZPOOL_MM_RW: /* fallthru */
+	default:
+		zs_mm = ZS_MM_RW;
+		break;
+	}
+
+	return zs_map_object(pool, handle, zs_mm);
+}
+void zs_zpool_unmap(void *pool, unsigned long handle)
+{
+	zs_unmap_object(pool, handle);
+}
+
+u64 zs_zpool_total_size(void *pool)
+{
+	return zs_get_total_size_bytes(pool);
+}
+
+static struct zpool_driver zs_zpool_driver = {
+	.type =		"zsmalloc",
+	.create =	zs_zpool_create,
+	.destroy =	zs_zpool_destroy,
+	.malloc =	zs_zpool_malloc,
+	.free =		zs_zpool_free,
+	.shrink =	zs_zpool_shrink,
+	.map =		zs_zpool_map,
+	.unmap =	zs_zpool_unmap,
+	.total_size =	zs_zpool_total_size,
+};
+
+#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);
@@ -814,6 +887,10 @@ static void zs_exit(void)
 {
 	int cpu;
 
+#ifdef CONFIG_ZPOOL
+	zpool_unregister_driver(&zs_zpool_driver);
+#endif
+
 	cpu_notifier_register_begin();
 
 	for_each_online_cpu(cpu)
@@ -840,6 +917,10 @@ static int zs_init(void)
 
 	cpu_notifier_register_done();
 
+#ifdef CONFIG_ZPOOL
+	zpool_register_driver(&zs_zpool_driver);
+#endif
+
 	return 0;
 fail:
 	zs_exit();
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCHv3 5/6] mm/zpool: update zswap to use zpool
  2014-05-24 19:06   ` [PATCHv3 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
                       ` (3 preceding siblings ...)
  2014-05-24 19:06     ` [PATCH 4/6] mm/zpool: zbud/zsmalloc implement zpool Dan Streetman
@ 2014-05-24 19:06     ` Dan Streetman
  2014-05-24 19:06     ` [PATCH 6/6] mm/zpool: prevent zbud/zsmalloc from unloading when used Dan Streetman
                       ` (2 subsequent siblings)
  7 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-05-24 19:06 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Change zswap to use the zpool api instead of directly using zbud.
Add a boot-time param to allow selecting which zpool implementation
to use, with zbud as the default.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Cc: Seth Jennings <sjennings@variantweb.net>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

Changes since v2 : https://lkml.org/lkml/2014/5/7/894
  -change zswap to select ZPOOL instead of ZBUD
  -only try zbud default if not already tried

Changes since v1 https://lkml.org/lkml/2014/4/19/102
 -since zpool fallback is removed, manually fall back to zbud if
  specified type fails


 mm/Kconfig |  2 +-
 mm/zswap.c | 76 +++++++++++++++++++++++++++++++++++++-------------------------
 2 files changed, 46 insertions(+), 32 deletions(-)

diff --git a/mm/Kconfig b/mm/Kconfig
index 00f7720..5ae0016 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -531,7 +531,7 @@ config ZSWAP
 	bool "Compressed cache for swap pages (EXPERIMENTAL)"
 	depends on FRONTSWAP && CRYPTO=y
 	select CRYPTO_LZO
-	select ZBUD
+	select ZPOOL
 	default n
 	help
 	  A lightweight compressed cache for swap pages.  It takes
diff --git a/mm/zswap.c b/mm/zswap.c
index 1cc6770..3b54715 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -34,7 +34,7 @@
 #include <linux/swap.h>
 #include <linux/crypto.h>
 #include <linux/mempool.h>
-#include <linux/zbud.h>
+#include <linux/zpool.h>
 
 #include <linux/mm_types.h>
 #include <linux/page-flags.h>
@@ -45,8 +45,8 @@
 /*********************************
 * statistics
 **********************************/
-/* Number of memory pages used by the compressed pool */
-static u64 zswap_pool_pages;
+/* Total bytes used by the compressed storage */
+static u64 zswap_pool_total_size;
 /* The number of compressed pages currently stored in zswap */
 static atomic_t zswap_stored_pages = ATOMIC_INIT(0);
 
@@ -89,8 +89,13 @@ static unsigned int zswap_max_pool_percent = 20;
 module_param_named(max_pool_percent,
 			zswap_max_pool_percent, uint, 0644);
 
-/* zbud_pool is shared by all of zswap backend  */
-static struct zbud_pool *zswap_pool;
+/* Compressed storage to use */
+#define ZSWAP_ZPOOL_DEFAULT "zbud"
+static char *zswap_zpool_type = ZSWAP_ZPOOL_DEFAULT;
+module_param_named(zpool, zswap_zpool_type, charp, 0444);
+
+/* zpool is shared by all of zswap backend  */
+static struct zpool *zswap_pool;
 
 /*********************************
 * compression functions
@@ -168,7 +173,7 @@ static void zswap_comp_exit(void)
  *            be held while changing the refcount.  Since the lock must
  *            be held, there is no reason to also make refcount atomic.
  * offset - the swap offset for the entry.  Index into the red-black tree.
- * handle - zbud allocation handle that stores the compressed page data
+ * handle - zpool allocation handle that stores the compressed page data
  * length - the length in bytes of the compressed page data.  Needed during
  *          decompression
  */
@@ -284,15 +289,15 @@ static void zswap_rb_erase(struct rb_root *root, struct zswap_entry *entry)
 }
 
 /*
- * Carries out the common pattern of freeing and entry's zbud allocation,
+ * Carries out the common pattern of freeing and entry's zpool allocation,
  * freeing the entry itself, and decrementing the number of stored pages.
  */
 static void zswap_free_entry(struct zswap_entry *entry)
 {
-	zbud_free(zswap_pool, entry->handle);
+	zpool_free(zswap_pool, entry->handle);
 	zswap_entry_cache_free(entry);
 	atomic_dec(&zswap_stored_pages);
-	zswap_pool_pages = zbud_get_pool_size(zswap_pool);
+	zswap_pool_total_size = zpool_get_total_size(zswap_pool);
 }
 
 /* caller must hold the tree lock */
@@ -409,7 +414,7 @@ cleanup:
 static bool zswap_is_full(void)
 {
 	return totalram_pages * zswap_max_pool_percent / 100 <
-		zswap_pool_pages;
+		DIV_ROUND_UP(zswap_pool_total_size, PAGE_SIZE);
 }
 
 /*********************************
@@ -525,7 +530,7 @@ static int zswap_get_swap_cache_page(swp_entry_t entry,
  * the swap cache, the compressed version stored by zswap can be
  * freed.
  */
-static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle)
+static int zswap_writeback_entry(struct zpool *pool, unsigned long handle)
 {
 	struct zswap_header *zhdr;
 	swp_entry_t swpentry;
@@ -541,9 +546,9 @@ static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle)
 	};
 
 	/* extract swpentry from data */
-	zhdr = zbud_map(pool, handle);
+	zhdr = zpool_map_handle(pool, handle, ZPOOL_MM_RO);
 	swpentry = zhdr->swpentry; /* here */
-	zbud_unmap(pool, handle);
+	zpool_unmap_handle(pool, handle);
 	tree = zswap_trees[swp_type(swpentry)];
 	offset = swp_offset(swpentry);
 
@@ -573,13 +578,13 @@ static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle)
 	case ZSWAP_SWAPCACHE_NEW: /* page is locked */
 		/* decompress */
 		dlen = PAGE_SIZE;
-		src = (u8 *)zbud_map(zswap_pool, entry->handle) +
-			sizeof(struct zswap_header);
+		src = (u8 *)zpool_map_handle(zswap_pool, entry->handle,
+				ZPOOL_MM_RO) + sizeof(struct zswap_header);
 		dst = kmap_atomic(page);
 		ret = zswap_comp_op(ZSWAP_COMPOP_DECOMPRESS, src,
 				entry->length, dst, &dlen);
 		kunmap_atomic(dst);
-		zbud_unmap(zswap_pool, entry->handle);
+		zpool_unmap_handle(zswap_pool, entry->handle);
 		BUG_ON(ret);
 		BUG_ON(dlen != PAGE_SIZE);
 
@@ -652,7 +657,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 	/* reclaim space if needed */
 	if (zswap_is_full()) {
 		zswap_pool_limit_hit++;
-		if (zbud_reclaim_page(zswap_pool, 8)) {
+		if (zpool_shrink(zswap_pool, PAGE_SIZE)) {
 			zswap_reject_reclaim_fail++;
 			ret = -ENOMEM;
 			goto reject;
@@ -679,7 +684,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 
 	/* store */
 	len = dlen + sizeof(struct zswap_header);
-	ret = zbud_alloc(zswap_pool, len, &handle);
+	ret = zpool_malloc(zswap_pool, len, &handle);
 	if (ret == -ENOSPC) {
 		zswap_reject_compress_poor++;
 		goto freepage;
@@ -688,11 +693,11 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 		zswap_reject_alloc_fail++;
 		goto freepage;
 	}
-	zhdr = zbud_map(zswap_pool, handle);
+	zhdr = zpool_map_handle(zswap_pool, handle, ZPOOL_MM_RW);
 	zhdr->swpentry = swp_entry(type, offset);
 	buf = (u8 *)(zhdr + 1);
 	memcpy(buf, dst, dlen);
-	zbud_unmap(zswap_pool, handle);
+	zpool_unmap_handle(zswap_pool, handle);
 	put_cpu_var(zswap_dstmem);
 
 	/* populate entry */
@@ -715,7 +720,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 
 	/* update stats */
 	atomic_inc(&zswap_stored_pages);
-	zswap_pool_pages = zbud_get_pool_size(zswap_pool);
+	zswap_pool_total_size = zpool_get_total_size(zswap_pool);
 
 	return 0;
 
@@ -751,13 +756,13 @@ static int zswap_frontswap_load(unsigned type, pgoff_t offset,
 
 	/* decompress */
 	dlen = PAGE_SIZE;
-	src = (u8 *)zbud_map(zswap_pool, entry->handle) +
-			sizeof(struct zswap_header);
+	src = (u8 *)zpool_map_handle(zswap_pool, entry->handle,
+			ZPOOL_MM_RO) + sizeof(struct zswap_header);
 	dst = kmap_atomic(page);
 	ret = zswap_comp_op(ZSWAP_COMPOP_DECOMPRESS, src, entry->length,
 		dst, &dlen);
 	kunmap_atomic(dst);
-	zbud_unmap(zswap_pool, entry->handle);
+	zpool_unmap_handle(zswap_pool, entry->handle);
 	BUG_ON(ret);
 
 	spin_lock(&tree->lock);
@@ -810,7 +815,7 @@ static void zswap_frontswap_invalidate_area(unsigned type)
 	zswap_trees[type] = NULL;
 }
 
-static struct zbud_ops zswap_zbud_ops = {
+static struct zpool_ops zswap_zpool_ops = {
 	.evict = zswap_writeback_entry
 };
 
@@ -868,8 +873,8 @@ static int __init zswap_debugfs_init(void)
 			zswap_debugfs_root, &zswap_written_back_pages);
 	debugfs_create_u64("duplicate_entry", S_IRUGO,
 			zswap_debugfs_root, &zswap_duplicate_entry);
-	debugfs_create_u64("pool_pages", S_IRUGO,
-			zswap_debugfs_root, &zswap_pool_pages);
+	debugfs_create_u64("pool_total_size", S_IRUGO,
+			zswap_debugfs_root, &zswap_pool_total_size);
 	debugfs_create_atomic_t("stored_pages", S_IRUGO,
 			zswap_debugfs_root, &zswap_stored_pages);
 
@@ -894,17 +899,26 @@ static void __exit zswap_debugfs_exit(void) { }
 **********************************/
 static int __init init_zswap(void)
 {
+	gfp_t gfp = __GFP_NORETRY | __GFP_NOWARN;
+
 	if (!zswap_enabled)
 		return 0;
 
 	pr_info("loading zswap\n");
 
-	zswap_pool = zbud_create_pool(__GFP_NORETRY | __GFP_NOWARN,
-			&zswap_zbud_ops);
+	zswap_pool = zpool_create_pool(zswap_zpool_type, gfp, &zswap_zpool_ops);
+	if (!zswap_pool && strcmp(zswap_zpool_type, ZSWAP_ZPOOL_DEFAULT)) {
+		pr_info("%s zpool not available\n", zswap_zpool_type);
+		zswap_zpool_type = ZSWAP_ZPOOL_DEFAULT;
+		zswap_pool = zpool_create_pool(zswap_zpool_type, gfp,
+					       &zswap_zpool_ops);
+	}
 	if (!zswap_pool) {
-		pr_err("zbud pool creation failed\n");
+		pr_err("%s zpool not available\n", zswap_zpool_type);
+		pr_err("zpool creation failed\n");
 		goto error;
 	}
+	pr_info("using %s pool\n", zswap_zpool_type);
 
 	if (zswap_entry_cache_create()) {
 		pr_err("entry cache creation failed\n");
@@ -928,7 +942,7 @@ pcpufail:
 compfail:
 	zswap_entry_cache_destory();
 cachefail:
-	zbud_destroy_pool(zswap_pool);
+	zpool_destroy_pool(zswap_pool);
 error:
 	return -ENOMEM;
 }
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 6/6] mm/zpool: prevent zbud/zsmalloc from unloading when used
  2014-05-24 19:06   ` [PATCHv3 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
                       ` (4 preceding siblings ...)
  2014-05-24 19:06     ` [PATCHv3 5/6] mm/zpool: update zswap to use zpool Dan Streetman
@ 2014-05-24 19:06     ` Dan Streetman
  2014-05-27 22:40       ` Seth Jennings
  2014-05-27 22:44     ` [PATCHv3 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc Seth Jennings
  2014-06-02 22:19     ` [PATCHv4 " Dan Streetman
  7 siblings, 1 reply; 65+ messages in thread
From: Dan Streetman @ 2014-05-24 19:06 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Add try_module_get() to pool creation functions for zbud and zsmalloc,
and module_put() to pool destruction functions, since they now can be
modules used via zpool.  Without usage counting, they could be unloaded
while pool(s) were active, resulting in an oops.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Cc: Seth Jennings <sjennings@variantweb.net>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Nitin Gupta <ngupta@vflare.org>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

New for this patch set.

 mm/zbud.c     | 5 +++++
 mm/zsmalloc.c | 5 +++++
 2 files changed, 10 insertions(+)

diff --git a/mm/zbud.c b/mm/zbud.c
index 8a72cb1..2b3689c 100644
--- a/mm/zbud.c
+++ b/mm/zbud.c
@@ -282,6 +282,10 @@ struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops)
 	pool = kmalloc(sizeof(struct zbud_pool), GFP_KERNEL);
 	if (!pool)
 		return NULL;
+	if (!try_module_get(THIS_MODULE)) {
+		kfree(pool);
+		return NULL;
+	}
 	spin_lock_init(&pool->lock);
 	for_each_unbuddied_list(i, 0)
 		INIT_LIST_HEAD(&pool->unbuddied[i]);
@@ -302,6 +306,7 @@ struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops)
 void zbud_destroy_pool(struct zbud_pool *pool)
 {
 	kfree(pool);
+	module_put(THIS_MODULE);
 }
 
 /**
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index 07c3130..2cc2647 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -946,6 +946,10 @@ struct zs_pool *zs_create_pool(gfp_t flags)
 	pool = kzalloc(ovhd_size, GFP_KERNEL);
 	if (!pool)
 		return NULL;
+	if (!try_module_get(THIS_MODULE)) {
+		kfree(pool);
+		return NULL;
+	}
 
 	for (i = 0; i < ZS_SIZE_CLASSES; i++) {
 		int size;
@@ -985,6 +989,7 @@ void zs_destroy_pool(struct zs_pool *pool)
 		}
 	}
 	kfree(pool);
+	module_put(THIS_MODULE);
 }
 EXPORT_SYMBOL_GPL(zs_destroy_pool);
 
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* Re: [PATCHv3 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc
  2014-05-24 19:06     ` [PATCHv3 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
@ 2014-05-27 22:06       ` Seth Jennings
  2014-05-27 22:48         ` Seth Jennings
  2014-05-28  0:06         ` Dan Streetman
  0 siblings, 2 replies; 65+ messages in thread
From: Seth Jennings @ 2014-05-27 22:06 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Minchan Kim, Weijie Yang, Nitin Gupta, Andrew Morton, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Sat, May 24, 2014 at 03:06:06PM -0400, Dan Streetman wrote:
> Add zpool api.
> 
> zpool provides an interface for memory storage, typically of compressed
> memory.  Users can select what backend to use; currently the only
> implementations are zbud, a low density implementation with up to
> two compressed pages per storage page, and zsmalloc, a higher density
> implementation with multiple compressed pages per storage page.
> 
> Signed-off-by: Dan Streetman <ddstreet@ieee.org>
> Cc: Seth Jennings <sjennings@variantweb.net>
> Cc: Minchan Kim <minchan@kernel.org>
> Cc: Nitin Gupta <ngupta@vflare.org>
> Cc: Weijie Yang <weijie.yang@samsung.com>
> ---
> 
> Note this patch set is against the mmotm tree at
> git://git.cmpxchg.org/linux-mmotm.git
> This patch may need context changes to the -next or other trees.
> 
> Changes since v2 : https://lkml.org/lkml/2014/5/7/733
>   -Remove hardcoded zbud/zsmalloc implementations
>   -Add driver (un)register functions
> 
> Changes since v1 https://lkml.org/lkml/2014/4/19/101
>  -add some pr_info() during creation and pr_err() on errors
>  -remove zpool code to call zs_shrink(), since zsmalloc shrinking
>   was removed from this patchset
>  -remove fallback; only specified pool type will be tried
>  -pr_fmt() is defined in zpool to prefix zpool: in any pr_XXX() calls
> 
> 
>  include/linux/zpool.h | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  mm/Kconfig            |  41 ++++++----
>  mm/Makefile           |   1 +
>  mm/zpool.c            | 197 ++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 436 insertions(+), 17 deletions(-)
>  create mode 100644 include/linux/zpool.h
>  create mode 100644 mm/zpool.c
> 
> diff --git a/include/linux/zpool.h b/include/linux/zpool.h
> new file mode 100644
> index 0000000..699ac9b
> --- /dev/null
> +++ b/include/linux/zpool.h
> @@ -0,0 +1,214 @@
> +/*
> + * zpool memory storage api
> + *
> + * Copyright (C) 2014 Dan Streetman
> + *
> + * This is a common frontend for the zbud and zsmalloc memory
> + * storage pool implementations.  Typically, this is used to
> + * store compressed memory.
> + */
> +
> +#ifndef _ZPOOL_H_
> +#define _ZPOOL_H_
> +
> +struct zpool;
> +
> +struct zpool_ops {
> +	int (*evict)(struct zpool *pool, unsigned long handle);
> +};
> +
> +/*
> + * Control how a handle is mapped.  It will be ignored if the
> + * implementation does not support it.  Its use is optional.
> + * Note that this does not refer to memory protection, it
> + * refers to how the memory will be copied in/out if copying
> + * is necessary during mapping; read-write is the safest as
> + * it copies the existing memory in on map, and copies the
> + * changed memory back out on unmap.  Write-only does not copy
> + * in the memory and should only be used for initialization.
> + * If in doubt, use ZPOOL_MM_DEFAULT which is read-write.
> + */
> +enum zpool_mapmode {
> +	ZPOOL_MM_RW, /* normal read-write mapping */
> +	ZPOOL_MM_RO, /* read-only (no copy-out at unmap time) */
> +	ZPOOL_MM_WO, /* write-only (no copy-in at map time) */
> +
> +	ZPOOL_MM_DEFAULT = ZPOOL_MM_RW
> +};
> +
> +/**
> + * zpool_create_pool() - Create a new zpool
> + * @type	The type of the zpool to create (e.g. zbud, zsmalloc)
> + * @flags	What GFP flags should be used when the zpool allocates memory.
> + * @ops		The optional ops callback.
> + *
> + * This creates a new zpool of the specified type.  The zpool will use the
> + * given flags when allocating any memory.  If the ops param is NULL, then
> + * the created zpool will not be shrinkable.
> + *
> + * Returns: New zpool on success, NULL on failure.
> + */
> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
> +			struct zpool_ops *ops);
> +
> +/**
> + * zpool_get_type() - Get the type of the zpool
> + * @pool	The zpool to check
> + *
> + * This returns the type of the pool.
> + *
> + * Returns: The type of zpool.
> + */
> +char *zpool_get_type(struct zpool *pool);
> +
> +/**
> + * zpool_destroy_pool() - Destroy a zpool
> + * @pool	The zpool to destroy.
> + *
> + * This destroys an existing zpool.  The zpool should not be in use.
> + */
> +void zpool_destroy_pool(struct zpool *pool);
> +
> +/**
> + * zpool_malloc() - Allocate memory
> + * @pool	The zpool to allocate from.
> + * @size	The amount of memory to allocate.
> + * @handle	Pointer to the handle to set
> + *
> + * This allocates the requested amount of memory from the pool.
> + * The provided @handle will be set to the allocated object handle.
> + *
> + * Returns: 0 on success, negative value on error.
> + */
> +int zpool_malloc(struct zpool *pool, size_t size, unsigned long *handle);
> +
> +/**
> + * zpool_free() - Free previously allocated memory
> + * @pool	The zpool that allocated the memory.
> + * @handle	The handle to the memory to free.
> + *
> + * This frees previously allocated memory.  This does not guarantee
> + * that the pool will actually free memory, only that the memory
> + * in the pool will become available for use by the pool.
> + */
> +void zpool_free(struct zpool *pool, unsigned long handle);
> +
> +/**
> + * zpool_shrink() - Shrink the pool size
> + * @pool	The zpool to shrink.
> + * @size	The minimum amount to shrink the pool.
> + *
> + * This attempts to shrink the actual memory size of the pool
> + * by evicting currently used handle(s).  If the pool was
> + * created with no zpool_ops, or the evict call fails for any
> + * of the handles, this will fail.
> + *
> + * Returns: 0 on success, negative value on error/failure.
> + */
> +int zpool_shrink(struct zpool *pool, size_t size);

This should take a number of pages to be reclaimed, not a size.  The
user can evict their own object to reclaim a certain number of bytes
from the pool.  What the user can't do is reclaim a page since it is not
aware of the arrangement of the stored objects in the memory pages.

Also in patch 5/6 of six I see:

-		if (zbud_reclaim_page(zswap_pool, 8)) {
+		if (zpool_shrink(zswap_pool, PAGE_SIZE)) {

but then in 4/6 I see:

+int zbud_zpool_shrink(void *pool, size_t size)
+{
+	return zbud_reclaim_page(pool, 8);
+}

That is why it didn't completely explode on you since the zbud logic
is still reclaiming pages.

> +
> +/**
> + * zpool_map_handle() - Map a previously allocated handle into memory
> + * @pool	The zpool that the handle was allocated from
> + * @handle	The handle to map
> + * @mm	How the memory should be mapped
> + *
> + * This maps a previously allocated handle into memory.  The @mm
> + * param indicates to the implemenation how the memory will be
> + * used, i.e. read-only, write-only, read-write.  If the
> + * implementation does not support it, the memory will be treated
> + * as read-write.
> + *
> + * This may hold locks, disable interrupts, and/or preemption,
> + * and the zpool_unmap_handle() must be called to undo those
> + * actions.  The code that uses the mapped handle should complete
> + * its operatons on the mapped handle memory quickly and unmap
> + * as soon as possible.  Multiple handles should not be mapped
> + * concurrently on a cpu.
> + *
> + * Returns: A pointer to the handle's mapped memory area.
> + */
> +void *zpool_map_handle(struct zpool *pool, unsigned long handle,
> +			enum zpool_mapmode mm);
> +
> +/**
> + * zpool_unmap_handle() - Unmap a previously mapped handle
> + * @pool	The zpool that the handle was allocated from
> + * @handle	The handle to unmap
> + *
> + * This unmaps a previously mapped handle.  Any locks or other
> + * actions that the implemenation took in zpool_map_handle()
> + * will be undone here.  The memory area returned from
> + * zpool_map_handle() should no longer be used after this.
> + */
> +void zpool_unmap_handle(struct zpool *pool, unsigned long handle);
> +
> +/**
> + * zpool_get_total_size() - The total size of the pool
> + * @pool	The zpool to check
> + *
> + * This returns the total size in bytes of the pool.
> + *
> + * Returns: Total size of the zpool in bytes.
> + */
> +u64 zpool_get_total_size(struct zpool *pool);
> +
> +
> +/**
> + * struct zpool_driver - driver implementation for zpool
> + * @type:	name of the driver.
> + * @list:	entry in the list of zpool drivers.
> + * @create:	create a new pool.
> + * @destroy:	destroy a pool.
> + * @malloc:	allocate mem from a pool.
> + * @free:	free mem from a pool.
> + * @shrink:	shrink the pool.
> + * @map:	map a handle.
> + * @unmap:	unmap a handle.
> + * @total_size:	get total size of a pool.
> + *
> + * This is created by a zpool implementation and registered
> + * with zpool.
> + */
> +struct zpool_driver {
> +	char *type;
> +	struct list_head list;
> +
> +	void *(*create)(gfp_t gfp, struct zpool_ops *ops);
> +	void (*destroy)(void *pool);
> +
> +	int (*malloc)(void *pool, size_t size, unsigned long *handle);
> +	void (*free)(void *pool, unsigned long handle);
> +
> +	int (*shrink)(void *pool, size_t size);
> +
> +	void *(*map)(void *pool, unsigned long handle,
> +				enum zpool_mapmode mm);
> +	void (*unmap)(void *pool, unsigned long handle);
> +
> +	u64 (*total_size)(void *pool);
> +};
> +
> +/**
> + * zpool_register_driver() - register a zpool implementation.
> + * @driver:	driver to register
> + */
> +void zpool_register_driver(struct zpool_driver *driver);
> +
> +/**
> + * zpool_unregister_driver() - unregister a zpool implementation.
> + * @driver:	driver to unregister.
> + */
> +void zpool_unregister_driver(struct zpool_driver *driver);
> +
> +/**
> + * zpool_evict() - evict callback from a zpool implementation.
> + * @pool:	pool to evict from.
> + * @handle:	handle to evict.
> + *
> + * This can be used by zpool implementations to call the
> + * user's evict zpool_ops struct evict callback.
> + */
> +int zpool_evict(void *pool, unsigned long handle);
> +
> +#endif
> diff --git a/mm/Kconfig b/mm/Kconfig
> index 7511b4a..00f7720 100644
> --- a/mm/Kconfig
> +++ b/mm/Kconfig
> @@ -515,15 +515,17 @@ config CMA_DEBUG
>  	  processing calls such as dma_alloc_from_contiguous().
>  	  This option does not affect warning and error messages.
>  
> -config ZBUD
> -	tristate
> -	default n
> +config MEM_SOFT_DIRTY
> +	bool "Track memory changes"
> +	depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
> +	select PROC_PAGE_MONITOR
>  	help
> -	  A special purpose allocator for storing compressed pages.
> -	  It is designed to store up to two compressed pages per physical
> -	  page.  While this design limits storage density, it has simple and
> -	  deterministic reclaim properties that make it preferable to a higher
> -	  density approach when reclaim will be used.
> +	  This option enables memory changes tracking by introducing a
> +	  soft-dirty bit on pte-s. This bit it set when someone writes
> +	  into a page just as regular dirty bit, but unlike the latter
> +	  it can be cleared by hands.
> +
> +	  See Documentation/vm/soft-dirty.txt for more details.
>  
>  config ZSWAP
>  	bool "Compressed cache for swap pages (EXPERIMENTAL)"
> @@ -545,17 +547,22 @@ config ZSWAP
>  	  they have not be fully explored on the large set of potential
>  	  configurations and workloads that exist.
>  
> -config MEM_SOFT_DIRTY
> -	bool "Track memory changes"
> -	depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
> -	select PROC_PAGE_MONITOR
> +config ZPOOL
> +	tristate "Common API for compressed memory storage"
> +	default n
>  	help
> -	  This option enables memory changes tracking by introducing a
> -	  soft-dirty bit on pte-s. This bit it set when someone writes
> -	  into a page just as regular dirty bit, but unlike the latter
> -	  it can be cleared by hands.
> +	  Compressed memory storage API.  This allows using either zbud or
> +	  zsmalloc.
>  
> -	  See Documentation/vm/soft-dirty.txt for more details.
> +config ZBUD
> +	tristate "Low density storage for compressed pages"
> +	default n
> +	help
> +	  A special purpose allocator for storing compressed pages.
> +	  It is designed to store up to two compressed pages per physical
> +	  page.  While this design limits storage density, it has simple and
> +	  deterministic reclaim properties that make it preferable to a higher
> +	  density approach when reclaim will be used.
>  
>  config ZSMALLOC
>  	tristate "Memory allocator for compressed pages"
> diff --git a/mm/Makefile b/mm/Makefile
> index 2b6fff2..759db04 100644
> --- a/mm/Makefile
> +++ b/mm/Makefile
> @@ -61,6 +61,7 @@ obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
>  obj-$(CONFIG_CLEANCACHE) += cleancache.o
>  obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
>  obj-$(CONFIG_PAGE_OWNER) += pageowner.o
> +obj-$(CONFIG_ZPOOL)	+= zpool.o
>  obj-$(CONFIG_ZBUD)	+= zbud.o
>  obj-$(CONFIG_ZSMALLOC)	+= zsmalloc.o
>  obj-$(CONFIG_GENERIC_EARLY_IOREMAP) += early_ioremap.o
> diff --git a/mm/zpool.c b/mm/zpool.c
> new file mode 100644
> index 0000000..89ed71f
> --- /dev/null
> +++ b/mm/zpool.c
> @@ -0,0 +1,197 @@
> +/*
> + * zpool memory storage api
> + *
> + * Copyright (C) 2014 Dan Streetman
> + *
> + * This is a common frontend for memory storage pool implementations.
> + * Typically, this is used to store compressed memory.
> + */
> +
> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
> +
> +#include <linux/list.h>
> +#include <linux/types.h>
> +#include <linux/mm.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/module.h>
> +#include <linux/zpool.h>
> +
> +struct zpool {
> +	char *type;
> +
> +	struct zpool_driver *driver;
> +	void *pool;
> +	struct zpool_ops *ops;
> +
> +	struct list_head list;
> +};
> +
> +static LIST_HEAD(drivers_head);
> +static DEFINE_SPINLOCK(drivers_lock);
> +
> +static LIST_HEAD(pools_head);
> +static DEFINE_SPINLOCK(pools_lock);
> +
> +void zpool_register_driver(struct zpool_driver *driver)
> +{
> +	spin_lock(&drivers_lock);
> +	list_add(&driver->list, &drivers_head);
> +	spin_unlock(&drivers_lock);
> +}
> +EXPORT_SYMBOL(zpool_register_driver);
> +
> +void zpool_unregister_driver(struct zpool_driver *driver)
> +{
> +	spin_lock(&drivers_lock);
> +	list_del(&driver->list);
> +	spin_unlock(&drivers_lock);
> +}
> +EXPORT_SYMBOL(zpool_unregister_driver);
> +
> +int zpool_evict(void *pool, unsigned long handle)
> +{
> +	struct zpool *zpool;
> +
> +	spin_lock(&pools_lock);
> +	list_for_each_entry(zpool, &pools_head, list) {

You can do a container_of() here:

zpool = container_of(pool, struct zpool, pool);

Seth

> +		if (zpool->pool == pool) {
> +			spin_unlock(&pools_lock);
> +			if (!zpool->ops || !zpool->ops->evict)
> +				return -EINVAL;
> +			return zpool->ops->evict(zpool, handle);
> +		}
> +	}
> +	spin_unlock(&pools_lock);
> +
> +	return -ENOENT;
> +}
> +EXPORT_SYMBOL(zpool_evict);
> +
> +static struct zpool_driver *zpool_get_driver(char *type)
> +{
> +	struct zpool_driver *driver;
> +
> +	assert_spin_locked(&drivers_lock);
> +	list_for_each_entry(driver, &drivers_head, list) {
> +		if (!strcmp(driver->type, type))
> +			return driver;
> +	}
> +
> +	return NULL;
> +}
> +
> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
> +			struct zpool_ops *ops)
> +{
> +	struct zpool_driver *driver;
> +	struct zpool *zpool;
> +
> +	pr_info("creating pool type %s\n", type);
> +
> +	spin_lock(&drivers_lock);
> +	driver = zpool_get_driver(type);
> +	spin_unlock(&drivers_lock);
> +
> +	if (!driver) {
> +		request_module(type);
> +		spin_lock(&drivers_lock);
> +		driver = zpool_get_driver(type);
> +		spin_unlock(&drivers_lock);
> +	}
> +
> +	if (!driver) {
> +		pr_err("no driver for type %s\n", type);
> +		return NULL;
> +	}
> +
> +	zpool = kmalloc(sizeof(*zpool), GFP_KERNEL);
> +	if (!zpool) {
> +		pr_err("couldn't create zpool - out of memory\n");
> +		return NULL;
> +	}
> +
> +	zpool->type = driver->type;
> +	zpool->driver = driver;
> +	zpool->pool = driver->create(flags, ops);
> +	zpool->ops = ops;
> +
> +	if (!zpool->pool) {
> +		pr_err("couldn't create %s pool\n", type);
> +		kfree(zpool);
> +		return NULL;
> +	}
> +
> +	pr_info("created %s pool\n", type);
> +
> +	spin_lock(&pools_lock);
> +	list_add(&zpool->list, &pools_head);
> +	spin_unlock(&pools_lock);
> +
> +	return zpool;
> +}
> +
> +void zpool_destroy_pool(struct zpool *zpool)
> +{
> +	pr_info("destroying pool type %s\n", zpool->type);
> +
> +	spin_lock(&pools_lock);
> +	list_del(&zpool->list);
> +	spin_unlock(&pools_lock);
> +	zpool->driver->destroy(zpool->pool);
> +	kfree(zpool);
> +}
> +
> +char *zpool_get_type(struct zpool *zpool)
> +{
> +	return zpool->type;
> +}
> +
> +int zpool_malloc(struct zpool *zpool, size_t size, unsigned long *handle)
> +{
> +	return zpool->driver->malloc(zpool->pool, size, handle);
> +}
> +
> +void zpool_free(struct zpool *zpool, unsigned long handle)
> +{
> +	zpool->driver->free(zpool->pool, handle);
> +}
> +
> +int zpool_shrink(struct zpool *zpool, size_t size)
> +{
> +	return zpool->driver->shrink(zpool->pool, size);
> +}
> +
> +void *zpool_map_handle(struct zpool *zpool, unsigned long handle,
> +			enum zpool_mapmode mapmode)
> +{
> +	return zpool->driver->map(zpool->pool, handle, mapmode);
> +}
> +
> +void zpool_unmap_handle(struct zpool *zpool, unsigned long handle)
> +{
> +	zpool->driver->unmap(zpool->pool, handle);
> +}
> +
> +u64 zpool_get_total_size(struct zpool *zpool)
> +{
> +	return zpool->driver->total_size(zpool->pool);
> +}
> +
> +static int __init init_zpool(void)
> +{
> +	pr_info("loaded\n");
> +	return 0;
> +}
> +
> +static void __exit exit_zpool(void)
> +{
> +	pr_info("unloaded\n");
> +}
> +
> +module_init(init_zpool);
> +module_exit(exit_zpool);
> +
> +MODULE_LICENSE("GPL");
> +MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
> +MODULE_DESCRIPTION("Common API for compressed memory storage");
> -- 
> 1.8.3.1
> 

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 6/6] mm/zpool: prevent zbud/zsmalloc from unloading when used
  2014-05-24 19:06     ` [PATCH 6/6] mm/zpool: prevent zbud/zsmalloc from unloading when used Dan Streetman
@ 2014-05-27 22:40       ` Seth Jennings
  2014-05-28  0:40         ` Dan Streetman
  0 siblings, 1 reply; 65+ messages in thread
From: Seth Jennings @ 2014-05-27 22:40 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Minchan Kim, Weijie Yang, Nitin Gupta, Andrew Morton, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Sat, May 24, 2014 at 03:06:09PM -0400, Dan Streetman wrote:
> Add try_module_get() to pool creation functions for zbud and zsmalloc,
> and module_put() to pool destruction functions, since they now can be
> modules used via zpool.  Without usage counting, they could be unloaded
> while pool(s) were active, resulting in an oops.

I like the idea here, but what about doing this in the zpool layer? For
me, it is kinda weird for a module to be taking a ref on itself.  Maybe
this is excepted practice.  Is there precedent for this?

What about having the zbud/zsmalloc drivers pass their module pointers
to zpool_register_driver() as an additional field in struct zpool_driver
and have zpool take the reference?  Since zpool is the one in trouble if
the driver is unloaded.

Seth

> 
> Signed-off-by: Dan Streetman <ddstreet@ieee.org>
> Cc: Seth Jennings <sjennings@variantweb.net>
> Cc: Minchan Kim <minchan@kernel.org>
> Cc: Nitin Gupta <ngupta@vflare.org>
> Cc: Weijie Yang <weijie.yang@samsung.com>
> ---
> 
> New for this patch set.
> 
>  mm/zbud.c     | 5 +++++
>  mm/zsmalloc.c | 5 +++++
>  2 files changed, 10 insertions(+)
> 
> diff --git a/mm/zbud.c b/mm/zbud.c
> index 8a72cb1..2b3689c 100644
> --- a/mm/zbud.c
> +++ b/mm/zbud.c
> @@ -282,6 +282,10 @@ struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops)
>  	pool = kmalloc(sizeof(struct zbud_pool), GFP_KERNEL);
>  	if (!pool)
>  		return NULL;
> +	if (!try_module_get(THIS_MODULE)) {
> +		kfree(pool);
> +		return NULL;
> +	}
>  	spin_lock_init(&pool->lock);
>  	for_each_unbuddied_list(i, 0)
>  		INIT_LIST_HEAD(&pool->unbuddied[i]);
> @@ -302,6 +306,7 @@ struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops)
>  void zbud_destroy_pool(struct zbud_pool *pool)
>  {
>  	kfree(pool);
> +	module_put(THIS_MODULE);
>  }
>  
>  /**
> diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
> index 07c3130..2cc2647 100644
> --- a/mm/zsmalloc.c
> +++ b/mm/zsmalloc.c
> @@ -946,6 +946,10 @@ struct zs_pool *zs_create_pool(gfp_t flags)
>  	pool = kzalloc(ovhd_size, GFP_KERNEL);
>  	if (!pool)
>  		return NULL;
> +	if (!try_module_get(THIS_MODULE)) {
> +		kfree(pool);
> +		return NULL;
> +	}
>  
>  	for (i = 0; i < ZS_SIZE_CLASSES; i++) {
>  		int size;
> @@ -985,6 +989,7 @@ void zs_destroy_pool(struct zs_pool *pool)
>  		}
>  	}
>  	kfree(pool);
> +	module_put(THIS_MODULE);
>  }
>  EXPORT_SYMBOL_GPL(zs_destroy_pool);
>  
> -- 
> 1.8.3.1
> 

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv3 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc
  2014-05-24 19:06   ` [PATCHv3 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
                       ` (5 preceding siblings ...)
  2014-05-24 19:06     ` [PATCH 6/6] mm/zpool: prevent zbud/zsmalloc from unloading when used Dan Streetman
@ 2014-05-27 22:44     ` Seth Jennings
  2014-06-02 22:19     ` [PATCHv4 " Dan Streetman
  7 siblings, 0 replies; 65+ messages in thread
From: Seth Jennings @ 2014-05-27 22:44 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Minchan Kim, Weijie Yang, Nitin Gupta, Andrew Morton, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Sat, May 24, 2014 at 03:06:03PM -0400, Dan Streetman wrote:
> In order to allow zswap users to choose between zbud and zsmalloc for
> the compressed storage pool, this patch set adds a new api "zpool" that
> provides an interface to both zbud and zsmalloc.  Only minor changes
> to zbud's interface were needed.  This does not include implementing
> shrinking in zsmalloc, which will be sent separately.
> 
> I believe Seth originally was using zsmalloc for swap, but there were
> concerns about how significant the impact of shrinking zsmalloc would
> be when zswap had to start reclaiming pages.  That still may be an
> issue, but this at least allows users to choose themselves whether
> they want a lower-density or higher-density compressed storage medium.
> At least for situations where zswap reclaim is never or rarely reached,
> it probably makes sense to use the higher density of zsmalloc.
> 
> Note this patch set does not change zram to use zpool, although that
> change should be possible as well.

Much cleaner now, thanks Dan :)

A few more comments (see replies to 3/6 and 6/6)

Seth

> 
> ---
> 
> Changes since v2 : https://lkml.org/lkml/2014/5/7/927
>   -Change zpool to use driver registration instead of hardcoding
>    implementations
>   -Add module use counting in zbud/zsmalloc
> 
> Changes since v1 https://lkml.org/lkml/2014/4/19/97
>  -remove zsmalloc shrinking
>  -change zbud size param type from unsigned int to size_t
>  -remove zpool fallback creation
>  -zswap manually falls back to zbud if specified type fails
> 
> 
> Dan Streetman (6):
>   mm/zbud: zbud_alloc() minor param change
>   mm/zbud: change zbud_alloc size type to size_t
>   mm/zpool: implement common zpool api to zbud/zsmalloc
>   mm/zpool: zbud/zsmalloc implement zpool
>   mm/zpool: update zswap to use zpool
>   mm/zpool: prevent zbud/zsmalloc from unloading when used
> 
>  include/linux/zbud.h  |   2 +-
>  include/linux/zpool.h | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  mm/Kconfig            |  43 +++++-----
>  mm/Makefile           |   1 +
>  mm/zbud.c             | 113 ++++++++++++++++++++++----
>  mm/zpool.c            | 197 ++++++++++++++++++++++++++++++++++++++++++++++
>  mm/zsmalloc.c         |  86 ++++++++++++++++++++
>  mm/zswap.c            |  76 ++++++++++--------
>  8 files changed, 668 insertions(+), 64 deletions(-)
>  create mode 100644 include/linux/zpool.h
>  create mode 100644 mm/zpool.c
> 
> -- 
> 1.8.3.1
> 

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv3 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc
  2014-05-27 22:06       ` Seth Jennings
@ 2014-05-27 22:48         ` Seth Jennings
  2014-05-28  0:06         ` Dan Streetman
  1 sibling, 0 replies; 65+ messages in thread
From: Seth Jennings @ 2014-05-27 22:48 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Minchan Kim, Weijie Yang, Nitin Gupta, Andrew Morton, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Tue, May 27, 2014 at 05:06:39PM -0500, Seth Jennings wrote:
> On Sat, May 24, 2014 at 03:06:06PM -0400, Dan Streetman wrote:
<snip>
> > +
> > +int zpool_evict(void *pool, unsigned long handle)
> > +{
> > +	struct zpool *zpool;
> > +
> > +	spin_lock(&pools_lock);
> > +	list_for_each_entry(zpool, &pools_head, list) {
> 
> You can do a container_of() here:
> 
> zpool = container_of(pool, struct zpool, pool);

If you do this, all of the pools_head/pools_lock is unneeded as well.

Seth

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv3 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc
  2014-05-27 22:06       ` Seth Jennings
  2014-05-27 22:48         ` Seth Jennings
@ 2014-05-28  0:06         ` Dan Streetman
  2014-05-29  3:48           ` Seth Jennings
  1 sibling, 1 reply; 65+ messages in thread
From: Dan Streetman @ 2014-05-28  0:06 UTC (permalink / raw)
  To: Seth Jennings
  Cc: Minchan Kim, Weijie Yang, Nitin Gupta, Andrew Morton, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Tue, May 27, 2014 at 6:06 PM, Seth Jennings <sjennings@variantweb.net> wrote:
> On Sat, May 24, 2014 at 03:06:06PM -0400, Dan Streetman wrote:
>> Add zpool api.
>>
>> zpool provides an interface for memory storage, typically of compressed
>> memory.  Users can select what backend to use; currently the only
>> implementations are zbud, a low density implementation with up to
>> two compressed pages per storage page, and zsmalloc, a higher density
>> implementation with multiple compressed pages per storage page.
>>
>> Signed-off-by: Dan Streetman <ddstreet@ieee.org>
>> Cc: Seth Jennings <sjennings@variantweb.net>
>> Cc: Minchan Kim <minchan@kernel.org>
>> Cc: Nitin Gupta <ngupta@vflare.org>
>> Cc: Weijie Yang <weijie.yang@samsung.com>
>> ---
>>
>> Note this patch set is against the mmotm tree at
>> git://git.cmpxchg.org/linux-mmotm.git
>> This patch may need context changes to the -next or other trees.
>>
>> Changes since v2 : https://lkml.org/lkml/2014/5/7/733
>>   -Remove hardcoded zbud/zsmalloc implementations
>>   -Add driver (un)register functions
>>
>> Changes since v1 https://lkml.org/lkml/2014/4/19/101
>>  -add some pr_info() during creation and pr_err() on errors
>>  -remove zpool code to call zs_shrink(), since zsmalloc shrinking
>>   was removed from this patchset
>>  -remove fallback; only specified pool type will be tried
>>  -pr_fmt() is defined in zpool to prefix zpool: in any pr_XXX() calls
>>
>>
>>  include/linux/zpool.h | 214 ++++++++++++++++++++++++++++++++++++++++++++++++++
>>  mm/Kconfig            |  41 ++++++----
>>  mm/Makefile           |   1 +
>>  mm/zpool.c            | 197 ++++++++++++++++++++++++++++++++++++++++++++++
>>  4 files changed, 436 insertions(+), 17 deletions(-)
>>  create mode 100644 include/linux/zpool.h
>>  create mode 100644 mm/zpool.c
>>
>> diff --git a/include/linux/zpool.h b/include/linux/zpool.h
>> new file mode 100644
>> index 0000000..699ac9b
>> --- /dev/null
>> +++ b/include/linux/zpool.h
>> @@ -0,0 +1,214 @@
>> +/*
>> + * zpool memory storage api
>> + *
>> + * Copyright (C) 2014 Dan Streetman
>> + *
>> + * This is a common frontend for the zbud and zsmalloc memory
>> + * storage pool implementations.  Typically, this is used to
>> + * store compressed memory.
>> + */
>> +
>> +#ifndef _ZPOOL_H_
>> +#define _ZPOOL_H_
>> +
>> +struct zpool;
>> +
>> +struct zpool_ops {
>> +     int (*evict)(struct zpool *pool, unsigned long handle);
>> +};
>> +
>> +/*
>> + * Control how a handle is mapped.  It will be ignored if the
>> + * implementation does not support it.  Its use is optional.
>> + * Note that this does not refer to memory protection, it
>> + * refers to how the memory will be copied in/out if copying
>> + * is necessary during mapping; read-write is the safest as
>> + * it copies the existing memory in on map, and copies the
>> + * changed memory back out on unmap.  Write-only does not copy
>> + * in the memory and should only be used for initialization.
>> + * If in doubt, use ZPOOL_MM_DEFAULT which is read-write.
>> + */
>> +enum zpool_mapmode {
>> +     ZPOOL_MM_RW, /* normal read-write mapping */
>> +     ZPOOL_MM_RO, /* read-only (no copy-out at unmap time) */
>> +     ZPOOL_MM_WO, /* write-only (no copy-in at map time) */
>> +
>> +     ZPOOL_MM_DEFAULT = ZPOOL_MM_RW
>> +};
>> +
>> +/**
>> + * zpool_create_pool() - Create a new zpool
>> + * @type     The type of the zpool to create (e.g. zbud, zsmalloc)
>> + * @flags    What GFP flags should be used when the zpool allocates memory.
>> + * @ops              The optional ops callback.
>> + *
>> + * This creates a new zpool of the specified type.  The zpool will use the
>> + * given flags when allocating any memory.  If the ops param is NULL, then
>> + * the created zpool will not be shrinkable.
>> + *
>> + * Returns: New zpool on success, NULL on failure.
>> + */
>> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
>> +                     struct zpool_ops *ops);
>> +
>> +/**
>> + * zpool_get_type() - Get the type of the zpool
>> + * @pool     The zpool to check
>> + *
>> + * This returns the type of the pool.
>> + *
>> + * Returns: The type of zpool.
>> + */
>> +char *zpool_get_type(struct zpool *pool);
>> +
>> +/**
>> + * zpool_destroy_pool() - Destroy a zpool
>> + * @pool     The zpool to destroy.
>> + *
>> + * This destroys an existing zpool.  The zpool should not be in use.
>> + */
>> +void zpool_destroy_pool(struct zpool *pool);
>> +
>> +/**
>> + * zpool_malloc() - Allocate memory
>> + * @pool     The zpool to allocate from.
>> + * @size     The amount of memory to allocate.
>> + * @handle   Pointer to the handle to set
>> + *
>> + * This allocates the requested amount of memory from the pool.
>> + * The provided @handle will be set to the allocated object handle.
>> + *
>> + * Returns: 0 on success, negative value on error.
>> + */
>> +int zpool_malloc(struct zpool *pool, size_t size, unsigned long *handle);
>> +
>> +/**
>> + * zpool_free() - Free previously allocated memory
>> + * @pool     The zpool that allocated the memory.
>> + * @handle   The handle to the memory to free.
>> + *
>> + * This frees previously allocated memory.  This does not guarantee
>> + * that the pool will actually free memory, only that the memory
>> + * in the pool will become available for use by the pool.
>> + */
>> +void zpool_free(struct zpool *pool, unsigned long handle);
>> +
>> +/**
>> + * zpool_shrink() - Shrink the pool size
>> + * @pool     The zpool to shrink.
>> + * @size     The minimum amount to shrink the pool.
>> + *
>> + * This attempts to shrink the actual memory size of the pool
>> + * by evicting currently used handle(s).  If the pool was
>> + * created with no zpool_ops, or the evict call fails for any
>> + * of the handles, this will fail.
>> + *
>> + * Returns: 0 on success, negative value on error/failure.
>> + */
>> +int zpool_shrink(struct zpool *pool, size_t size);
>
> This should take a number of pages to be reclaimed, not a size.  The
> user can evict their own object to reclaim a certain number of bytes
> from the pool.  What the user can't do is reclaim a page since it is not
> aware of the arrangement of the stored objects in the memory pages.

Yes I suppose that's true, I'll update it for v4...

>
> Also in patch 5/6 of six I see:
>
> -               if (zbud_reclaim_page(zswap_pool, 8)) {
> +               if (zpool_shrink(zswap_pool, PAGE_SIZE)) {
>
> but then in 4/6 I see:
>
> +int zbud_zpool_shrink(void *pool, size_t size)
> +{
> +       return zbud_reclaim_page(pool, 8);
> +}
>
> That is why it didn't completely explode on you since the zbud logic
> is still reclaiming pages.

Ha, yes clearly I neglected to translate between the size and the
number of pages there, oops!

On this topic - 8 retries seems very arbitrary.  Does it make sense to
include retrying in zbud and/or zpool at all?  The caller can easily
retry any number of times themselves, especially since zbud (and
eventually zsmalloc) will return -EAGAIN if the caller should retry.

>
>> +
>> +/**
>> + * zpool_map_handle() - Map a previously allocated handle into memory
>> + * @pool     The zpool that the handle was allocated from
>> + * @handle   The handle to map
>> + * @mm       How the memory should be mapped
>> + *
>> + * This maps a previously allocated handle into memory.  The @mm
>> + * param indicates to the implemenation how the memory will be
>> + * used, i.e. read-only, write-only, read-write.  If the
>> + * implementation does not support it, the memory will be treated
>> + * as read-write.
>> + *
>> + * This may hold locks, disable interrupts, and/or preemption,
>> + * and the zpool_unmap_handle() must be called to undo those
>> + * actions.  The code that uses the mapped handle should complete
>> + * its operatons on the mapped handle memory quickly and unmap
>> + * as soon as possible.  Multiple handles should not be mapped
>> + * concurrently on a cpu.
>> + *
>> + * Returns: A pointer to the handle's mapped memory area.
>> + */
>> +void *zpool_map_handle(struct zpool *pool, unsigned long handle,
>> +                     enum zpool_mapmode mm);
>> +
>> +/**
>> + * zpool_unmap_handle() - Unmap a previously mapped handle
>> + * @pool     The zpool that the handle was allocated from
>> + * @handle   The handle to unmap
>> + *
>> + * This unmaps a previously mapped handle.  Any locks or other
>> + * actions that the implemenation took in zpool_map_handle()
>> + * will be undone here.  The memory area returned from
>> + * zpool_map_handle() should no longer be used after this.
>> + */
>> +void zpool_unmap_handle(struct zpool *pool, unsigned long handle);
>> +
>> +/**
>> + * zpool_get_total_size() - The total size of the pool
>> + * @pool     The zpool to check
>> + *
>> + * This returns the total size in bytes of the pool.
>> + *
>> + * Returns: Total size of the zpool in bytes.
>> + */
>> +u64 zpool_get_total_size(struct zpool *pool);
>> +
>> +
>> +/**
>> + * struct zpool_driver - driver implementation for zpool
>> + * @type:    name of the driver.
>> + * @list:    entry in the list of zpool drivers.
>> + * @create:  create a new pool.
>> + * @destroy: destroy a pool.
>> + * @malloc:  allocate mem from a pool.
>> + * @free:    free mem from a pool.
>> + * @shrink:  shrink the pool.
>> + * @map:     map a handle.
>> + * @unmap:   unmap a handle.
>> + * @total_size:      get total size of a pool.
>> + *
>> + * This is created by a zpool implementation and registered
>> + * with zpool.
>> + */
>> +struct zpool_driver {
>> +     char *type;
>> +     struct list_head list;
>> +
>> +     void *(*create)(gfp_t gfp, struct zpool_ops *ops);
>> +     void (*destroy)(void *pool);
>> +
>> +     int (*malloc)(void *pool, size_t size, unsigned long *handle);
>> +     void (*free)(void *pool, unsigned long handle);
>> +
>> +     int (*shrink)(void *pool, size_t size);
>> +
>> +     void *(*map)(void *pool, unsigned long handle,
>> +                             enum zpool_mapmode mm);
>> +     void (*unmap)(void *pool, unsigned long handle);
>> +
>> +     u64 (*total_size)(void *pool);
>> +};
>> +
>> +/**
>> + * zpool_register_driver() - register a zpool implementation.
>> + * @driver:  driver to register
>> + */
>> +void zpool_register_driver(struct zpool_driver *driver);
>> +
>> +/**
>> + * zpool_unregister_driver() - unregister a zpool implementation.
>> + * @driver:  driver to unregister.
>> + */
>> +void zpool_unregister_driver(struct zpool_driver *driver);
>> +
>> +/**
>> + * zpool_evict() - evict callback from a zpool implementation.
>> + * @pool:    pool to evict from.
>> + * @handle:  handle to evict.
>> + *
>> + * This can be used by zpool implementations to call the
>> + * user's evict zpool_ops struct evict callback.
>> + */
>> +int zpool_evict(void *pool, unsigned long handle);
>> +
>> +#endif
>> diff --git a/mm/Kconfig b/mm/Kconfig
>> index 7511b4a..00f7720 100644
>> --- a/mm/Kconfig
>> +++ b/mm/Kconfig
>> @@ -515,15 +515,17 @@ config CMA_DEBUG
>>         processing calls such as dma_alloc_from_contiguous().
>>         This option does not affect warning and error messages.
>>
>> -config ZBUD
>> -     tristate
>> -     default n
>> +config MEM_SOFT_DIRTY
>> +     bool "Track memory changes"
>> +     depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
>> +     select PROC_PAGE_MONITOR
>>       help
>> -       A special purpose allocator for storing compressed pages.
>> -       It is designed to store up to two compressed pages per physical
>> -       page.  While this design limits storage density, it has simple and
>> -       deterministic reclaim properties that make it preferable to a higher
>> -       density approach when reclaim will be used.
>> +       This option enables memory changes tracking by introducing a
>> +       soft-dirty bit on pte-s. This bit it set when someone writes
>> +       into a page just as regular dirty bit, but unlike the latter
>> +       it can be cleared by hands.
>> +
>> +       See Documentation/vm/soft-dirty.txt for more details.
>>
>>  config ZSWAP
>>       bool "Compressed cache for swap pages (EXPERIMENTAL)"
>> @@ -545,17 +547,22 @@ config ZSWAP
>>         they have not be fully explored on the large set of potential
>>         configurations and workloads that exist.
>>
>> -config MEM_SOFT_DIRTY
>> -     bool "Track memory changes"
>> -     depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
>> -     select PROC_PAGE_MONITOR
>> +config ZPOOL
>> +     tristate "Common API for compressed memory storage"
>> +     default n
>>       help
>> -       This option enables memory changes tracking by introducing a
>> -       soft-dirty bit on pte-s. This bit it set when someone writes
>> -       into a page just as regular dirty bit, but unlike the latter
>> -       it can be cleared by hands.
>> +       Compressed memory storage API.  This allows using either zbud or
>> +       zsmalloc.
>>
>> -       See Documentation/vm/soft-dirty.txt for more details.
>> +config ZBUD
>> +     tristate "Low density storage for compressed pages"
>> +     default n
>> +     help
>> +       A special purpose allocator for storing compressed pages.
>> +       It is designed to store up to two compressed pages per physical
>> +       page.  While this design limits storage density, it has simple and
>> +       deterministic reclaim properties that make it preferable to a higher
>> +       density approach when reclaim will be used.
>>
>>  config ZSMALLOC
>>       tristate "Memory allocator for compressed pages"
>> diff --git a/mm/Makefile b/mm/Makefile
>> index 2b6fff2..759db04 100644
>> --- a/mm/Makefile
>> +++ b/mm/Makefile
>> @@ -61,6 +61,7 @@ obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
>>  obj-$(CONFIG_CLEANCACHE) += cleancache.o
>>  obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
>>  obj-$(CONFIG_PAGE_OWNER) += pageowner.o
>> +obj-$(CONFIG_ZPOOL)  += zpool.o
>>  obj-$(CONFIG_ZBUD)   += zbud.o
>>  obj-$(CONFIG_ZSMALLOC)       += zsmalloc.o
>>  obj-$(CONFIG_GENERIC_EARLY_IOREMAP) += early_ioremap.o
>> diff --git a/mm/zpool.c b/mm/zpool.c
>> new file mode 100644
>> index 0000000..89ed71f
>> --- /dev/null
>> +++ b/mm/zpool.c
>> @@ -0,0 +1,197 @@
>> +/*
>> + * zpool memory storage api
>> + *
>> + * Copyright (C) 2014 Dan Streetman
>> + *
>> + * This is a common frontend for memory storage pool implementations.
>> + * Typically, this is used to store compressed memory.
>> + */
>> +
>> +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
>> +
>> +#include <linux/list.h>
>> +#include <linux/types.h>
>> +#include <linux/mm.h>
>> +#include <linux/slab.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/module.h>
>> +#include <linux/zpool.h>
>> +
>> +struct zpool {
>> +     char *type;
>> +
>> +     struct zpool_driver *driver;
>> +     void *pool;
>> +     struct zpool_ops *ops;
>> +
>> +     struct list_head list;
>> +};
>> +
>> +static LIST_HEAD(drivers_head);
>> +static DEFINE_SPINLOCK(drivers_lock);
>> +
>> +static LIST_HEAD(pools_head);
>> +static DEFINE_SPINLOCK(pools_lock);
>> +
>> +void zpool_register_driver(struct zpool_driver *driver)
>> +{
>> +     spin_lock(&drivers_lock);
>> +     list_add(&driver->list, &drivers_head);
>> +     spin_unlock(&drivers_lock);
>> +}
>> +EXPORT_SYMBOL(zpool_register_driver);
>> +
>> +void zpool_unregister_driver(struct zpool_driver *driver)
>> +{
>> +     spin_lock(&drivers_lock);
>> +     list_del(&driver->list);
>> +     spin_unlock(&drivers_lock);
>> +}
>> +EXPORT_SYMBOL(zpool_unregister_driver);
>> +
>> +int zpool_evict(void *pool, unsigned long handle)
>> +{
>> +     struct zpool *zpool;
>> +
>> +     spin_lock(&pools_lock);
>> +     list_for_each_entry(zpool, &pools_head, list) {
>
> You can do a container_of() here:
>
> zpool = container_of(pool, struct zpool, pool);

unfortunately, that's not true, since the driver pool isn't actually a
member of the struct zpool.  The struct zpool only has a pointer to
the driver pool.

I really wanted to use container_of(), but I think zbud/zsmalloc would
need alternate pool creation functions that create struct zpools of
the appropriate size with their pool embedded, and the
driver->create() function would need to alloc and return the entire
struct zpool, instead of just the driver pool.  Do you think that's a
better approach?  Or is there another better way I'm missing?


>
> Seth
>
>> +             if (zpool->pool == pool) {
>> +                     spin_unlock(&pools_lock);
>> +                     if (!zpool->ops || !zpool->ops->evict)
>> +                             return -EINVAL;
>> +                     return zpool->ops->evict(zpool, handle);
>> +             }
>> +     }
>> +     spin_unlock(&pools_lock);
>> +
>> +     return -ENOENT;
>> +}
>> +EXPORT_SYMBOL(zpool_evict);
>> +
>> +static struct zpool_driver *zpool_get_driver(char *type)
>> +{
>> +     struct zpool_driver *driver;
>> +
>> +     assert_spin_locked(&drivers_lock);
>> +     list_for_each_entry(driver, &drivers_head, list) {
>> +             if (!strcmp(driver->type, type))
>> +                     return driver;
>> +     }
>> +
>> +     return NULL;
>> +}
>> +
>> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
>> +                     struct zpool_ops *ops)
>> +{
>> +     struct zpool_driver *driver;
>> +     struct zpool *zpool;
>> +
>> +     pr_info("creating pool type %s\n", type);
>> +
>> +     spin_lock(&drivers_lock);
>> +     driver = zpool_get_driver(type);
>> +     spin_unlock(&drivers_lock);
>> +
>> +     if (!driver) {
>> +             request_module(type);
>> +             spin_lock(&drivers_lock);
>> +             driver = zpool_get_driver(type);
>> +             spin_unlock(&drivers_lock);
>> +     }
>> +
>> +     if (!driver) {
>> +             pr_err("no driver for type %s\n", type);
>> +             return NULL;
>> +     }
>> +
>> +     zpool = kmalloc(sizeof(*zpool), GFP_KERNEL);
>> +     if (!zpool) {
>> +             pr_err("couldn't create zpool - out of memory\n");
>> +             return NULL;
>> +     }
>> +
>> +     zpool->type = driver->type;
>> +     zpool->driver = driver;
>> +     zpool->pool = driver->create(flags, ops);
>> +     zpool->ops = ops;
>> +
>> +     if (!zpool->pool) {
>> +             pr_err("couldn't create %s pool\n", type);
>> +             kfree(zpool);
>> +             return NULL;
>> +     }
>> +
>> +     pr_info("created %s pool\n", type);
>> +
>> +     spin_lock(&pools_lock);
>> +     list_add(&zpool->list, &pools_head);
>> +     spin_unlock(&pools_lock);
>> +
>> +     return zpool;
>> +}
>> +
>> +void zpool_destroy_pool(struct zpool *zpool)
>> +{
>> +     pr_info("destroying pool type %s\n", zpool->type);
>> +
>> +     spin_lock(&pools_lock);
>> +     list_del(&zpool->list);
>> +     spin_unlock(&pools_lock);
>> +     zpool->driver->destroy(zpool->pool);
>> +     kfree(zpool);
>> +}
>> +
>> +char *zpool_get_type(struct zpool *zpool)
>> +{
>> +     return zpool->type;
>> +}
>> +
>> +int zpool_malloc(struct zpool *zpool, size_t size, unsigned long *handle)
>> +{
>> +     return zpool->driver->malloc(zpool->pool, size, handle);
>> +}
>> +
>> +void zpool_free(struct zpool *zpool, unsigned long handle)
>> +{
>> +     zpool->driver->free(zpool->pool, handle);
>> +}
>> +
>> +int zpool_shrink(struct zpool *zpool, size_t size)
>> +{
>> +     return zpool->driver->shrink(zpool->pool, size);
>> +}
>> +
>> +void *zpool_map_handle(struct zpool *zpool, unsigned long handle,
>> +                     enum zpool_mapmode mapmode)
>> +{
>> +     return zpool->driver->map(zpool->pool, handle, mapmode);
>> +}
>> +
>> +void zpool_unmap_handle(struct zpool *zpool, unsigned long handle)
>> +{
>> +     zpool->driver->unmap(zpool->pool, handle);
>> +}
>> +
>> +u64 zpool_get_total_size(struct zpool *zpool)
>> +{
>> +     return zpool->driver->total_size(zpool->pool);
>> +}
>> +
>> +static int __init init_zpool(void)
>> +{
>> +     pr_info("loaded\n");
>> +     return 0;
>> +}
>> +
>> +static void __exit exit_zpool(void)
>> +{
>> +     pr_info("unloaded\n");
>> +}
>> +
>> +module_init(init_zpool);
>> +module_exit(exit_zpool);
>> +
>> +MODULE_LICENSE("GPL");
>> +MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
>> +MODULE_DESCRIPTION("Common API for compressed memory storage");
>> --
>> 1.8.3.1
>>

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCH 6/6] mm/zpool: prevent zbud/zsmalloc from unloading when used
  2014-05-27 22:40       ` Seth Jennings
@ 2014-05-28  0:40         ` Dan Streetman
  0 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-05-28  0:40 UTC (permalink / raw)
  To: Seth Jennings
  Cc: Minchan Kim, Weijie Yang, Nitin Gupta, Andrew Morton, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Tue, May 27, 2014 at 6:40 PM, Seth Jennings <sjennings@variantweb.net> wrote:
> On Sat, May 24, 2014 at 03:06:09PM -0400, Dan Streetman wrote:
>> Add try_module_get() to pool creation functions for zbud and zsmalloc,
>> and module_put() to pool destruction functions, since they now can be
>> modules used via zpool.  Without usage counting, they could be unloaded
>> while pool(s) were active, resulting in an oops.
>
> I like the idea here, but what about doing this in the zpool layer? For
> me, it is kinda weird for a module to be taking a ref on itself.  Maybe
> this is excepted practice.  Is there precedent for this?

It's done in some places already:
git grep try_module_get\(THIS_MODULE | wc -l
83

but it definitely could be done in zpool, and since other users of
zbud/zsmalloc would be calling directly to their functions, instead of
indirectly by driver registration, I believe the module dependency
there would prevent zbud/zsmalloc unloading while a using module was
still loaded (if I understand module usage counting correctly).

>
> What about having the zbud/zsmalloc drivers pass their module pointers
> to zpool_register_driver() as an additional field in struct zpool_driver
> and have zpool take the reference?  Since zpool is the one in trouble if
> the driver is unloaded.

Yep this seems to be the other common way of doing it, with a ->owner
field in the registered struct.  Either way is fine with me, and zpool
definitely is the one in trouble if its driver is unloaded.  I'll
update for v4 of this patch set.

>
> Seth
>
>>
>> Signed-off-by: Dan Streetman <ddstreet@ieee.org>
>> Cc: Seth Jennings <sjennings@variantweb.net>
>> Cc: Minchan Kim <minchan@kernel.org>
>> Cc: Nitin Gupta <ngupta@vflare.org>
>> Cc: Weijie Yang <weijie.yang@samsung.com>
>> ---
>>
>> New for this patch set.
>>
>>  mm/zbud.c     | 5 +++++
>>  mm/zsmalloc.c | 5 +++++
>>  2 files changed, 10 insertions(+)
>>
>> diff --git a/mm/zbud.c b/mm/zbud.c
>> index 8a72cb1..2b3689c 100644
>> --- a/mm/zbud.c
>> +++ b/mm/zbud.c
>> @@ -282,6 +282,10 @@ struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops)
>>       pool = kmalloc(sizeof(struct zbud_pool), GFP_KERNEL);
>>       if (!pool)
>>               return NULL;
>> +     if (!try_module_get(THIS_MODULE)) {
>> +             kfree(pool);
>> +             return NULL;
>> +     }
>>       spin_lock_init(&pool->lock);
>>       for_each_unbuddied_list(i, 0)
>>               INIT_LIST_HEAD(&pool->unbuddied[i]);
>> @@ -302,6 +306,7 @@ struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops)
>>  void zbud_destroy_pool(struct zbud_pool *pool)
>>  {
>>       kfree(pool);
>> +     module_put(THIS_MODULE);
>>  }
>>
>>  /**
>> diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
>> index 07c3130..2cc2647 100644
>> --- a/mm/zsmalloc.c
>> +++ b/mm/zsmalloc.c
>> @@ -946,6 +946,10 @@ struct zs_pool *zs_create_pool(gfp_t flags)
>>       pool = kzalloc(ovhd_size, GFP_KERNEL);
>>       if (!pool)
>>               return NULL;
>> +     if (!try_module_get(THIS_MODULE)) {
>> +             kfree(pool);
>> +             return NULL;
>> +     }
>>
>>       for (i = 0; i < ZS_SIZE_CLASSES; i++) {
>>               int size;
>> @@ -985,6 +989,7 @@ void zs_destroy_pool(struct zs_pool *pool)
>>               }
>>       }
>>       kfree(pool);
>> +     module_put(THIS_MODULE);
>>  }
>>  EXPORT_SYMBOL_GPL(zs_destroy_pool);
>>
>> --
>> 1.8.3.1
>>

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv3 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc
  2014-05-28  0:06         ` Dan Streetman
@ 2014-05-29  3:48           ` Seth Jennings
  0 siblings, 0 replies; 65+ messages in thread
From: Seth Jennings @ 2014-05-29  3:48 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Minchan Kim, Weijie Yang, Nitin Gupta, Andrew Morton, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Tue, May 27, 2014 at 08:06:28PM -0400, Dan Streetman wrote:
> On Tue, May 27, 2014 at 6:06 PM, Seth Jennings <sjennings@variantweb.net> wrote:
> > On Sat, May 24, 2014 at 03:06:06PM -0400, Dan Streetman wrote:
<snip>
> >> + * Returns: 0 on success, negative value on error/failure.
> >> + */
> >> +int zpool_shrink(struct zpool *pool, size_t size);
> >
> > This should take a number of pages to be reclaimed, not a size.  The
> > user can evict their own object to reclaim a certain number of bytes
> > from the pool.  What the user can't do is reclaim a page since it is not
> > aware of the arrangement of the stored objects in the memory pages.
> 
> Yes I suppose that's true, I'll update it for v4...
> 
> >
> > Also in patch 5/6 of six I see:
> >
> > -               if (zbud_reclaim_page(zswap_pool, 8)) {
> > +               if (zpool_shrink(zswap_pool, PAGE_SIZE)) {
> >
> > but then in 4/6 I see:
> >
> > +int zbud_zpool_shrink(void *pool, size_t size)
> > +{
> > +       return zbud_reclaim_page(pool, 8);
> > +}
> >
> > That is why it didn't completely explode on you since the zbud logic
> > is still reclaiming pages.
> 
> Ha, yes clearly I neglected to translate between the size and the
> number of pages there, oops!
> 
> On this topic - 8 retries seems very arbitrary.  Does it make sense to
> include retrying in zbud and/or zpool at all?  The caller can easily
> retry any number of times themselves, especially since zbud (and
> eventually zsmalloc) will return -EAGAIN if the caller should retry.

Yeah, the retries argument in the zbud API isn't good.  You can change
the zbud_reclaim_page() to just try once and return -EAGAIN if you want
and I'll be in favor of that.

That did make me think of something else though.  The zpool API is
zpool_shrink() with, what will be, a number of pages.  The zbud API is
zbud_reclaim_page() which, as the name implies, reclaims one page.  So
it seems that you would need a loop in zbud_zpool_shrink() to try to
reclaim a multiple number of pages.

> 
> >
> >> +
> >> +/**
> >> + * zpool_map_handle() - Map a previously allocated handle into memory
> >> + * @pool     The zpool that the handle was allocated from
> >> + * @handle   The handle to map
> >> + * @mm       How the memory should be mapped
> >> + *
<snip>
> >> +int zpool_evict(void *pool, unsigned long handle)
> >> +{
> >> +     struct zpool *zpool;
> >> +
> >> +     spin_lock(&pools_lock);
> >> +     list_for_each_entry(zpool, &pools_head, list) {
> >
> > You can do a container_of() here:
> >
> > zpool = container_of(pool, struct zpool, pool);
> 
> unfortunately, that's not true, since the driver pool isn't actually a
> member of the struct zpool.  The struct zpool only has a pointer to
> the driver pool.

Ah yes, got my user API vs driver API crossed here :-/

Meh, can't think of a better way for now and it doesn't cause contention
on the hot paths so... works for me.

Seth

> 
> I really wanted to use container_of(), but I think zbud/zsmalloc would
> need alternate pool creation functions that create struct zpools of
> the appropriate size with their pool embedded, and the
> driver->create() function would need to alloc and return the entire
> struct zpool, instead of just the driver pool.  Do you think that's a
> better approach?  Or is there another better way I'm missing?
> 

^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv4 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc
  2014-05-24 19:06   ` [PATCHv3 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
                       ` (6 preceding siblings ...)
  2014-05-27 22:44     ` [PATCHv3 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc Seth Jennings
@ 2014-06-02 22:19     ` Dan Streetman
  2014-06-02 22:19       ` [PATCHv2 1/6] mm/zbud: zbud_alloc() minor param change Dan Streetman
                         ` (9 more replies)
  7 siblings, 10 replies; 65+ messages in thread
From: Dan Streetman @ 2014-06-02 22:19 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

In order to allow zswap users to choose between zbud and zsmalloc for
the compressed storage pool, this patch set adds a new api "zpool" that
provides an interface to both zbud and zsmalloc.  Only minor changes
to zbud's interface were needed.  This does not include implementing
shrinking in zsmalloc, which will be sent separately.

I believe Seth originally was using zsmalloc for swap, but there were
concerns about how significant the impact of shrinking zsmalloc would
be when zswap had to start reclaiming pages.  That still may be an
issue, but this at least allows users to choose themselves whether
they want a lower-density or higher-density compressed storage medium.
At least for situations where zswap reclaim is never or rarely reached,
it probably makes sense to use the higher density of zsmalloc.

Note this patch set does not change zram to use zpool, although that
change should be possible as well.

---

Changes since v3 : https://lkml.org/lkml/2014/5/24/130
  -In zpool_shrink() use # pages instead of # bytes
  -Add reclaimed param to zpool_shrink() to indicate to caller
   # pages actually reclaimed
  -move module usage counting to zpool, from zbud/zsmalloc
  -update zbud_zpool_shrink() to call zbud_reclaim_page() in a
   loop until requested # pages have been reclaimed (or error)

Changes since v2 : https://lkml.org/lkml/2014/5/7/927
  -Change zpool to use driver registration instead of hardcoding
   implementations
  -Add module use counting in zbud/zsmalloc

Changes since v1 https://lkml.org/lkml/2014/4/19/97
 -remove zsmalloc shrinking
 -change zbud size param type from unsigned int to size_t
 -remove zpool fallback creation
 -zswap manually falls back to zbud if specified type fails


Dan Streetman (6):
  mm/zbud: zbud_alloc() minor param change
  mm/zbud: change zbud_alloc size type to size_t
  mm/zpool: implement common zpool api to zbud/zsmalloc
  mm/zpool: zbud/zsmalloc implement zpool
  mm/zpool: update zswap to use zpool
  mm/zpool: prevent zbud/zsmalloc from unloading when used

 include/linux/zbud.h  |   2 +-
 include/linux/zpool.h | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++
 mm/Kconfig            |  43 ++++++----
 mm/Makefile           |   1 +
 mm/zbud.c             | 123 +++++++++++++++++++++++----
 mm/zpool.c            | 206 ++++++++++++++++++++++++++++++++++++++++++++++
 mm/zsmalloc.c         |  83 +++++++++++++++++++
 mm/zswap.c            |  76 ++++++++++-------
 8 files changed, 694 insertions(+), 64 deletions(-)
 create mode 100644 include/linux/zpool.h
 create mode 100644 mm/zpool.c

-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv2 1/6] mm/zbud: zbud_alloc() minor param change
  2014-06-02 22:19     ` [PATCHv4 " Dan Streetman
@ 2014-06-02 22:19       ` Dan Streetman
  2014-06-23 21:19         ` Andrew Morton
  2014-06-02 22:19       ` [PATCH 2/6] mm/zbud: change zbud_alloc size type to size_t Dan Streetman
                         ` (8 subsequent siblings)
  9 siblings, 1 reply; 65+ messages in thread
From: Dan Streetman @ 2014-06-02 22:19 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Change zbud to store gfp_t flags passed at pool creation to use for
each alloc; this allows the api to be closer to the existing zsmalloc
interface, and the only current zbud user (zswap) uses the same gfp
flags for all allocs.  Update zswap to use changed interface.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Acked-by: Seth Jennings <sjennings@variantweb.net>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

No change since v2 : https://lkml.org/lkml/2014/5/7/727

Changes since v1 https://lkml.org/lkml/2014/4/19/98
 -context changes only; zbud_alloc parameter type changed since
  last patch

 include/linux/zbud.h |  2 +-
 mm/zbud.c            | 27 +++++++++++++++------------
 mm/zswap.c           |  6 +++---
 3 files changed, 19 insertions(+), 16 deletions(-)

diff --git a/include/linux/zbud.h b/include/linux/zbud.h
index 13af0d4..0b2534e 100644
--- a/include/linux/zbud.h
+++ b/include/linux/zbud.h
@@ -11,7 +11,7 @@ struct zbud_ops {
 
 struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops);
 void zbud_destroy_pool(struct zbud_pool *pool);
-int zbud_alloc(struct zbud_pool *pool, unsigned int size, gfp_t gfp,
+int zbud_alloc(struct zbud_pool *pool, unsigned int size,
 	unsigned long *handle);
 void zbud_free(struct zbud_pool *pool, unsigned long handle);
 int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries);
diff --git a/mm/zbud.c b/mm/zbud.c
index 01df13a..847c01c 100644
--- a/mm/zbud.c
+++ b/mm/zbud.c
@@ -94,6 +94,7 @@ struct zbud_pool {
 	struct list_head lru;
 	u64 pages_nr;
 	struct zbud_ops *ops;
+	gfp_t gfp;
 };
 
 /*
@@ -193,9 +194,12 @@ static int num_free_chunks(struct zbud_header *zhdr)
 *****************/
 /**
  * zbud_create_pool() - create a new zbud pool
- * @gfp:	gfp flags when allocating the zbud pool structure
+ * @gfp:	gfp flags when growing the pool
  * @ops:	user-defined operations for the zbud pool
  *
+ * gfp should not set __GFP_HIGHMEM as highmem pages cannot be used
+ * as zbud pool pages.
+ *
  * Return: pointer to the new zbud pool or NULL if the metadata allocation
  * failed.
  */
@@ -204,7 +208,9 @@ struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops)
 	struct zbud_pool *pool;
 	int i;
 
-	pool = kmalloc(sizeof(struct zbud_pool), gfp);
+	if (gfp & __GFP_HIGHMEM)
+		return NULL;
+	pool = kmalloc(sizeof(struct zbud_pool), GFP_KERNEL);
 	if (!pool)
 		return NULL;
 	spin_lock_init(&pool->lock);
@@ -214,6 +220,7 @@ struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops)
 	INIT_LIST_HEAD(&pool->lru);
 	pool->pages_nr = 0;
 	pool->ops = ops;
+	pool->gfp = gfp;
 	return pool;
 }
 
@@ -232,7 +239,6 @@ void zbud_destroy_pool(struct zbud_pool *pool)
  * zbud_alloc() - allocates a region of a given size
  * @pool:	zbud pool from which to allocate
  * @size:	size in bytes of the desired allocation
- * @gfp:	gfp flags used if the pool needs to grow
  * @handle:	handle of the new allocation
  *
  * This function will attempt to find a free region in the pool large enough to
@@ -240,14 +246,11 @@ void zbud_destroy_pool(struct zbud_pool *pool)
  * performed first. If no suitable free region is found, then a new page is
  * allocated and added to the pool to satisfy the request.
  *
- * gfp should not set __GFP_HIGHMEM as highmem pages cannot be used
- * as zbud pool pages.
- *
- * Return: 0 if success and handle is set, otherwise -EINVAL if the size or
- * gfp arguments are invalid or -ENOMEM if the pool was unable to allocate
- * a new page.
+ * Return: 0 if success and @handle is set, -ENOSPC if the @size is too large,
+ * -EINVAL if the @size is 0, or -ENOMEM if the pool was unable to
+ * allocate a new page.
  */
-int zbud_alloc(struct zbud_pool *pool, unsigned int size, gfp_t gfp,
+int zbud_alloc(struct zbud_pool *pool, unsigned int size,
 			unsigned long *handle)
 {
 	int chunks, i, freechunks;
@@ -255,7 +258,7 @@ int zbud_alloc(struct zbud_pool *pool, unsigned int size, gfp_t gfp,
 	enum buddy bud;
 	struct page *page;
 
-	if (!size || (gfp & __GFP_HIGHMEM))
+	if (!size)
 		return -EINVAL;
 	if (size > PAGE_SIZE - ZHDR_SIZE_ALIGNED - CHUNK_SIZE)
 		return -ENOSPC;
@@ -279,7 +282,7 @@ int zbud_alloc(struct zbud_pool *pool, unsigned int size, gfp_t gfp,
 
 	/* Couldn't find unbuddied zbud page, create new one */
 	spin_unlock(&pool->lock);
-	page = alloc_page(gfp);
+	page = alloc_page(pool->gfp);
 	if (!page)
 		return -ENOMEM;
 	spin_lock(&pool->lock);
diff --git a/mm/zswap.c b/mm/zswap.c
index aeaef0f..1cc6770 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -679,8 +679,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 
 	/* store */
 	len = dlen + sizeof(struct zswap_header);
-	ret = zbud_alloc(zswap_pool, len, __GFP_NORETRY | __GFP_NOWARN,
-		&handle);
+	ret = zbud_alloc(zswap_pool, len, &handle);
 	if (ret == -ENOSPC) {
 		zswap_reject_compress_poor++;
 		goto freepage;
@@ -900,7 +899,8 @@ static int __init init_zswap(void)
 
 	pr_info("loading zswap\n");
 
-	zswap_pool = zbud_create_pool(GFP_KERNEL, &zswap_zbud_ops);
+	zswap_pool = zbud_create_pool(__GFP_NORETRY | __GFP_NOWARN,
+			&zswap_zbud_ops);
 	if (!zswap_pool) {
 		pr_err("zbud pool creation failed\n");
 		goto error;
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCH 2/6] mm/zbud: change zbud_alloc size type to size_t
  2014-06-02 22:19     ` [PATCHv4 " Dan Streetman
  2014-06-02 22:19       ` [PATCHv2 1/6] mm/zbud: zbud_alloc() minor param change Dan Streetman
@ 2014-06-02 22:19       ` Dan Streetman
  2014-06-02 22:19       ` [PATCHv4 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
                         ` (7 subsequent siblings)
  9 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-06-02 22:19 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Change the type of the zbud_alloc() size param from unsigned int
to size_t.

Technically, this should not make any difference, as the zbud
implementation already restricts the size to well within either
type's limits; but as zsmalloc (and kmalloc) use size_t, and
zpool will use size_t, this brings the size parameter type
in line with zsmalloc/zpool.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Acked-by: Seth Jennings <sjennings@variantweb.net>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

No change since v1 : https://lkml.org/lkml/2014/5/7/757

 include/linux/zbud.h | 2 +-
 mm/zbud.c            | 5 ++---
 2 files changed, 3 insertions(+), 4 deletions(-)

diff --git a/include/linux/zbud.h b/include/linux/zbud.h
index 0b2534e..1e9cb57 100644
--- a/include/linux/zbud.h
+++ b/include/linux/zbud.h
@@ -11,7 +11,7 @@ struct zbud_ops {
 
 struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops);
 void zbud_destroy_pool(struct zbud_pool *pool);
-int zbud_alloc(struct zbud_pool *pool, unsigned int size,
+int zbud_alloc(struct zbud_pool *pool, size_t size,
 	unsigned long *handle);
 void zbud_free(struct zbud_pool *pool, unsigned long handle);
 int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries);
diff --git a/mm/zbud.c b/mm/zbud.c
index 847c01c..dd13665 100644
--- a/mm/zbud.c
+++ b/mm/zbud.c
@@ -123,7 +123,7 @@ enum buddy {
 };
 
 /* Converts an allocation size in bytes to size in zbud chunks */
-static int size_to_chunks(int size)
+static int size_to_chunks(size_t size)
 {
 	return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT;
 }
@@ -250,8 +250,7 @@ void zbud_destroy_pool(struct zbud_pool *pool)
  * -EINVAL if the @size is 0, or -ENOMEM if the pool was unable to
  * allocate a new page.
  */
-int zbud_alloc(struct zbud_pool *pool, unsigned int size,
-			unsigned long *handle)
+int zbud_alloc(struct zbud_pool *pool, size_t size, unsigned long *handle)
 {
 	int chunks, i, freechunks;
 	struct zbud_header *zhdr = NULL;
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCHv4 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc
  2014-06-02 22:19     ` [PATCHv4 " Dan Streetman
  2014-06-02 22:19       ` [PATCHv2 1/6] mm/zbud: zbud_alloc() minor param change Dan Streetman
  2014-06-02 22:19       ` [PATCH 2/6] mm/zbud: change zbud_alloc size type to size_t Dan Streetman
@ 2014-06-02 22:19       ` Dan Streetman
  2014-06-23 21:46         ` Andrew Morton
  2014-06-02 22:19       ` [PATCHv2 4/6] mm/zpool: zbud/zsmalloc implement zpool Dan Streetman
                         ` (6 subsequent siblings)
  9 siblings, 1 reply; 65+ messages in thread
From: Dan Streetman @ 2014-06-02 22:19 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Add zpool api.

zpool provides an interface for memory storage, typically of compressed
memory.  Users can select what backend to use; currently the only
implementations are zbud, a low density implementation with up to
two compressed pages per storage page, and zsmalloc, a higher density
implementation with multiple compressed pages per storage page.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Cc: Seth Jennings <sjennings@variantweb.net>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Nitin Gupta <ngupta@vflare.org>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

Note this patch set is against the mmotm tree at
git://git.cmpxchg.org/linux-mmotm.git
This patch may need context changes to the -next or other trees.

Changes since v3 : https://lkml.org/lkml/2014/5/24/135
  -change zpool_shrink() to use # pages instead of # bytes
  -change zpool_shrink() to update *reclaimed param with
   number of pages actually reclaimed

Changes since v2 : https://lkml.org/lkml/2014/5/7/733
  -Remove hardcoded zbud/zsmalloc implementations
  -Add driver (un)register functions

Changes since v1 https://lkml.org/lkml/2014/4/19/101
 -add some pr_info() during creation and pr_err() on errors
 -remove zpool code to call zs_shrink(), since zsmalloc shrinking
  was removed from this patchset
 -remove fallback; only specified pool type will be tried
 -pr_fmt() is defined in zpool to prefix zpool: in any pr_XXX() calls

 include/linux/zpool.h | 219 ++++++++++++++++++++++++++++++++++++++++++++++++++
 mm/Kconfig            |  41 ++++++----
 mm/Makefile           |   1 +
 mm/zpool.c            | 198 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 442 insertions(+), 17 deletions(-)
 create mode 100644 include/linux/zpool.h
 create mode 100644 mm/zpool.c

diff --git a/include/linux/zpool.h b/include/linux/zpool.h
new file mode 100644
index 0000000..a528f7c
--- /dev/null
+++ b/include/linux/zpool.h
@@ -0,0 +1,219 @@
+/*
+ * zpool memory storage api
+ *
+ * Copyright (C) 2014 Dan Streetman
+ *
+ * This is a common frontend for the zbud and zsmalloc memory
+ * storage pool implementations.  Typically, this is used to
+ * store compressed memory.
+ */
+
+#ifndef _ZPOOL_H_
+#define _ZPOOL_H_
+
+struct zpool;
+
+struct zpool_ops {
+	int (*evict)(struct zpool *pool, unsigned long handle);
+};
+
+/*
+ * Control how a handle is mapped.  It will be ignored if the
+ * implementation does not support it.  Its use is optional.
+ * Note that this does not refer to memory protection, it
+ * refers to how the memory will be copied in/out if copying
+ * is necessary during mapping; read-write is the safest as
+ * it copies the existing memory in on map, and copies the
+ * changed memory back out on unmap.  Write-only does not copy
+ * in the memory and should only be used for initialization.
+ * If in doubt, use ZPOOL_MM_DEFAULT which is read-write.
+ */
+enum zpool_mapmode {
+	ZPOOL_MM_RW, /* normal read-write mapping */
+	ZPOOL_MM_RO, /* read-only (no copy-out at unmap time) */
+	ZPOOL_MM_WO, /* write-only (no copy-in at map time) */
+
+	ZPOOL_MM_DEFAULT = ZPOOL_MM_RW
+};
+
+/**
+ * zpool_create_pool() - Create a new zpool
+ * @type	The type of the zpool to create (e.g. zbud, zsmalloc)
+ * @flags	What GFP flags should be used when the zpool allocates memory.
+ * @ops		The optional ops callback.
+ *
+ * This creates a new zpool of the specified type.  The zpool will use the
+ * given flags when allocating any memory.  If the ops param is NULL, then
+ * the created zpool will not be shrinkable.
+ *
+ * Returns: New zpool on success, NULL on failure.
+ */
+struct zpool *zpool_create_pool(char *type, gfp_t flags,
+			struct zpool_ops *ops);
+
+/**
+ * zpool_get_type() - Get the type of the zpool
+ * @pool	The zpool to check
+ *
+ * This returns the type of the pool.
+ *
+ * Returns: The type of zpool.
+ */
+char *zpool_get_type(struct zpool *pool);
+
+/**
+ * zpool_destroy_pool() - Destroy a zpool
+ * @pool	The zpool to destroy.
+ *
+ * This destroys an existing zpool.  The zpool should not be in use.
+ */
+void zpool_destroy_pool(struct zpool *pool);
+
+/**
+ * zpool_malloc() - Allocate memory
+ * @pool	The zpool to allocate from.
+ * @size	The amount of memory to allocate.
+ * @handle	Pointer to the handle to set
+ *
+ * This allocates the requested amount of memory from the pool.
+ * The provided @handle will be set to the allocated object handle.
+ *
+ * Returns: 0 on success, negative value on error.
+ */
+int zpool_malloc(struct zpool *pool, size_t size, unsigned long *handle);
+
+/**
+ * zpool_free() - Free previously allocated memory
+ * @pool	The zpool that allocated the memory.
+ * @handle	The handle to the memory to free.
+ *
+ * This frees previously allocated memory.  This does not guarantee
+ * that the pool will actually free memory, only that the memory
+ * in the pool will become available for use by the pool.
+ */
+void zpool_free(struct zpool *pool, unsigned long handle);
+
+/**
+ * zpool_shrink() - Shrink the pool size
+ * @pool	The zpool to shrink.
+ * @pages	The number of pages to shrink the pool.
+ * @reclaimed	The number of pages successfully evicted.
+ *
+ * This attempts to shrink the actual memory size of the pool
+ * by evicting currently used handle(s).  If the pool was
+ * created with no zpool_ops, or the evict call fails for any
+ * of the handles, this will fail.  If non-NULL, the @reclaimed
+ * parameter will be set to the number of pages reclaimed,
+ * which may be more than the number of pages requested.
+ *
+ * Returns: 0 on success, negative value on error/failure.
+ */
+int zpool_shrink(struct zpool *pool, unsigned int pages,
+			unsigned int *reclaimed);
+
+/**
+ * zpool_map_handle() - Map a previously allocated handle into memory
+ * @pool	The zpool that the handle was allocated from
+ * @handle	The handle to map
+ * @mm	How the memory should be mapped
+ *
+ * This maps a previously allocated handle into memory.  The @mm
+ * param indicates to the implemenation how the memory will be
+ * used, i.e. read-only, write-only, read-write.  If the
+ * implementation does not support it, the memory will be treated
+ * as read-write.
+ *
+ * This may hold locks, disable interrupts, and/or preemption,
+ * and the zpool_unmap_handle() must be called to undo those
+ * actions.  The code that uses the mapped handle should complete
+ * its operatons on the mapped handle memory quickly and unmap
+ * as soon as possible.  Multiple handles should not be mapped
+ * concurrently on a cpu.
+ *
+ * Returns: A pointer to the handle's mapped memory area.
+ */
+void *zpool_map_handle(struct zpool *pool, unsigned long handle,
+			enum zpool_mapmode mm);
+
+/**
+ * zpool_unmap_handle() - Unmap a previously mapped handle
+ * @pool	The zpool that the handle was allocated from
+ * @handle	The handle to unmap
+ *
+ * This unmaps a previously mapped handle.  Any locks or other
+ * actions that the implemenation took in zpool_map_handle()
+ * will be undone here.  The memory area returned from
+ * zpool_map_handle() should no longer be used after this.
+ */
+void zpool_unmap_handle(struct zpool *pool, unsigned long handle);
+
+/**
+ * zpool_get_total_size() - The total size of the pool
+ * @pool	The zpool to check
+ *
+ * This returns the total size in bytes of the pool.
+ *
+ * Returns: Total size of the zpool in bytes.
+ */
+u64 zpool_get_total_size(struct zpool *pool);
+
+
+/**
+ * struct zpool_driver - driver implementation for zpool
+ * @type:	name of the driver.
+ * @list:	entry in the list of zpool drivers.
+ * @create:	create a new pool.
+ * @destroy:	destroy a pool.
+ * @malloc:	allocate mem from a pool.
+ * @free:	free mem from a pool.
+ * @shrink:	shrink the pool.
+ * @map:	map a handle.
+ * @unmap:	unmap a handle.
+ * @total_size:	get total size of a pool.
+ *
+ * This is created by a zpool implementation and registered
+ * with zpool.
+ */
+struct zpool_driver {
+	char *type;
+	struct list_head list;
+
+	void *(*create)(gfp_t gfp, struct zpool_ops *ops);
+	void (*destroy)(void *pool);
+
+	int (*malloc)(void *pool, size_t size, unsigned long *handle);
+	void (*free)(void *pool, unsigned long handle);
+
+	int (*shrink)(void *pool, unsigned int pages,
+				unsigned int *reclaimed);
+
+	void *(*map)(void *pool, unsigned long handle,
+				enum zpool_mapmode mm);
+	void (*unmap)(void *pool, unsigned long handle);
+
+	u64 (*total_size)(void *pool);
+};
+
+/**
+ * zpool_register_driver() - register a zpool implementation.
+ * @driver:	driver to register
+ */
+void zpool_register_driver(struct zpool_driver *driver);
+
+/**
+ * zpool_unregister_driver() - unregister a zpool implementation.
+ * @driver:	driver to unregister.
+ */
+void zpool_unregister_driver(struct zpool_driver *driver);
+
+/**
+ * zpool_evict() - evict callback from a zpool implementation.
+ * @pool:	pool to evict from.
+ * @handle:	handle to evict.
+ *
+ * This can be used by zpool implementations to call the
+ * user's evict zpool_ops struct evict callback.
+ */
+int zpool_evict(void *pool, unsigned long handle);
+
+#endif
diff --git a/mm/Kconfig b/mm/Kconfig
index 7511b4a..00f7720 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -515,15 +515,17 @@ config CMA_DEBUG
 	  processing calls such as dma_alloc_from_contiguous().
 	  This option does not affect warning and error messages.
 
-config ZBUD
-	tristate
-	default n
+config MEM_SOFT_DIRTY
+	bool "Track memory changes"
+	depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
+	select PROC_PAGE_MONITOR
 	help
-	  A special purpose allocator for storing compressed pages.
-	  It is designed to store up to two compressed pages per physical
-	  page.  While this design limits storage density, it has simple and
-	  deterministic reclaim properties that make it preferable to a higher
-	  density approach when reclaim will be used.
+	  This option enables memory changes tracking by introducing a
+	  soft-dirty bit on pte-s. This bit it set when someone writes
+	  into a page just as regular dirty bit, but unlike the latter
+	  it can be cleared by hands.
+
+	  See Documentation/vm/soft-dirty.txt for more details.
 
 config ZSWAP
 	bool "Compressed cache for swap pages (EXPERIMENTAL)"
@@ -545,17 +547,22 @@ config ZSWAP
 	  they have not be fully explored on the large set of potential
 	  configurations and workloads that exist.
 
-config MEM_SOFT_DIRTY
-	bool "Track memory changes"
-	depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
-	select PROC_PAGE_MONITOR
+config ZPOOL
+	tristate "Common API for compressed memory storage"
+	default n
 	help
-	  This option enables memory changes tracking by introducing a
-	  soft-dirty bit on pte-s. This bit it set when someone writes
-	  into a page just as regular dirty bit, but unlike the latter
-	  it can be cleared by hands.
+	  Compressed memory storage API.  This allows using either zbud or
+	  zsmalloc.
 
-	  See Documentation/vm/soft-dirty.txt for more details.
+config ZBUD
+	tristate "Low density storage for compressed pages"
+	default n
+	help
+	  A special purpose allocator for storing compressed pages.
+	  It is designed to store up to two compressed pages per physical
+	  page.  While this design limits storage density, it has simple and
+	  deterministic reclaim properties that make it preferable to a higher
+	  density approach when reclaim will be used.
 
 config ZSMALLOC
 	tristate "Memory allocator for compressed pages"
diff --git a/mm/Makefile b/mm/Makefile
index 2b6fff2..759db04 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -61,6 +61,7 @@ obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
 obj-$(CONFIG_CLEANCACHE) += cleancache.o
 obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
 obj-$(CONFIG_PAGE_OWNER) += pageowner.o
+obj-$(CONFIG_ZPOOL)	+= zpool.o
 obj-$(CONFIG_ZBUD)	+= zbud.o
 obj-$(CONFIG_ZSMALLOC)	+= zsmalloc.o
 obj-$(CONFIG_GENERIC_EARLY_IOREMAP) += early_ioremap.o
diff --git a/mm/zpool.c b/mm/zpool.c
new file mode 100644
index 0000000..578c379
--- /dev/null
+++ b/mm/zpool.c
@@ -0,0 +1,198 @@
+/*
+ * zpool memory storage api
+ *
+ * Copyright (C) 2014 Dan Streetman
+ *
+ * This is a common frontend for memory storage pool implementations.
+ * Typically, this is used to store compressed memory.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/zpool.h>
+
+struct zpool {
+	char *type;
+
+	struct zpool_driver *driver;
+	void *pool;
+	struct zpool_ops *ops;
+
+	struct list_head list;
+};
+
+static LIST_HEAD(drivers_head);
+static DEFINE_SPINLOCK(drivers_lock);
+
+static LIST_HEAD(pools_head);
+static DEFINE_SPINLOCK(pools_lock);
+
+void zpool_register_driver(struct zpool_driver *driver)
+{
+	spin_lock(&drivers_lock);
+	list_add(&driver->list, &drivers_head);
+	spin_unlock(&drivers_lock);
+}
+EXPORT_SYMBOL(zpool_register_driver);
+
+void zpool_unregister_driver(struct zpool_driver *driver)
+{
+	spin_lock(&drivers_lock);
+	list_del(&driver->list);
+	spin_unlock(&drivers_lock);
+}
+EXPORT_SYMBOL(zpool_unregister_driver);
+
+int zpool_evict(void *pool, unsigned long handle)
+{
+	struct zpool *zpool;
+
+	spin_lock(&pools_lock);
+	list_for_each_entry(zpool, &pools_head, list) {
+		if (zpool->pool == pool) {
+			spin_unlock(&pools_lock);
+			if (!zpool->ops || !zpool->ops->evict)
+				return -EINVAL;
+			return zpool->ops->evict(zpool, handle);
+		}
+	}
+	spin_unlock(&pools_lock);
+
+	return -ENOENT;
+}
+EXPORT_SYMBOL(zpool_evict);
+
+static struct zpool_driver *zpool_get_driver(char *type)
+{
+	struct zpool_driver *driver;
+
+	assert_spin_locked(&drivers_lock);
+	list_for_each_entry(driver, &drivers_head, list) {
+		if (!strcmp(driver->type, type))
+			return driver;
+	}
+
+	return NULL;
+}
+
+struct zpool *zpool_create_pool(char *type, gfp_t flags,
+			struct zpool_ops *ops)
+{
+	struct zpool_driver *driver;
+	struct zpool *zpool;
+
+	pr_info("creating pool type %s\n", type);
+
+	spin_lock(&drivers_lock);
+	driver = zpool_get_driver(type);
+	spin_unlock(&drivers_lock);
+
+	if (!driver) {
+		request_module(type);
+		spin_lock(&drivers_lock);
+		driver = zpool_get_driver(type);
+		spin_unlock(&drivers_lock);
+	}
+
+	if (!driver) {
+		pr_err("no driver for type %s\n", type);
+		return NULL;
+	}
+
+	zpool = kmalloc(sizeof(*zpool), GFP_KERNEL);
+	if (!zpool) {
+		pr_err("couldn't create zpool - out of memory\n");
+		return NULL;
+	}
+
+	zpool->type = driver->type;
+	zpool->driver = driver;
+	zpool->pool = driver->create(flags, ops);
+	zpool->ops = ops;
+
+	if (!zpool->pool) {
+		pr_err("couldn't create %s pool\n", type);
+		kfree(zpool);
+		return NULL;
+	}
+
+	pr_info("created %s pool\n", type);
+
+	spin_lock(&pools_lock);
+	list_add(&zpool->list, &pools_head);
+	spin_unlock(&pools_lock);
+
+	return zpool;
+}
+
+void zpool_destroy_pool(struct zpool *zpool)
+{
+	pr_info("destroying pool type %s\n", zpool->type);
+
+	spin_lock(&pools_lock);
+	list_del(&zpool->list);
+	spin_unlock(&pools_lock);
+	zpool->driver->destroy(zpool->pool);
+	kfree(zpool);
+}
+
+char *zpool_get_type(struct zpool *zpool)
+{
+	return zpool->type;
+}
+
+int zpool_malloc(struct zpool *zpool, size_t size, unsigned long *handle)
+{
+	return zpool->driver->malloc(zpool->pool, size, handle);
+}
+
+void zpool_free(struct zpool *zpool, unsigned long handle)
+{
+	zpool->driver->free(zpool->pool, handle);
+}
+
+int zpool_shrink(struct zpool *zpool, unsigned int pages,
+			unsigned int *reclaimed)
+{
+	return zpool->driver->shrink(zpool->pool, pages, reclaimed);
+}
+
+void *zpool_map_handle(struct zpool *zpool, unsigned long handle,
+			enum zpool_mapmode mapmode)
+{
+	return zpool->driver->map(zpool->pool, handle, mapmode);
+}
+
+void zpool_unmap_handle(struct zpool *zpool, unsigned long handle)
+{
+	zpool->driver->unmap(zpool->pool, handle);
+}
+
+u64 zpool_get_total_size(struct zpool *zpool)
+{
+	return zpool->driver->total_size(zpool->pool);
+}
+
+static int __init init_zpool(void)
+{
+	pr_info("loaded\n");
+	return 0;
+}
+
+static void __exit exit_zpool(void)
+{
+	pr_info("unloaded\n");
+}
+
+module_init(init_zpool);
+module_exit(exit_zpool);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
+MODULE_DESCRIPTION("Common API for compressed memory storage");
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCHv2 4/6] mm/zpool: zbud/zsmalloc implement zpool
  2014-06-02 22:19     ` [PATCHv4 " Dan Streetman
                         ` (2 preceding siblings ...)
  2014-06-02 22:19       ` [PATCHv4 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
@ 2014-06-02 22:19       ` Dan Streetman
  2014-06-02 22:19       ` [PATCHv4 5/6] mm/zpool: update zswap to use zpool Dan Streetman
                         ` (5 subsequent siblings)
  9 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-06-02 22:19 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Update zbud and zsmalloc to implement the zpool api.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Cc: Seth Jennings <sjennings@variantweb.net>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Nitin Gupta <ngupta@vflare.org>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

Note to Seth: We talked about removing the retries parameter from
zbud_reclaim_page(), but I did not include that in this patch.
I'll send a separate patch for that.

Changes since v1 : https://lkml.org/lkml/2014/5/24/136
  -Update zbud_zpool_shrink() to call zbud_reclaim_page()
   in a loop until number of pages requested has been
   reclaimed, or error
  -Update zbud_zpool_shrink() to update passed *reclaimed
   param with # pages actually reclaimed
  -Update zs_pool_shrink() with new param, although function
   is not implemented yet

 mm/zbud.c     | 92 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 mm/zsmalloc.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 174 insertions(+)

diff --git a/mm/zbud.c b/mm/zbud.c
index dd13665..645379e 100644
--- a/mm/zbud.c
+++ b/mm/zbud.c
@@ -51,6 +51,7 @@
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/zbud.h>
+#include <linux/zpool.h>
 
 /*****************
  * Structures
@@ -114,6 +115,88 @@ struct zbud_header {
 };
 
 /*****************
+ * zpool
+ ****************/
+
+#ifdef CONFIG_ZPOOL
+
+static int zbud_zpool_evict(struct zbud_pool *pool, unsigned long handle)
+{
+	return zpool_evict(pool, handle);
+}
+
+static struct zbud_ops zbud_zpool_ops = {
+	.evict =	zbud_zpool_evict
+};
+
+static void *zbud_zpool_create(gfp_t gfp, struct zpool_ops *zpool_ops)
+{
+	return zbud_create_pool(gfp, &zbud_zpool_ops);
+}
+
+void zbud_zpool_destroy(void *pool)
+{
+	zbud_destroy_pool(pool);
+}
+
+int zbud_zpool_malloc(void *pool, size_t size, unsigned long *handle)
+{
+	return zbud_alloc(pool, size, handle);
+}
+void zbud_zpool_free(void *pool, unsigned long handle)
+{
+	zbud_free(pool, handle);
+}
+
+int zbud_zpool_shrink(void *pool, unsigned int pages,
+			unsigned int *reclaimed)
+{
+	unsigned int total = 0;
+	int ret = -EINVAL;
+
+	while (total < pages) {
+		ret = zbud_reclaim_page(pool, 8);
+		if (ret < 0)
+			break;
+		total++;
+	}
+
+	if (reclaimed)
+		*reclaimed = total;
+
+	return ret;
+}
+
+void *zbud_zpool_map(void *pool, unsigned long handle,
+			enum zpool_mapmode mm)
+{
+	return zbud_map(pool, handle);
+}
+void zbud_zpool_unmap(void *pool, unsigned long handle)
+{
+	zbud_unmap(pool, handle);
+}
+
+u64 zbud_zpool_total_size(void *pool)
+{
+	return zbud_get_pool_size(pool) * PAGE_SIZE;
+}
+
+static struct zpool_driver zbud_zpool_driver = {
+	.type =		"zbud",
+	.create =	zbud_zpool_create,
+	.destroy =	zbud_zpool_destroy,
+	.malloc =	zbud_zpool_malloc,
+	.free =		zbud_zpool_free,
+	.shrink =	zbud_zpool_shrink,
+	.map =		zbud_zpool_map,
+	.unmap =	zbud_zpool_unmap,
+	.total_size =	zbud_zpool_total_size,
+};
+
+#endif /* CONFIG_ZPOOL */
+
+/*****************
  * Helpers
 *****************/
 /* Just to make the code easier to read */
@@ -513,11 +596,20 @@ static int __init init_zbud(void)
 	/* Make sure the zbud header will fit in one chunk */
 	BUILD_BUG_ON(sizeof(struct zbud_header) > ZHDR_SIZE_ALIGNED);
 	pr_info("loaded\n");
+
+#ifdef CONFIG_ZPOOL
+	zpool_register_driver(&zbud_zpool_driver);
+#endif
+
 	return 0;
 }
 
 static void __exit exit_zbud(void)
 {
+#ifdef CONFIG_ZPOOL
+	zpool_unregister_driver(&zbud_zpool_driver);
+#endif
+
 	pr_info("unloaded\n");
 }
 
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index fe78189..feba644 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -92,6 +92,7 @@
 #include <linux/spinlock.h>
 #include <linux/types.h>
 #include <linux/zsmalloc.h>
+#include <linux/zpool.h>
 
 /*
  * This must be power of 2 and greater than of equal to sizeof(link_free).
@@ -240,6 +241,79 @@ struct mapping_area {
 	enum zs_mapmode vm_mm; /* mapping mode */
 };
 
+/* zpool driver */
+
+#ifdef CONFIG_ZPOOL
+
+static void *zs_zpool_create(gfp_t gfp, struct zpool_ops *zpool_ops)
+{
+	return zs_create_pool(gfp);
+}
+
+void zs_zpool_destroy(void *pool)
+{
+	zs_destroy_pool(pool);
+}
+
+int zs_zpool_malloc(void *pool, size_t size, unsigned long *handle)
+{
+	*handle = zs_malloc(pool, size);
+	return *handle ? 0 : -1;
+}
+void zs_zpool_free(void *pool, unsigned long handle)
+{
+	zs_free(pool, handle);
+}
+
+int zs_zpool_shrink(void *pool, unsigned int pages,
+			unsigned int *reclaimed)
+{
+	return -EINVAL;
+}
+
+void *zs_zpool_map(void *pool, unsigned long handle,
+			enum zpool_mapmode mm)
+{
+	enum zs_mapmode zs_mm;
+
+	switch (mm) {
+	case ZPOOL_MM_RO:
+		zs_mm = ZS_MM_RO;
+		break;
+	case ZPOOL_MM_WO:
+		zs_mm = ZS_MM_WO;
+		break;
+	case ZPOOL_MM_RW: /* fallthru */
+	default:
+		zs_mm = ZS_MM_RW;
+		break;
+	}
+
+	return zs_map_object(pool, handle, zs_mm);
+}
+void zs_zpool_unmap(void *pool, unsigned long handle)
+{
+	zs_unmap_object(pool, handle);
+}
+
+u64 zs_zpool_total_size(void *pool)
+{
+	return zs_get_total_size_bytes(pool);
+}
+
+static struct zpool_driver zs_zpool_driver = {
+	.type =		"zsmalloc",
+	.create =	zs_zpool_create,
+	.destroy =	zs_zpool_destroy,
+	.malloc =	zs_zpool_malloc,
+	.free =		zs_zpool_free,
+	.shrink =	zs_zpool_shrink,
+	.map =		zs_zpool_map,
+	.unmap =	zs_zpool_unmap,
+	.total_size =	zs_zpool_total_size,
+};
+
+#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);
@@ -814,6 +888,10 @@ static void zs_exit(void)
 {
 	int cpu;
 
+#ifdef CONFIG_ZPOOL
+	zpool_unregister_driver(&zs_zpool_driver);
+#endif
+
 	cpu_notifier_register_begin();
 
 	for_each_online_cpu(cpu)
@@ -840,6 +918,10 @@ static int zs_init(void)
 
 	cpu_notifier_register_done();
 
+#ifdef CONFIG_ZPOOL
+	zpool_register_driver(&zs_zpool_driver);
+#endif
+
 	return 0;
 fail:
 	zs_exit();
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCHv4 5/6] mm/zpool: update zswap to use zpool
  2014-06-02 22:19     ` [PATCHv4 " Dan Streetman
                         ` (3 preceding siblings ...)
  2014-06-02 22:19       ` [PATCHv2 4/6] mm/zpool: zbud/zsmalloc implement zpool Dan Streetman
@ 2014-06-02 22:19       ` Dan Streetman
  2014-06-02 22:19       ` [PATCHv2 6/6] mm/zpool: prevent zbud/zsmalloc from unloading when used Dan Streetman
                         ` (4 subsequent siblings)
  9 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-06-02 22:19 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Change zswap to use the zpool api instead of directly using zbud.
Add a boot-time param to allow selecting which zpool implementation
to use, with zbud as the default.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Cc: Seth Jennings <sjennings@variantweb.net>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

Changes since v3 : https://lkml.org/lkml/2014/5/24/131
  -use new parameters in call to zpool_shrink()

Changes since v2 : https://lkml.org/lkml/2014/5/7/894
  -change zswap to select ZPOOL instead of ZBUD
  -only try zbud default if not already tried

Changes since v1 https://lkml.org/lkml/2014/4/19/102
 -since zpool fallback is removed, manually fall back to zbud if
  specified type fails

 mm/Kconfig |  2 +-
 mm/zswap.c | 76 +++++++++++++++++++++++++++++++++++++-------------------------
 2 files changed, 46 insertions(+), 32 deletions(-)

diff --git a/mm/Kconfig b/mm/Kconfig
index 00f7720..5ae0016 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -531,7 +531,7 @@ config ZSWAP
 	bool "Compressed cache for swap pages (EXPERIMENTAL)"
 	depends on FRONTSWAP && CRYPTO=y
 	select CRYPTO_LZO
-	select ZBUD
+	select ZPOOL
 	default n
 	help
 	  A lightweight compressed cache for swap pages.  It takes
diff --git a/mm/zswap.c b/mm/zswap.c
index 1cc6770..67cf9d8 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -34,7 +34,7 @@
 #include <linux/swap.h>
 #include <linux/crypto.h>
 #include <linux/mempool.h>
-#include <linux/zbud.h>
+#include <linux/zpool.h>
 
 #include <linux/mm_types.h>
 #include <linux/page-flags.h>
@@ -45,8 +45,8 @@
 /*********************************
 * statistics
 **********************************/
-/* Number of memory pages used by the compressed pool */
-static u64 zswap_pool_pages;
+/* Total bytes used by the compressed storage */
+static u64 zswap_pool_total_size;
 /* The number of compressed pages currently stored in zswap */
 static atomic_t zswap_stored_pages = ATOMIC_INIT(0);
 
@@ -89,8 +89,13 @@ static unsigned int zswap_max_pool_percent = 20;
 module_param_named(max_pool_percent,
 			zswap_max_pool_percent, uint, 0644);
 
-/* zbud_pool is shared by all of zswap backend  */
-static struct zbud_pool *zswap_pool;
+/* Compressed storage to use */
+#define ZSWAP_ZPOOL_DEFAULT "zbud"
+static char *zswap_zpool_type = ZSWAP_ZPOOL_DEFAULT;
+module_param_named(zpool, zswap_zpool_type, charp, 0444);
+
+/* zpool is shared by all of zswap backend  */
+static struct zpool *zswap_pool;
 
 /*********************************
 * compression functions
@@ -168,7 +173,7 @@ static void zswap_comp_exit(void)
  *            be held while changing the refcount.  Since the lock must
  *            be held, there is no reason to also make refcount atomic.
  * offset - the swap offset for the entry.  Index into the red-black tree.
- * handle - zbud allocation handle that stores the compressed page data
+ * handle - zpool allocation handle that stores the compressed page data
  * length - the length in bytes of the compressed page data.  Needed during
  *          decompression
  */
@@ -284,15 +289,15 @@ static void zswap_rb_erase(struct rb_root *root, struct zswap_entry *entry)
 }
 
 /*
- * Carries out the common pattern of freeing and entry's zbud allocation,
+ * Carries out the common pattern of freeing and entry's zpool allocation,
  * freeing the entry itself, and decrementing the number of stored pages.
  */
 static void zswap_free_entry(struct zswap_entry *entry)
 {
-	zbud_free(zswap_pool, entry->handle);
+	zpool_free(zswap_pool, entry->handle);
 	zswap_entry_cache_free(entry);
 	atomic_dec(&zswap_stored_pages);
-	zswap_pool_pages = zbud_get_pool_size(zswap_pool);
+	zswap_pool_total_size = zpool_get_total_size(zswap_pool);
 }
 
 /* caller must hold the tree lock */
@@ -409,7 +414,7 @@ cleanup:
 static bool zswap_is_full(void)
 {
 	return totalram_pages * zswap_max_pool_percent / 100 <
-		zswap_pool_pages;
+		DIV_ROUND_UP(zswap_pool_total_size, PAGE_SIZE);
 }
 
 /*********************************
@@ -525,7 +530,7 @@ static int zswap_get_swap_cache_page(swp_entry_t entry,
  * the swap cache, the compressed version stored by zswap can be
  * freed.
  */
-static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle)
+static int zswap_writeback_entry(struct zpool *pool, unsigned long handle)
 {
 	struct zswap_header *zhdr;
 	swp_entry_t swpentry;
@@ -541,9 +546,9 @@ static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle)
 	};
 
 	/* extract swpentry from data */
-	zhdr = zbud_map(pool, handle);
+	zhdr = zpool_map_handle(pool, handle, ZPOOL_MM_RO);
 	swpentry = zhdr->swpentry; /* here */
-	zbud_unmap(pool, handle);
+	zpool_unmap_handle(pool, handle);
 	tree = zswap_trees[swp_type(swpentry)];
 	offset = swp_offset(swpentry);
 
@@ -573,13 +578,13 @@ static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle)
 	case ZSWAP_SWAPCACHE_NEW: /* page is locked */
 		/* decompress */
 		dlen = PAGE_SIZE;
-		src = (u8 *)zbud_map(zswap_pool, entry->handle) +
-			sizeof(struct zswap_header);
+		src = (u8 *)zpool_map_handle(zswap_pool, entry->handle,
+				ZPOOL_MM_RO) + sizeof(struct zswap_header);
 		dst = kmap_atomic(page);
 		ret = zswap_comp_op(ZSWAP_COMPOP_DECOMPRESS, src,
 				entry->length, dst, &dlen);
 		kunmap_atomic(dst);
-		zbud_unmap(zswap_pool, entry->handle);
+		zpool_unmap_handle(zswap_pool, entry->handle);
 		BUG_ON(ret);
 		BUG_ON(dlen != PAGE_SIZE);
 
@@ -652,7 +657,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 	/* reclaim space if needed */
 	if (zswap_is_full()) {
 		zswap_pool_limit_hit++;
-		if (zbud_reclaim_page(zswap_pool, 8)) {
+		if (zpool_shrink(zswap_pool, 1, NULL)) {
 			zswap_reject_reclaim_fail++;
 			ret = -ENOMEM;
 			goto reject;
@@ -679,7 +684,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 
 	/* store */
 	len = dlen + sizeof(struct zswap_header);
-	ret = zbud_alloc(zswap_pool, len, &handle);
+	ret = zpool_malloc(zswap_pool, len, &handle);
 	if (ret == -ENOSPC) {
 		zswap_reject_compress_poor++;
 		goto freepage;
@@ -688,11 +693,11 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 		zswap_reject_alloc_fail++;
 		goto freepage;
 	}
-	zhdr = zbud_map(zswap_pool, handle);
+	zhdr = zpool_map_handle(zswap_pool, handle, ZPOOL_MM_RW);
 	zhdr->swpentry = swp_entry(type, offset);
 	buf = (u8 *)(zhdr + 1);
 	memcpy(buf, dst, dlen);
-	zbud_unmap(zswap_pool, handle);
+	zpool_unmap_handle(zswap_pool, handle);
 	put_cpu_var(zswap_dstmem);
 
 	/* populate entry */
@@ -715,7 +720,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 
 	/* update stats */
 	atomic_inc(&zswap_stored_pages);
-	zswap_pool_pages = zbud_get_pool_size(zswap_pool);
+	zswap_pool_total_size = zpool_get_total_size(zswap_pool);
 
 	return 0;
 
@@ -751,13 +756,13 @@ static int zswap_frontswap_load(unsigned type, pgoff_t offset,
 
 	/* decompress */
 	dlen = PAGE_SIZE;
-	src = (u8 *)zbud_map(zswap_pool, entry->handle) +
-			sizeof(struct zswap_header);
+	src = (u8 *)zpool_map_handle(zswap_pool, entry->handle,
+			ZPOOL_MM_RO) + sizeof(struct zswap_header);
 	dst = kmap_atomic(page);
 	ret = zswap_comp_op(ZSWAP_COMPOP_DECOMPRESS, src, entry->length,
 		dst, &dlen);
 	kunmap_atomic(dst);
-	zbud_unmap(zswap_pool, entry->handle);
+	zpool_unmap_handle(zswap_pool, entry->handle);
 	BUG_ON(ret);
 
 	spin_lock(&tree->lock);
@@ -810,7 +815,7 @@ static void zswap_frontswap_invalidate_area(unsigned type)
 	zswap_trees[type] = NULL;
 }
 
-static struct zbud_ops zswap_zbud_ops = {
+static struct zpool_ops zswap_zpool_ops = {
 	.evict = zswap_writeback_entry
 };
 
@@ -868,8 +873,8 @@ static int __init zswap_debugfs_init(void)
 			zswap_debugfs_root, &zswap_written_back_pages);
 	debugfs_create_u64("duplicate_entry", S_IRUGO,
 			zswap_debugfs_root, &zswap_duplicate_entry);
-	debugfs_create_u64("pool_pages", S_IRUGO,
-			zswap_debugfs_root, &zswap_pool_pages);
+	debugfs_create_u64("pool_total_size", S_IRUGO,
+			zswap_debugfs_root, &zswap_pool_total_size);
 	debugfs_create_atomic_t("stored_pages", S_IRUGO,
 			zswap_debugfs_root, &zswap_stored_pages);
 
@@ -894,17 +899,26 @@ static void __exit zswap_debugfs_exit(void) { }
 **********************************/
 static int __init init_zswap(void)
 {
+	gfp_t gfp = __GFP_NORETRY | __GFP_NOWARN;
+
 	if (!zswap_enabled)
 		return 0;
 
 	pr_info("loading zswap\n");
 
-	zswap_pool = zbud_create_pool(__GFP_NORETRY | __GFP_NOWARN,
-			&zswap_zbud_ops);
+	zswap_pool = zpool_create_pool(zswap_zpool_type, gfp, &zswap_zpool_ops);
+	if (!zswap_pool && strcmp(zswap_zpool_type, ZSWAP_ZPOOL_DEFAULT)) {
+		pr_info("%s zpool not available\n", zswap_zpool_type);
+		zswap_zpool_type = ZSWAP_ZPOOL_DEFAULT;
+		zswap_pool = zpool_create_pool(zswap_zpool_type, gfp,
+					       &zswap_zpool_ops);
+	}
 	if (!zswap_pool) {
-		pr_err("zbud pool creation failed\n");
+		pr_err("%s zpool not available\n", zswap_zpool_type);
+		pr_err("zpool creation failed\n");
 		goto error;
 	}
+	pr_info("using %s pool\n", zswap_zpool_type);
 
 	if (zswap_entry_cache_create()) {
 		pr_err("entry cache creation failed\n");
@@ -928,7 +942,7 @@ pcpufail:
 compfail:
 	zswap_entry_cache_destory();
 cachefail:
-	zbud_destroy_pool(zswap_pool);
+	zpool_destroy_pool(zswap_pool);
 error:
 	return -ENOMEM;
 }
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCHv2 6/6] mm/zpool: prevent zbud/zsmalloc from unloading when used
  2014-06-02 22:19     ` [PATCHv4 " Dan Streetman
                         ` (4 preceding siblings ...)
  2014-06-02 22:19       ` [PATCHv4 5/6] mm/zpool: update zswap to use zpool Dan Streetman
@ 2014-06-02 22:19       ` Dan Streetman
  2014-06-23 21:48         ` Andrew Morton
  2014-06-04  1:38       ` [PATCHv4 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc Bob Liu
                         ` (3 subsequent siblings)
  9 siblings, 1 reply; 65+ messages in thread
From: Dan Streetman @ 2014-06-02 22:19 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Add try_module_get() to zpool_create_pool(), and module_put() to
zpool_destroy_pool().  Without module usage counting, the driver module(s)
could be unloaded while their pool(s) were active, resulting in an oops
when zpool tried to access them.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Cc: Seth Jennings <sjennings@variantweb.net>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Nitin Gupta <ngupta@vflare.org>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

Changes since v1 : https://lkml.org/lkml/2014/5/24/134
  -add owner field to struct zpool_driver, pointing to driver module
  -move module usage counting from zbud/zsmalloc into zpool

 include/linux/zpool.h |  5 +++++
 mm/zbud.c             |  1 +
 mm/zpool.c            | 22 +++++++++++++++-------
 mm/zsmalloc.c         |  1 +
 4 files changed, 22 insertions(+), 7 deletions(-)

diff --git a/include/linux/zpool.h b/include/linux/zpool.h
index a528f7c..49bd02b 100644
--- a/include/linux/zpool.h
+++ b/include/linux/zpool.h
@@ -176,6 +176,7 @@ u64 zpool_get_total_size(struct zpool *pool);
  */
 struct zpool_driver {
 	char *type;
+	struct module *owner;
 	struct list_head list;
 
 	void *(*create)(gfp_t gfp, struct zpool_ops *ops);
@@ -203,6 +204,10 @@ void zpool_register_driver(struct zpool_driver *driver);
 /**
  * zpool_unregister_driver() - unregister a zpool implementation.
  * @driver:	driver to unregister.
+ *
+ * Module usage counting is used to prevent using a driver
+ * while/after unloading.  Please only call unregister from
+ * module exit function.
  */
 void zpool_unregister_driver(struct zpool_driver *driver);
 
diff --git a/mm/zbud.c b/mm/zbud.c
index 645379e..440bab7 100644
--- a/mm/zbud.c
+++ b/mm/zbud.c
@@ -184,6 +184,7 @@ u64 zbud_zpool_total_size(void *pool)
 
 static struct zpool_driver zbud_zpool_driver = {
 	.type =		"zbud",
+	.owner =	THIS_MODULE,
 	.create =	zbud_zpool_create,
 	.destroy =	zbud_zpool_destroy,
 	.malloc =	zbud_zpool_malloc,
diff --git a/mm/zpool.c b/mm/zpool.c
index 578c379..119f340 100644
--- a/mm/zpool.c
+++ b/mm/zpool.c
@@ -72,15 +72,24 @@ static struct zpool_driver *zpool_get_driver(char *type)
 {
 	struct zpool_driver *driver;
 
-	assert_spin_locked(&drivers_lock);
+	spin_lock(&drivers_lock);
 	list_for_each_entry(driver, &drivers_head, list) {
-		if (!strcmp(driver->type, type))
-			return driver;
+		if (!strcmp(driver->type, type)) {
+			bool got = try_module_get(driver->owner);
+			spin_unlock(&drivers_lock);
+			return got ? driver : NULL;
+		}
 	}
 
+	spin_unlock(&drivers_lock);
 	return NULL;
 }
 
+static void zpool_put_driver(struct zpool_driver *driver)
+{
+	module_put(driver->owner);
+}
+
 struct zpool *zpool_create_pool(char *type, gfp_t flags,
 			struct zpool_ops *ops)
 {
@@ -89,15 +98,11 @@ struct zpool *zpool_create_pool(char *type, gfp_t flags,
 
 	pr_info("creating pool type %s\n", type);
 
-	spin_lock(&drivers_lock);
 	driver = zpool_get_driver(type);
-	spin_unlock(&drivers_lock);
 
 	if (!driver) {
 		request_module(type);
-		spin_lock(&drivers_lock);
 		driver = zpool_get_driver(type);
-		spin_unlock(&drivers_lock);
 	}
 
 	if (!driver) {
@@ -108,6 +113,7 @@ struct zpool *zpool_create_pool(char *type, gfp_t flags,
 	zpool = kmalloc(sizeof(*zpool), GFP_KERNEL);
 	if (!zpool) {
 		pr_err("couldn't create zpool - out of memory\n");
+		zpool_put_driver(driver);
 		return NULL;
 	}
 
@@ -118,6 +124,7 @@ struct zpool *zpool_create_pool(char *type, gfp_t flags,
 
 	if (!zpool->pool) {
 		pr_err("couldn't create %s pool\n", type);
+		zpool_put_driver(driver);
 		kfree(zpool);
 		return NULL;
 	}
@@ -139,6 +146,7 @@ void zpool_destroy_pool(struct zpool *zpool)
 	list_del(&zpool->list);
 	spin_unlock(&pools_lock);
 	zpool->driver->destroy(zpool->pool);
+	zpool_put_driver(zpool->driver);
 	kfree(zpool);
 }
 
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index feba644..ae3a28f 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -303,6 +303,7 @@ u64 zs_zpool_total_size(void *pool)
 
 static struct zpool_driver zs_zpool_driver = {
 	.type =		"zsmalloc",
+	.owner =	THIS_MODULE,
 	.create =	zs_zpool_create,
 	.destroy =	zs_zpool_destroy,
 	.malloc =	zs_zpool_malloc,
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* Re: [PATCHv4 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc
  2014-06-02 22:19     ` [PATCHv4 " Dan Streetman
                         ` (5 preceding siblings ...)
  2014-06-02 22:19       ` [PATCHv2 6/6] mm/zpool: prevent zbud/zsmalloc from unloading when used Dan Streetman
@ 2014-06-04  1:38       ` Bob Liu
  2014-06-06 21:01       ` Seth Jennings
                         ` (2 subsequent siblings)
  9 siblings, 0 replies; 65+ messages in thread
From: Bob Liu @ 2014-06-04  1:38 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta,
	Andrew Morton, Hugh Dickins, Mel Gorman, Rik van Riel,
	Johannes Weiner, Sergey Senozhatsky, Linux-MM, linux-kernel



On 06/03/2014 06:19 AM, Dan Streetman wrote:
> In order to allow zswap users to choose between zbud and zsmalloc for
> the compressed storage pool, this patch set adds a new api "zpool" that
> provides an interface to both zbud and zsmalloc.  Only minor changes
> to zbud's interface were needed.  This does not include implementing
> shrinking in zsmalloc, which will be sent separately.
> 
> I believe Seth originally was using zsmalloc for swap, but there were
> concerns about how significant the impact of shrinking zsmalloc would
> be when zswap had to start reclaiming pages.  That still may be an
> issue, but this at least allows users to choose themselves whether
> they want a lower-density or higher-density compressed storage medium.
> At least for situations where zswap reclaim is never or rarely reached,
> it probably makes sense to use the higher density of zsmalloc.
> 

Nice job!
I also made a attempt last year, but didn't finish.

> Note this patch set does not change zram to use zpool, although that
> change should be possible as well.
> 

This version looks good to me!

Reviewed-by: Bob Liu <bob.liu@oracle.com>

> ---
> 
> Changes since v3 : https://lkml.org/lkml/2014/5/24/130
>   -In zpool_shrink() use # pages instead of # bytes
>   -Add reclaimed param to zpool_shrink() to indicate to caller
>    # pages actually reclaimed
>   -move module usage counting to zpool, from zbud/zsmalloc
>   -update zbud_zpool_shrink() to call zbud_reclaim_page() in a
>    loop until requested # pages have been reclaimed (or error)
> 
> Changes since v2 : https://lkml.org/lkml/2014/5/7/927
>   -Change zpool to use driver registration instead of hardcoding
>    implementations
>   -Add module use counting in zbud/zsmalloc
> 
> Changes since v1 https://lkml.org/lkml/2014/4/19/97
>  -remove zsmalloc shrinking
>  -change zbud size param type from unsigned int to size_t
>  -remove zpool fallback creation
>  -zswap manually falls back to zbud if specified type fails
> 
> 
> Dan Streetman (6):
>   mm/zbud: zbud_alloc() minor param change
>   mm/zbud: change zbud_alloc size type to size_t
>   mm/zpool: implement common zpool api to zbud/zsmalloc
>   mm/zpool: zbud/zsmalloc implement zpool
>   mm/zpool: update zswap to use zpool
>   mm/zpool: prevent zbud/zsmalloc from unloading when used
> 
>  include/linux/zbud.h  |   2 +-
>  include/linux/zpool.h | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  mm/Kconfig            |  43 ++++++----
>  mm/Makefile           |   1 +
>  mm/zbud.c             | 123 +++++++++++++++++++++++----
>  mm/zpool.c            | 206 ++++++++++++++++++++++++++++++++++++++++++++++
>  mm/zsmalloc.c         |  83 +++++++++++++++++++
>  mm/zswap.c            |  76 ++++++++++-------
>  8 files changed, 694 insertions(+), 64 deletions(-)
>  create mode 100644 include/linux/zpool.h
>  create mode 100644 mm/zpool.c
> 

-- 
Regards,
-Bob

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv4 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc
  2014-06-02 22:19     ` [PATCHv4 " Dan Streetman
                         ` (6 preceding siblings ...)
  2014-06-04  1:38       ` [PATCHv4 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc Bob Liu
@ 2014-06-06 21:01       ` Seth Jennings
  2014-07-02 21:43       ` [PATCHv5 0/4] " Dan Streetman
  2014-07-02 21:45       ` Dan Streetman
  9 siblings, 0 replies; 65+ messages in thread
From: Seth Jennings @ 2014-06-06 21:01 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Minchan Kim, Weijie Yang, Nitin Gupta, Andrew Morton, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Mon, Jun 02, 2014 at 06:19:40PM -0400, Dan Streetman wrote:
> In order to allow zswap users to choose between zbud and zsmalloc for
> the compressed storage pool, this patch set adds a new api "zpool" that
> provides an interface to both zbud and zsmalloc.  Only minor changes
> to zbud's interface were needed.  This does not include implementing
> shrinking in zsmalloc, which will be sent separately.
> 
> I believe Seth originally was using zsmalloc for swap, but there were
> concerns about how significant the impact of shrinking zsmalloc would
> be when zswap had to start reclaiming pages.  That still may be an
> issue, but this at least allows users to choose themselves whether
> they want a lower-density or higher-density compressed storage medium.
> At least for situations where zswap reclaim is never or rarely reached,
> it probably makes sense to use the higher density of zsmalloc.
> 
> Note this patch set does not change zram to use zpool, although that
> change should be possible as well.

This looks good!  Much better than when we first started :) Thanks Dan.
I haven't had a chance to test it out yet so I'm going to wait to Ack it
until then, which might be as late as 6/16 due to a vacation and a
conference.

Thanks,
Seth

> 
> ---
> 
> Changes since v3 : https://lkml.org/lkml/2014/5/24/130
>   -In zpool_shrink() use # pages instead of # bytes
>   -Add reclaimed param to zpool_shrink() to indicate to caller
>    # pages actually reclaimed
>   -move module usage counting to zpool, from zbud/zsmalloc
>   -update zbud_zpool_shrink() to call zbud_reclaim_page() in a
>    loop until requested # pages have been reclaimed (or error)
> 
> Changes since v2 : https://lkml.org/lkml/2014/5/7/927
>   -Change zpool to use driver registration instead of hardcoding
>    implementations
>   -Add module use counting in zbud/zsmalloc
> 
> Changes since v1 https://lkml.org/lkml/2014/4/19/97
>  -remove zsmalloc shrinking
>  -change zbud size param type from unsigned int to size_t
>  -remove zpool fallback creation
>  -zswap manually falls back to zbud if specified type fails
> 
> 
> Dan Streetman (6):
>   mm/zbud: zbud_alloc() minor param change
>   mm/zbud: change zbud_alloc size type to size_t
>   mm/zpool: implement common zpool api to zbud/zsmalloc
>   mm/zpool: zbud/zsmalloc implement zpool
>   mm/zpool: update zswap to use zpool
>   mm/zpool: prevent zbud/zsmalloc from unloading when used
> 
>  include/linux/zbud.h  |   2 +-
>  include/linux/zpool.h | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  mm/Kconfig            |  43 ++++++----
>  mm/Makefile           |   1 +
>  mm/zbud.c             | 123 +++++++++++++++++++++++----
>  mm/zpool.c            | 206 ++++++++++++++++++++++++++++++++++++++++++++++
>  mm/zsmalloc.c         |  83 +++++++++++++++++++
>  mm/zswap.c            |  76 ++++++++++-------
>  8 files changed, 694 insertions(+), 64 deletions(-)
>  create mode 100644 include/linux/zpool.h
>  create mode 100644 mm/zpool.c
> 
> -- 
> 1.8.3.1
> 

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv2 1/6] mm/zbud: zbud_alloc() minor param change
  2014-06-02 22:19       ` [PATCHv2 1/6] mm/zbud: zbud_alloc() minor param change Dan Streetman
@ 2014-06-23 21:19         ` Andrew Morton
  2014-06-24 15:24           ` Dan Streetman
  0 siblings, 1 reply; 65+ messages in thread
From: Andrew Morton @ 2014-06-23 21:19 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Mon,  2 Jun 2014 18:19:41 -0400 Dan Streetman <ddstreet@ieee.org> wrote:

> Change zbud to store gfp_t flags passed at pool creation to use for
> each alloc; this allows the api to be closer to the existing zsmalloc
> interface, and the only current zbud user (zswap) uses the same gfp
> flags for all allocs.  Update zswap to use changed interface.

This would appear to be a step backwards.  There's nothing wrong with
requiring all callers to pass in a gfp_t and removing this option makes
the API less usable.

IMO the patch needs much better justification, or dropping.

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv4 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc
  2014-06-02 22:19       ` [PATCHv4 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
@ 2014-06-23 21:46         ` Andrew Morton
  2014-06-24 15:39           ` Dan Streetman
  0 siblings, 1 reply; 65+ messages in thread
From: Andrew Morton @ 2014-06-23 21:46 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Mon,  2 Jun 2014 18:19:43 -0400 Dan Streetman <ddstreet@ieee.org> wrote:

> Add zpool api.
> 
> zpool provides an interface for memory storage, typically of compressed
> memory.  Users can select what backend to use; currently the only
> implementations are zbud, a low density implementation with up to
> two compressed pages per storage page, and zsmalloc, a higher density
> implementation with multiple compressed pages per storage page.
> 
> ...
>
> +/**
> + * zpool_create_pool() - Create a new zpool
> + * @type	The type of the zpool to create (e.g. zbud, zsmalloc)
> + * @flags	What GFP flags should be used when the zpool allocates memory.
> + * @ops		The optional ops callback.
> + *
> + * This creates a new zpool of the specified type.  The zpool will use the
> + * given flags when allocating any memory.  If the ops param is NULL, then
> + * the created zpool will not be shrinkable.
> + *
> + * Returns: New zpool on success, NULL on failure.
> + */
> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
> +			struct zpool_ops *ops);

It is unconventional to document the API in the .h file.  It's better
to put the documentation where people expect to find it.

It's irritating for me (for example) because this kernel convention has
permitted me to train my tags system to ignore prototypes in headers. 
But if I want to find the zpool_create_pool documentation I will need
to jump through hoops.

> 
> ...
>
> +int zpool_evict(void *pool, unsigned long handle)
> +{
> +	struct zpool *zpool;
> +
> +	spin_lock(&pools_lock);
> +	list_for_each_entry(zpool, &pools_head, list) {
> +		if (zpool->pool == pool) {
> +			spin_unlock(&pools_lock);

This is racy against zpool_unregister_driver().

> +			if (!zpool->ops || !zpool->ops->evict)
> +				return -EINVAL;
> +			return zpool->ops->evict(zpool, handle);
> +		}
> +	}
> +	spin_unlock(&pools_lock);
> +
> +	return -ENOENT;
> +}
> +EXPORT_SYMBOL(zpool_evict);
> +
> +static struct zpool_driver *zpool_get_driver(char *type)

In kernel convention, "get" implies "take a reference upon".  A better
name would be zpool_find_driver or zpool_lookup_driver.

This is especially important because the code appears to need a
for-real zpool_get_driver to fix the races!

> 
> ...
>
> +
> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
> +			struct zpool_ops *ops)
> +{
> +	struct zpool_driver *driver;
> +	struct zpool *zpool;
> +
> +	pr_info("creating pool type %s\n", type);
> +
> +	spin_lock(&drivers_lock);
> +	driver = zpool_get_driver(type);
> +	spin_unlock(&drivers_lock);

Racy against unregister.  Can be solved with a standard get/put
refcounting implementation.  Or perhaps a big fat mutex.

> +	if (!driver) {
> +		request_module(type);
> +		spin_lock(&drivers_lock);
> +		driver = zpool_get_driver(type);
> +		spin_unlock(&drivers_lock);
> +	}
> +
> +	if (!driver) {
> +		pr_err("no driver for type %s\n", type);
> +		return NULL;
> +	}
> +
> +	zpool = kmalloc(sizeof(*zpool), GFP_KERNEL);
> +	if (!zpool) {
> +		pr_err("couldn't create zpool - out of memory\n");
> +		return NULL;
> +	}
> +
> +	zpool->type = driver->type;
> +	zpool->driver = driver;
> +	zpool->pool = driver->create(flags, ops);
> +	zpool->ops = ops;
> +
> +	if (!zpool->pool) {
> +		pr_err("couldn't create %s pool\n", type);
> +		kfree(zpool);
> +		return NULL;
> +	}
> +
> +	pr_info("created %s pool\n", type);
> +
> +	spin_lock(&pools_lock);
> +	list_add(&zpool->list, &pools_head);
> +	spin_unlock(&pools_lock);
> +
> +	return zpool;
> +}
> 
> ...
>
> +void zpool_destroy_pool(struct zpool *zpool)
> +{
> +	pr_info("destroying pool type %s\n", zpool->type);
> +
> +	spin_lock(&pools_lock);
> +	list_del(&zpool->list);
> +	spin_unlock(&pools_lock);
> +	zpool->driver->destroy(zpool->pool);
> +	kfree(zpool);
> +}

What are the lifecycle rules here?  How do we know that nobody else can
be concurrently using this pool?

> 
> ...
>


^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv2 6/6] mm/zpool: prevent zbud/zsmalloc from unloading when used
  2014-06-02 22:19       ` [PATCHv2 6/6] mm/zpool: prevent zbud/zsmalloc from unloading when used Dan Streetman
@ 2014-06-23 21:48         ` Andrew Morton
  2014-06-24 15:41           ` Dan Streetman
  0 siblings, 1 reply; 65+ messages in thread
From: Andrew Morton @ 2014-06-23 21:48 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Mon,  2 Jun 2014 18:19:46 -0400 Dan Streetman <ddstreet@ieee.org> wrote:

> Add try_module_get() to zpool_create_pool(), and module_put() to
> zpool_destroy_pool().  Without module usage counting, the driver module(s)
> could be unloaded while their pool(s) were active, resulting in an oops
> when zpool tried to access them.

Was wondering about that ;)  We may as well fold
this fix into "mm/zpool: implement common zpool api to zbud/zsmalloc"?

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv2 1/6] mm/zbud: zbud_alloc() minor param change
  2014-06-23 21:19         ` Andrew Morton
@ 2014-06-24 15:24           ` Dan Streetman
  0 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-06-24 15:24 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Mon, Jun 23, 2014 at 5:19 PM, Andrew Morton
<akpm@linux-foundation.org> wrote:
> On Mon,  2 Jun 2014 18:19:41 -0400 Dan Streetman <ddstreet@ieee.org> wrote:
>
>> Change zbud to store gfp_t flags passed at pool creation to use for
>> each alloc; this allows the api to be closer to the existing zsmalloc
>> interface, and the only current zbud user (zswap) uses the same gfp
>> flags for all allocs.  Update zswap to use changed interface.
>
> This would appear to be a step backwards.  There's nothing wrong with
> requiring all callers to pass in a gfp_t and removing this option makes
> the API less usable.
>
> IMO the patch needs much better justification, or dropping.

Well, since zpool can be backed by either zsmalloc or zbud, those 2
apis have to be consistent, and currently zbud does use a per-malloc
gfp_t param while zsmalloc doesn't.  Does it make more sense to add a
gfp_t param to zsmalloc's alloc function?


I wonder though if allowing the caller to pass a gfp_t for each alloc
really does make sense, though.  Any memory alloc'ed isn't actually
controllable by the caller, and in fact it's currently impossible for
the caller to free memory alloc'ed by the backing pool - the caller
can invalidate specific handles, but that doesn't guarantee the memory
alloc'ed for that handle will then be freed - it could remain in use
with some other handle(s).  Additionally, there's no guarantee that
when the user creates a new handle, and new memory will be allocated -
a previous available handle could be used.

So I guess what I'm suggesting is that because 1) there is no
guarantee that a call to zpool_malloc() will actually call kmalloc()
with the provided gfp_t; previously kmalloc'ed memory with a different
gfp_t could be (and probably in many cases will be) used, and 2) the
caller has no way to free any memory kmalloc'ed with specific gfp_t
(so for example, using GFP_ATOMIC would be a bad idea, since the
caller couldn't then free that memory directly), it makes more sense
to me to keep all allocations in the pool using the same gfp_t flags.
If there was a need to be able to create pool handles using different
gfp_t flags, then it would be probably more effective to create
multiple pools, each one with the different desired gfp_t flags to
use.

However, from the implementation side, changing zsmalloc is trivial to
just add a gfp_t param to alloc, and update zpool_malloc to accept and
pass through the gfp_t param.  So if that still makes more sense to
you, I can update things to change the zsmalloc api to add the param,
instead of this patch which removes the param from its api.  Assuming
that Minchan and Nitin also have no problem with updating the zsmalloc
api - there should be no functional difference in the zram/zsmalloc
relationship, since zram would simply always pass the same gfp_t to
zsmalloc.

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv4 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc
  2014-06-23 21:46         ` Andrew Morton
@ 2014-06-24 15:39           ` Dan Streetman
  2014-06-24 23:08             ` Andrew Morton
  0 siblings, 1 reply; 65+ messages in thread
From: Dan Streetman @ 2014-06-24 15:39 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Mon, Jun 23, 2014 at 5:46 PM, Andrew Morton
<akpm@linux-foundation.org> wrote:
> On Mon,  2 Jun 2014 18:19:43 -0400 Dan Streetman <ddstreet@ieee.org> wrote:
>
>> Add zpool api.
>>
>> zpool provides an interface for memory storage, typically of compressed
>> memory.  Users can select what backend to use; currently the only
>> implementations are zbud, a low density implementation with up to
>> two compressed pages per storage page, and zsmalloc, a higher density
>> implementation with multiple compressed pages per storage page.
>>
>> ...
>>
>> +/**
>> + * zpool_create_pool() - Create a new zpool
>> + * @type     The type of the zpool to create (e.g. zbud, zsmalloc)
>> + * @flags    What GFP flags should be used when the zpool allocates memory.
>> + * @ops              The optional ops callback.
>> + *
>> + * This creates a new zpool of the specified type.  The zpool will use the
>> + * given flags when allocating any memory.  If the ops param is NULL, then
>> + * the created zpool will not be shrinkable.
>> + *
>> + * Returns: New zpool on success, NULL on failure.
>> + */
>> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
>> +                     struct zpool_ops *ops);
>
> It is unconventional to document the API in the .h file.  It's better
> to put the documentation where people expect to find it.
>
> It's irritating for me (for example) because this kernel convention has
> permitted me to train my tags system to ignore prototypes in headers.
> But if I want to find the zpool_create_pool documentation I will need
> to jump through hoops.

Got it, I will move it to the .c file.

I noticed you pulled these into -mm, do you want me to send follow-on
patches for these changes, or actually update the origin patches and
resend the patch set?


>
>>
>> ...
>>
>> +int zpool_evict(void *pool, unsigned long handle)
>> +{
>> +     struct zpool *zpool;
>> +
>> +     spin_lock(&pools_lock);
>> +     list_for_each_entry(zpool, &pools_head, list) {
>> +             if (zpool->pool == pool) {
>> +                     spin_unlock(&pools_lock);
>
> This is racy against zpool_unregister_driver().
>
>> +                     if (!zpool->ops || !zpool->ops->evict)
>> +                             return -EINVAL;
>> +                     return zpool->ops->evict(zpool, handle);
>> +             }
>> +     }
>> +     spin_unlock(&pools_lock);
>> +
>> +     return -ENOENT;
>> +}
>> +EXPORT_SYMBOL(zpool_evict);
>> +
>> +static struct zpool_driver *zpool_get_driver(char *type)
>
> In kernel convention, "get" implies "take a reference upon".  A better
> name would be zpool_find_driver or zpool_lookup_driver.
>
> This is especially important because the code appears to need a
> for-real zpool_get_driver to fix the races!

yep as you mentioned in your next email, I will roll the
try_module_get() protection into this patch.

>
>>
>> ...
>>
>> +
>> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
>> +                     struct zpool_ops *ops)
>> +{
>> +     struct zpool_driver *driver;
>> +     struct zpool *zpool;
>> +
>> +     pr_info("creating pool type %s\n", type);
>> +
>> +     spin_lock(&drivers_lock);
>> +     driver = zpool_get_driver(type);
>> +     spin_unlock(&drivers_lock);
>
> Racy against unregister.  Can be solved with a standard get/put
> refcounting implementation.  Or perhaps a big fat mutex.
>
>> +     if (!driver) {
>> +             request_module(type);
>> +             spin_lock(&drivers_lock);
>> +             driver = zpool_get_driver(type);
>> +             spin_unlock(&drivers_lock);
>> +     }
>> +
>> +     if (!driver) {
>> +             pr_err("no driver for type %s\n", type);
>> +             return NULL;
>> +     }
>> +
>> +     zpool = kmalloc(sizeof(*zpool), GFP_KERNEL);
>> +     if (!zpool) {
>> +             pr_err("couldn't create zpool - out of memory\n");
>> +             return NULL;
>> +     }
>> +
>> +     zpool->type = driver->type;
>> +     zpool->driver = driver;
>> +     zpool->pool = driver->create(flags, ops);
>> +     zpool->ops = ops;
>> +
>> +     if (!zpool->pool) {
>> +             pr_err("couldn't create %s pool\n", type);
>> +             kfree(zpool);
>> +             return NULL;
>> +     }
>> +
>> +     pr_info("created %s pool\n", type);
>> +
>> +     spin_lock(&pools_lock);
>> +     list_add(&zpool->list, &pools_head);
>> +     spin_unlock(&pools_lock);
>> +
>> +     return zpool;
>> +}
>>
>> ...
>>
>> +void zpool_destroy_pool(struct zpool *zpool)
>> +{
>> +     pr_info("destroying pool type %s\n", zpool->type);
>> +
>> +     spin_lock(&pools_lock);
>> +     list_del(&zpool->list);
>> +     spin_unlock(&pools_lock);
>> +     zpool->driver->destroy(zpool->pool);
>> +     kfree(zpool);
>> +}
>
> What are the lifecycle rules here?  How do we know that nobody else can
> be concurrently using this pool?

Well I think with zpools, as well as direct use of zsmalloc and zbud
pools, whoever creates a pool is responsible for making sure it's no
longer in use before destroying it.  I think in most use cases, pool
creators won't be sharing their pools, so there should be no issue
with concurrent use.  In fact, concurrent pool use it probably a bad
idea in general - zsmalloc for example relies on per-cpu data during
handle mapping, so concurrent use of a single pool might result in the
per-cpu data being overwritten if multiple users of a single pool
tried to map and use different handles from the same cpu.

Should some use/sharing restrictions be added to the zpool documentation?

>
>>
>> ...
>>
>

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv2 6/6] mm/zpool: prevent zbud/zsmalloc from unloading when used
  2014-06-23 21:48         ` Andrew Morton
@ 2014-06-24 15:41           ` Dan Streetman
  0 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-06-24 15:41 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Mon, Jun 23, 2014 at 5:48 PM, Andrew Morton
<akpm@linux-foundation.org> wrote:
> On Mon,  2 Jun 2014 18:19:46 -0400 Dan Streetman <ddstreet@ieee.org> wrote:
>
>> Add try_module_get() to zpool_create_pool(), and module_put() to
>> zpool_destroy_pool().  Without module usage counting, the driver module(s)
>> could be unloaded while their pool(s) were active, resulting in an oops
>> when zpool tried to access them.
>
> Was wondering about that ;)  We may as well fold
> this fix into "mm/zpool: implement common zpool api to zbud/zsmalloc"?

Yes.  Sorry, I had this pulled out of that because I was trying to
keep the patches logically separated.  But they do need to be
together, to be safe ;-)

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv4 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc
  2014-06-24 15:39           ` Dan Streetman
@ 2014-06-24 23:08             ` Andrew Morton
  2014-06-27 17:11               ` Dan Streetman
  0 siblings, 1 reply; 65+ messages in thread
From: Andrew Morton @ 2014-06-24 23:08 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Tue, 24 Jun 2014 11:39:12 -0400 Dan Streetman <ddstreet@ieee.org> wrote:

> On Mon, Jun 23, 2014 at 5:46 PM, Andrew Morton
> <akpm@linux-foundation.org> wrote:
> > On Mon,  2 Jun 2014 18:19:43 -0400 Dan Streetman <ddstreet@ieee.org> wrote:
> >
> >> Add zpool api.
> >>
> >> zpool provides an interface for memory storage, typically of compressed
> >> memory.  Users can select what backend to use; currently the only
> >> implementations are zbud, a low density implementation with up to
> >> two compressed pages per storage page, and zsmalloc, a higher density
> >> implementation with multiple compressed pages per storage page.
> >>
> >> ...
> >>
> >> +/**
> >> + * zpool_create_pool() - Create a new zpool
> >> + * @type     The type of the zpool to create (e.g. zbud, zsmalloc)
> >> + * @flags    What GFP flags should be used when the zpool allocates memory.
> >> + * @ops              The optional ops callback.
> >> + *
> >> + * This creates a new zpool of the specified type.  The zpool will use the
> >> + * given flags when allocating any memory.  If the ops param is NULL, then
> >> + * the created zpool will not be shrinkable.
> >> + *
> >> + * Returns: New zpool on success, NULL on failure.
> >> + */
> >> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
> >> +                     struct zpool_ops *ops);
> >
> > It is unconventional to document the API in the .h file.  It's better
> > to put the documentation where people expect to find it.
> >
> > It's irritating for me (for example) because this kernel convention has
> > permitted me to train my tags system to ignore prototypes in headers.
> > But if I want to find the zpool_create_pool documentation I will need
> > to jump through hoops.
> 
> Got it, I will move it to the .c file.
> 
> I noticed you pulled these into -mm, do you want me to send follow-on
> patches for these changes, or actually update the origin patches and
> resend the patch set?

Full resend, I guess.  I often add things which are
not-quite-fully-baked to give them a bit of testing, check for
integration with other changes, etc.

> >
> >>
> >> ...
> >>
> >> +
> >> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
> >> +                     struct zpool_ops *ops)
> >> +{
> >> +     struct zpool_driver *driver;
> >> +     struct zpool *zpool;
> >> +
> >> +     pr_info("creating pool type %s\n", type);
> >> +
> >> +     spin_lock(&drivers_lock);
> >> +     driver = zpool_get_driver(type);
> >> +     spin_unlock(&drivers_lock);
> >
> > Racy against unregister.  Can be solved with a standard get/put
> > refcounting implementation.  Or perhaps a big fat mutex.

Was there a decision here?

> >> +void zpool_destroy_pool(struct zpool *zpool)
> >> +{
> >> +     pr_info("destroying pool type %s\n", zpool->type);
> >> +
> >> +     spin_lock(&pools_lock);
> >> +     list_del(&zpool->list);
> >> +     spin_unlock(&pools_lock);
> >> +     zpool->driver->destroy(zpool->pool);
> >> +     kfree(zpool);
> >> +}
> >
> > What are the lifecycle rules here?  How do we know that nobody else can
> > be concurrently using this pool?
> 
> Well I think with zpools, as well as direct use of zsmalloc and zbud
> pools, whoever creates a pool is responsible for making sure it's no
> longer in use before destroying it.

Sounds reasonable.  Perhaps there's some convenient WARN_ON we can put
in here to check that.

>  I think in most use cases, pool
> creators won't be sharing their pools, so there should be no issue
> with concurrent use.  In fact, concurrent pool use it probably a bad
> idea in general - zsmalloc for example relies on per-cpu data during
> handle mapping, so concurrent use of a single pool might result in the
> per-cpu data being overwritten if multiple users of a single pool
> tried to map and use different handles from the same cpu.

That's all a bit waffly.  Either we support concurrent use or we don't!

> Should some use/sharing restrictions be added to the zpool documentation?

Sure.  And the code if possible.  If a second user tries to use a pool
which is already in use, that attempt should just fail, with WARN,
printk, return -EBUSY, whatever.

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv4 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc
  2014-06-24 23:08             ` Andrew Morton
@ 2014-06-27 17:11               ` Dan Streetman
  2014-06-27 19:17                 ` Andrew Morton
  0 siblings, 1 reply; 65+ messages in thread
From: Dan Streetman @ 2014-06-27 17:11 UTC (permalink / raw)
  To: Andrew Morton
  Cc: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Tue, Jun 24, 2014 at 7:08 PM, Andrew Morton
<akpm@linux-foundation.org> wrote:
> On Tue, 24 Jun 2014 11:39:12 -0400 Dan Streetman <ddstreet@ieee.org> wrote:
>
>> On Mon, Jun 23, 2014 at 5:46 PM, Andrew Morton
>> <akpm@linux-foundation.org> wrote:
>> > On Mon,  2 Jun 2014 18:19:43 -0400 Dan Streetman <ddstreet@ieee.org> wrote:
>> >
>> >> Add zpool api.
>> >>
>> >> zpool provides an interface for memory storage, typically of compressed
>> >> memory.  Users can select what backend to use; currently the only
>> >> implementations are zbud, a low density implementation with up to
>> >> two compressed pages per storage page, and zsmalloc, a higher density
>> >> implementation with multiple compressed pages per storage page.
>> >>
>> >> ...
>> >>
>> >> +/**
>> >> + * zpool_create_pool() - Create a new zpool
>> >> + * @type     The type of the zpool to create (e.g. zbud, zsmalloc)
>> >> + * @flags    What GFP flags should be used when the zpool allocates memory.
>> >> + * @ops              The optional ops callback.
>> >> + *
>> >> + * This creates a new zpool of the specified type.  The zpool will use the
>> >> + * given flags when allocating any memory.  If the ops param is NULL, then
>> >> + * the created zpool will not be shrinkable.
>> >> + *
>> >> + * Returns: New zpool on success, NULL on failure.
>> >> + */
>> >> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
>> >> +                     struct zpool_ops *ops);
>> >
>> > It is unconventional to document the API in the .h file.  It's better
>> > to put the documentation where people expect to find it.
>> >
>> > It's irritating for me (for example) because this kernel convention has
>> > permitted me to train my tags system to ignore prototypes in headers.
>> > But if I want to find the zpool_create_pool documentation I will need
>> > to jump through hoops.
>>
>> Got it, I will move it to the .c file.
>>
>> I noticed you pulled these into -mm, do you want me to send follow-on
>> patches for these changes, or actually update the origin patches and
>> resend the patch set?
>
> Full resend, I guess.  I often add things which are
> not-quite-fully-baked to give them a bit of testing, check for
> integration with other changes, etc.
>
>> >
>> >>
>> >> ...
>> >>
>> >> +
>> >> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
>> >> +                     struct zpool_ops *ops)
>> >> +{
>> >> +     struct zpool_driver *driver;
>> >> +     struct zpool *zpool;
>> >> +
>> >> +     pr_info("creating pool type %s\n", type);
>> >> +
>> >> +     spin_lock(&drivers_lock);
>> >> +     driver = zpool_get_driver(type);
>> >> +     spin_unlock(&drivers_lock);
>> >
>> > Racy against unregister.  Can be solved with a standard get/put
>> > refcounting implementation.  Or perhaps a big fat mutex.
>
> Was there a decision here?

What I tried to do, with the final patch in the set, was use module
usage counting combined with function documentation - in
zpool_create_pool() the zpool_get_driver() does try_module_get()
before releasing the spinlock, so if the driver *only* calls
unregister from its module exit function, I think we should be good -
once zpool_create_pool() gets the driver module, the driver won't
enter its exit function and thus won't unregister; and if the driver
module has started its exit function, try_module_get() will return
failure and zpool_create_pool() will return failure.

Now, if we remove the restriction that the driver module can only
unregister from its module exit function, then we would need an
additional refcount (we could use module_refcount() but the module may
have refcounts unrelated to us) and unregister would need a return
value, to indicate failure.  I think the problem I had with that is,
in the driver module's exit function it can't abort if unregister
fails; but with the module refcounting, unregister shouldn't ever fail
in the driver's exit function...

So should I remove the unregister function doc asking to only call
unregister from the module exit function, and add a separate refcount
to the driver get/put functions?  I don't think we need to use a kref,
since we don't want to free the driver once kref == 0, we want to be
able to check in the unregister function if there are any refs, so
just an atomic_t should work.  And we would still need to keep the
module get/put, too, so it would be something like:

  spin_lock(&drivers_lock);
...
  bool got = try_module_get(driver->owner);
  if (got)
    atomic_inc(driver->refs);
  spin_unlock(&drivers_lock);
  return got ? driver : NULL;

with the appropriate atomic_dec in zpool_put_driver(), and unregister
would change to:

int zpool_unregister_driver(struct zpool_driver *driver)
{
  spin_lock(&drivers_lock);
  if (atomic_read(driver->refs) > 0) {
    spin_unlock(&drivers_lock);
    return -EBUSY;
  }
  list_del(&driver->list);
  spin_unlock(&drivers_lock);
  return 0;
}


>
>> >> +void zpool_destroy_pool(struct zpool *zpool)
>> >> +{
>> >> +     pr_info("destroying pool type %s\n", zpool->type);
>> >> +
>> >> +     spin_lock(&pools_lock);
>> >> +     list_del(&zpool->list);
>> >> +     spin_unlock(&pools_lock);
>> >> +     zpool->driver->destroy(zpool->pool);
>> >> +     kfree(zpool);
>> >> +}
>> >
>> > What are the lifecycle rules here?  How do we know that nobody else can
>> > be concurrently using this pool?
>>
>> Well I think with zpools, as well as direct use of zsmalloc and zbud
>> pools, whoever creates a pool is responsible for making sure it's no
>> longer in use before destroying it.
>
> Sounds reasonable.  Perhaps there's some convenient WARN_ON we can put
> in here to check that.

Since zpool's just a passthrough, there's no simple way of it telling
if a pool is in use or not, but warnings could be added to
zbud/zsmalloc's destroy functions.  zs_destroy_pool() already does
check and pr_info() if any non-empty pools are destroyed.

>
>>  I think in most use cases, pool
>> creators won't be sharing their pools, so there should be no issue
>> with concurrent use.  In fact, concurrent pool use it probably a bad
>> idea in general - zsmalloc for example relies on per-cpu data during
>> handle mapping, so concurrent use of a single pool might result in the
>> per-cpu data being overwritten if multiple users of a single pool
>> tried to map and use different handles from the same cpu.
>
> That's all a bit waffly.  Either we support concurrent use or we don't!

I think I got offtrack talking about pool creators and pool users.
zpool, and zbud/zsmalloc, really don't care about *who* is calling
each of their functions.  Only concurrency matters, and most of the
functions are safe for concurrent use, protected internally by
spinlocks, etc in each pool driver (zbud/zsmalloc).  The map/unmap
functions are a notable exception, but the function doc for
zpool_map_handle() clarifies the restrictions for how to call it and
what the implementation may do (hold spinlocks, disable preempt/ints)
and that the caller should call unmap quickly after using the mapped
handle.  And whoever creates the pool will need to also destroy the
pool, or at least handle coordinating who and when the pool is
destroyed (beyond warning, i don't think there is much the pool driver
can do when a non-empty pool is destroyed.  Maybe don't destroy the
pool, but that risks leaking memory if nobody ever uses the pool
again).

I'll review zbud and zsmalloc again to make sure each function is
threadsafe, and state that in each zpool function doc, or make sure to
clarify any restrictions.

Since you already mentioned a few changes, let me get an updated patch
set sent, I'll try to send that by Monday, and we can go from there if
more changes are needed.  Thanks for the review!


>
>> Should some use/sharing restrictions be added to the zpool documentation?
>
> Sure.  And the code if possible.  If a second user tries to use a pool
> which is already in use, that attempt should just fail, with WARN,
> printk, return -EBUSY, whatever.

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv4 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc
  2014-06-27 17:11               ` Dan Streetman
@ 2014-06-27 19:17                 ` Andrew Morton
  0 siblings, 0 replies; 65+ messages in thread
From: Andrew Morton @ 2014-06-27 19:17 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta, Bob Liu,
	Hugh Dickins, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Fri, 27 Jun 2014 13:11:15 -0400 Dan Streetman <ddstreet@ieee.org> wrote:

> >> >> +struct zpool *zpool_create_pool(char *type, gfp_t flags,
> >> >> +                     struct zpool_ops *ops)
> >> >> +{
> >> >> +     struct zpool_driver *driver;
> >> >> +     struct zpool *zpool;
> >> >> +
> >> >> +     pr_info("creating pool type %s\n", type);
> >> >> +
> >> >> +     spin_lock(&drivers_lock);
> >> >> +     driver = zpool_get_driver(type);
> >> >> +     spin_unlock(&drivers_lock);
> >> >
> >> > Racy against unregister.  Can be solved with a standard get/put
> >> > refcounting implementation.  Or perhaps a big fat mutex.
> >
> > Was there a decision here?
> 
> What I tried to do, with the final patch in the set, was use module
> usage counting combined with function documentation - in
> zpool_create_pool() the zpool_get_driver() does try_module_get()
> before releasing the spinlock, so if the driver *only* calls
> unregister from its module exit function, I think we should be good -
> once zpool_create_pool() gets the driver module, the driver won't
> enter its exit function and thus won't unregister; and if the driver
> module has started its exit function, try_module_get() will return
> failure and zpool_create_pool() will return failure.
> 
> Now, if we remove the restriction that the driver module can only
> unregister from its module exit function, then we would need an
> additional refcount (we could use module_refcount() but the module may
> have refcounts unrelated to us) and unregister would need a return
> value, to indicate failure.  I think the problem I had with that is,
> in the driver module's exit function it can't abort if unregister
> fails; but with the module refcounting, unregister shouldn't ever fail
> in the driver's exit function...
> 
> So should I remove the unregister function doc asking to only call
> unregister from the module exit function, and add a separate refcount
> to the driver get/put functions?  I don't think we need to use a kref,
> since we don't want to free the driver once kref == 0, we want to be
> able to check in the unregister function if there are any refs, so
> just an atomic_t should work.  And we would still need to keep the
> module get/put, too, so it would be something like:

I'm not sure I understood all that.  But I don't want to understand it
in this context!  Readers should be able to gather all this from
looking at the code.

>   spin_lock(&drivers_lock);
> ...
>   bool got = try_module_get(driver->owner);
>   if (got)
>     atomic_inc(driver->refs);
>   spin_unlock(&drivers_lock);
>   return got ? driver : NULL;
> 
> with the appropriate atomic_dec in zpool_put_driver(), and unregister
> would change to:
> 
> int zpool_unregister_driver(struct zpool_driver *driver)
> {
>   spin_lock(&drivers_lock);
>   if (atomic_read(driver->refs) > 0) {
>     spin_unlock(&drivers_lock);
>     return -EBUSY;
>   }
>   list_del(&driver->list);
>   spin_unlock(&drivers_lock);
>   return 0;
> }

It sounds like that will work.

^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv5 0/4] mm/zpool: add common api for zswap to use zbud/zsmalloc
  2014-06-02 22:19     ` [PATCHv4 " Dan Streetman
                         ` (7 preceding siblings ...)
  2014-06-06 21:01       ` Seth Jennings
@ 2014-07-02 21:43       ` Dan Streetman
  2014-07-02 21:45       ` Dan Streetman
  9 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-07-02 21:43 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

In order to allow zswap users to choose between zbud and zsmalloc for
the compressed storage pool, this patch set adds a new api "zpool" that
provides an interface to both zbud and zsmalloc.  This does not include
implementing shrinking in zsmalloc, which will be sent separately.

I believe Seth originally was using zsmalloc for swap, but there were
concerns about how significant the impact of shrinking zsmalloc would
be when zswap had to start reclaiming pages.  That still may be an
issue, but this at least allows users to choose themselves whether
they want a lower-density or higher-density compressed storage medium.
At least for situations where zswap reclaim is never or rarely reached,
it probably makes sense to use the higher density of zsmalloc.

Note this patch set does not change zram to use zpool, although that
change should be possible as well.

---
Changes since v4 : https://lkml.org/lkml/2014/6/2/711
  -omit first patch, that removed gfp_t param from zpool_malloc()
  -move function doc from zpool.h to zpool.c
  -move module usage refcounting into patch that adds zpool
  -add extra refcounting to prevent driver unregister if in use
  -add doc clarifying concurrency usage
  -make zbud/zsmalloc zpool functions static
  -typo corrections

Changes since v3 : https://lkml.org/lkml/2014/5/24/130
  -In zpool_shrink() use # pages instead of # bytes
  -Add reclaimed param to zpool_shrink() to indicate to caller
   # pages actually reclaimed
  -move module usage counting to zpool, from zbud/zsmalloc
  -update zbud_zpool_shrink() to call zbud_reclaim_page() in a
   loop until requested # pages have been reclaimed (or error)

Changes since v2 : https://lkml.org/lkml/2014/5/7/927
  -Change zpool to use driver registration instead of hardcoding
   implementations
  -Add module use counting in zbud/zsmalloc

Changes since v1 https://lkml.org/lkml/2014/4/19/97
 -remove zsmalloc shrinking
 -change zbud size param type from unsigned int to size_t
 -remove zpool fallback creation
 -zswap manually falls back to zbud if specified type fails


Dan Streetman (4):
  mm/zbud: change zbud_alloc size type to size_t
  mm/zpool: implement common zpool api to zbud/zsmalloc
  mm/zpool: zbud/zsmalloc implement zpool
  mm/zpool: update zswap to use zpool

 include/linux/zbud.h  |   2 +-
 include/linux/zpool.h | 106 +++++++++++++++
 mm/Kconfig            |  43 +++---
 mm/Makefile           |   1 +
 mm/zbud.c             |  98 +++++++++++++-
 mm/zpool.c            | 364 ++++++++++++++++++++++++++++++++++++++++++++++++++
 mm/zsmalloc.c         |  84 ++++++++++++
 mm/zswap.c            |  75 ++++++-----
 8 files changed, 722 insertions(+), 51 deletions(-)
 create mode 100644 include/linux/zpool.h
 create mode 100644 mm/zpool.c

-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv5 0/4] mm/zpool: add common api for zswap to use zbud/zsmalloc
  2014-06-02 22:19     ` [PATCHv4 " Dan Streetman
                         ` (8 preceding siblings ...)
  2014-07-02 21:43       ` [PATCHv5 0/4] " Dan Streetman
@ 2014-07-02 21:45       ` Dan Streetman
  2014-07-02 21:45         ` [PATCHv2 1/4] mm/zbud: change zbud_alloc size type to size_t Dan Streetman
                           ` (4 more replies)
  9 siblings, 5 replies; 65+ messages in thread
From: Dan Streetman @ 2014-07-02 21:45 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

In order to allow zswap users to choose between zbud and zsmalloc for
the compressed storage pool, this patch set adds a new api "zpool" that
provides an interface to both zbud and zsmalloc.  This does not include
implementing shrinking in zsmalloc, which will be sent separately.

I believe Seth originally was using zsmalloc for swap, but there were
concerns about how significant the impact of shrinking zsmalloc would
be when zswap had to start reclaiming pages.  That still may be an
issue, but this at least allows users to choose themselves whether
they want a lower-density or higher-density compressed storage medium.
At least for situations where zswap reclaim is never or rarely reached,
it probably makes sense to use the higher density of zsmalloc.

Note this patch set does not change zram to use zpool, although that
change should be possible as well.

---
Changes since v4 : https://lkml.org/lkml/2014/6/2/711
  -omit first patch, that removed gfp_t param from zpool_malloc()
  -move function doc from zpool.h to zpool.c
  -move module usage refcounting into patch that adds zpool
  -add extra refcounting to prevent driver unregister if in use
  -add doc clarifying concurrency usage
  -make zbud/zsmalloc zpool functions static
  -typo corrections

Changes since v3 : https://lkml.org/lkml/2014/5/24/130
  -In zpool_shrink() use # pages instead of # bytes
  -Add reclaimed param to zpool_shrink() to indicate to caller
   # pages actually reclaimed
  -move module usage counting to zpool, from zbud/zsmalloc
  -update zbud_zpool_shrink() to call zbud_reclaim_page() in a
   loop until requested # pages have been reclaimed (or error)

Changes since v2 : https://lkml.org/lkml/2014/5/7/927
  -Change zpool to use driver registration instead of hardcoding
   implementations
  -Add module use counting in zbud/zsmalloc

Changes since v1 https://lkml.org/lkml/2014/4/19/97
 -remove zsmalloc shrinking
 -change zbud size param type from unsigned int to size_t
 -remove zpool fallback creation
 -zswap manually falls back to zbud if specified type fails


Dan Streetman (4):
  mm/zbud: change zbud_alloc size type to size_t
  mm/zpool: implement common zpool api to zbud/zsmalloc
  mm/zpool: zbud/zsmalloc implement zpool
  mm/zpool: update zswap to use zpool

 include/linux/zbud.h  |   2 +-
 include/linux/zpool.h | 106 +++++++++++++++
 mm/Kconfig            |  43 +++---
 mm/Makefile           |   1 +
 mm/zbud.c             |  98 +++++++++++++-
 mm/zpool.c            | 364 ++++++++++++++++++++++++++++++++++++++++++++++++++
 mm/zsmalloc.c         |  84 ++++++++++++
 mm/zswap.c            |  75 ++++++-----
 8 files changed, 722 insertions(+), 51 deletions(-)
 create mode 100644 include/linux/zpool.h
 create mode 100644 mm/zpool.c

-- 
1.8.3.1


^ permalink raw reply	[flat|nested] 65+ messages in thread

* [PATCHv2 1/4] mm/zbud: change zbud_alloc size type to size_t
  2014-07-02 21:45       ` Dan Streetman
@ 2014-07-02 21:45         ` Dan Streetman
  2014-07-02 21:45         ` [PATCHv5 2/4] mm/zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
                           ` (3 subsequent siblings)
  4 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-07-02 21:45 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Change the type of the zbud_alloc() size param from unsigned int
to size_t.

Technically, this should not make any difference, as the zbud
implementation already restricts the size to well within either
type's limits; but as zsmalloc (and kmalloc) use size_t, and
zpool will use size_t, this brings the size parameter type
in line with zsmalloc/zpool.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Acked-by: Seth Jennings <sjennings@variantweb.net>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

Changes since v1 : https://lkml.org/lkml/2014/5/7/757
  -context change due to omitting patch to remove gfp_t param

 include/linux/zbud.h | 2 +-
 mm/zbud.c            | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/include/linux/zbud.h b/include/linux/zbud.h
index 13af0d4..f9d41a6 100644
--- a/include/linux/zbud.h
+++ b/include/linux/zbud.h
@@ -11,7 +11,7 @@ struct zbud_ops {
 
 struct zbud_pool *zbud_create_pool(gfp_t gfp, struct zbud_ops *ops);
 void zbud_destroy_pool(struct zbud_pool *pool);
-int zbud_alloc(struct zbud_pool *pool, unsigned int size, gfp_t gfp,
+int zbud_alloc(struct zbud_pool *pool, size_t size, gfp_t gfp,
 	unsigned long *handle);
 void zbud_free(struct zbud_pool *pool, unsigned long handle);
 int zbud_reclaim_page(struct zbud_pool *pool, unsigned int retries);
diff --git a/mm/zbud.c b/mm/zbud.c
index 01df13a..d012261 100644
--- a/mm/zbud.c
+++ b/mm/zbud.c
@@ -122,7 +122,7 @@ enum buddy {
 };
 
 /* Converts an allocation size in bytes to size in zbud chunks */
-static int size_to_chunks(int size)
+static int size_to_chunks(size_t size)
 {
 	return (size + CHUNK_SIZE - 1) >> CHUNK_SHIFT;
 }
@@ -247,7 +247,7 @@ void zbud_destroy_pool(struct zbud_pool *pool)
  * gfp arguments are invalid or -ENOMEM if the pool was unable to allocate
  * a new page.
  */
-int zbud_alloc(struct zbud_pool *pool, unsigned int size, gfp_t gfp,
+int zbud_alloc(struct zbud_pool *pool, size_t size, gfp_t gfp,
 			unsigned long *handle)
 {
 	int chunks, i, freechunks;
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCHv5 2/4] mm/zpool: implement common zpool api to zbud/zsmalloc
  2014-07-02 21:45       ` Dan Streetman
  2014-07-02 21:45         ` [PATCHv2 1/4] mm/zbud: change zbud_alloc size type to size_t Dan Streetman
@ 2014-07-02 21:45         ` Dan Streetman
  2014-07-02 21:45         ` [PATCHv3 3/4] mm/zpool: zbud/zsmalloc implement zpool Dan Streetman
                           ` (2 subsequent siblings)
  4 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-07-02 21:45 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Add zpool api.

zpool provides an interface for memory storage, typically of compressed
memory.  Users can select what backend to use; currently the only
implementations are zbud, a low density implementation with up to
two compressed pages per storage page, and zsmalloc, a higher density
implementation with multiple compressed pages per storage page.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Cc: Seth Jennings <sjennings@variantweb.net>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Nitin Gupta <ngupta@vflare.org>
Cc: Weijie Yang <weijie.yang@samsung.com>
Cc: Andrew Morton <akpm@linux-foundation.org>
---

Changes since v4 : https://lkml.org/lkml/2014/6/2/707
  -move function doc from zpool.h to zpool.c
  -add doc clarifying concurrency use
  -move module usage refcounting into this patch from later patch
  -add atomic_t refcounting
  -change driver unregister function to return failure if driver in use
  -update to pass gfp to create and malloc functions

Changes since v3 : https://lkml.org/lkml/2014/5/24/135
  -change zpool_shrink() to use # pages instead of # bytes
  -change zpool_shrink() to update *reclaimed param with
   number of pages actually reclaimed

Changes since v2 : https://lkml.org/lkml/2014/5/7/733
  -Remove hardcoded zbud/zsmalloc implementations
  -Add driver (un)register functions

Changes since v1 https://lkml.org/lkml/2014/4/19/101
 -add some pr_info() during creation and pr_err() on errors
 -remove zpool code to call zs_shrink(), since zsmalloc shrinking
  was removed from this patchset
 -remove fallback; only specified pool type will be tried
 -pr_fmt() is defined in zpool to prefix zpool: in any pr_XXX() calls


 include/linux/zpool.h | 106 +++++++++++++++
 mm/Kconfig            |  41 +++---
 mm/Makefile           |   1 +
 mm/zpool.c            | 364 ++++++++++++++++++++++++++++++++++++++++++++++++++
 mm/zsmalloc.c         |   1 -
 5 files changed, 495 insertions(+), 18 deletions(-)
 create mode 100644 include/linux/zpool.h
 create mode 100644 mm/zpool.c

diff --git a/include/linux/zpool.h b/include/linux/zpool.h
new file mode 100644
index 0000000..f14bd75
--- /dev/null
+++ b/include/linux/zpool.h
@@ -0,0 +1,106 @@
+/*
+ * zpool memory storage api
+ *
+ * Copyright (C) 2014 Dan Streetman
+ *
+ * This is a common frontend for the zbud and zsmalloc memory
+ * storage pool implementations.  Typically, this is used to
+ * store compressed memory.
+ */
+
+#ifndef _ZPOOL_H_
+#define _ZPOOL_H_
+
+struct zpool;
+
+struct zpool_ops {
+	int (*evict)(struct zpool *pool, unsigned long handle);
+};
+
+/*
+ * Control how a handle is mapped.  It will be ignored if the
+ * implementation does not support it.  Its use is optional.
+ * Note that this does not refer to memory protection, it
+ * refers to how the memory will be copied in/out if copying
+ * is necessary during mapping; read-write is the safest as
+ * it copies the existing memory in on map, and copies the
+ * changed memory back out on unmap.  Write-only does not copy
+ * in the memory and should only be used for initialization.
+ * If in doubt, use ZPOOL_MM_DEFAULT which is read-write.
+ */
+enum zpool_mapmode {
+	ZPOOL_MM_RW, /* normal read-write mapping */
+	ZPOOL_MM_RO, /* read-only (no copy-out at unmap time) */
+	ZPOOL_MM_WO, /* write-only (no copy-in at map time) */
+
+	ZPOOL_MM_DEFAULT = ZPOOL_MM_RW
+};
+
+struct zpool *zpool_create_pool(char *type, gfp_t gfp, struct zpool_ops *ops);
+
+char *zpool_get_type(struct zpool *pool);
+
+void zpool_destroy_pool(struct zpool *pool);
+
+int zpool_malloc(struct zpool *pool, size_t size, gfp_t gfp,
+			unsigned long *handle);
+
+void zpool_free(struct zpool *pool, unsigned long handle);
+
+int zpool_shrink(struct zpool *pool, unsigned int pages,
+			unsigned int *reclaimed);
+
+void *zpool_map_handle(struct zpool *pool, unsigned long handle,
+			enum zpool_mapmode mm);
+
+void zpool_unmap_handle(struct zpool *pool, unsigned long handle);
+
+u64 zpool_get_total_size(struct zpool *pool);
+
+
+/**
+ * struct zpool_driver - driver implementation for zpool
+ * @type:	name of the driver.
+ * @list:	entry in the list of zpool drivers.
+ * @create:	create a new pool.
+ * @destroy:	destroy a pool.
+ * @malloc:	allocate mem from a pool.
+ * @free:	free mem from a pool.
+ * @shrink:	shrink the pool.
+ * @map:	map a handle.
+ * @unmap:	unmap a handle.
+ * @total_size:	get total size of a pool.
+ *
+ * This is created by a zpool implementation and registered
+ * with zpool.
+ */
+struct zpool_driver {
+	char *type;
+	struct module *owner;
+	atomic_t refcount;
+	struct list_head list;
+
+	void *(*create)(gfp_t gfp, struct zpool_ops *ops);
+	void (*destroy)(void *pool);
+
+	int (*malloc)(void *pool, size_t size, gfp_t gfp,
+				unsigned long *handle);
+	void (*free)(void *pool, unsigned long handle);
+
+	int (*shrink)(void *pool, unsigned int pages,
+				unsigned int *reclaimed);
+
+	void *(*map)(void *pool, unsigned long handle,
+				enum zpool_mapmode mm);
+	void (*unmap)(void *pool, unsigned long handle);
+
+	u64 (*total_size)(void *pool);
+};
+
+void zpool_register_driver(struct zpool_driver *driver);
+
+int zpool_unregister_driver(struct zpool_driver *driver);
+
+int zpool_evict(void *pool, unsigned long handle);
+
+#endif
diff --git a/mm/Kconfig b/mm/Kconfig
index 3e9977a..865f91c 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -508,15 +508,17 @@ config CMA_DEBUG
 	  processing calls such as dma_alloc_from_contiguous().
 	  This option does not affect warning and error messages.
 
-config ZBUD
-	tristate
-	default n
+config MEM_SOFT_DIRTY
+	bool "Track memory changes"
+	depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
+	select PROC_PAGE_MONITOR
 	help
-	  A special purpose allocator for storing compressed pages.
-	  It is designed to store up to two compressed pages per physical
-	  page.  While this design limits storage density, it has simple and
-	  deterministic reclaim properties that make it preferable to a higher
-	  density approach when reclaim will be used.
+	  This option enables memory changes tracking by introducing a
+	  soft-dirty bit on pte-s. This bit it set when someone writes
+	  into a page just as regular dirty bit, but unlike the latter
+	  it can be cleared by hands.
+
+	  See Documentation/vm/soft-dirty.txt for more details.
 
 config ZSWAP
 	bool "Compressed cache for swap pages (EXPERIMENTAL)"
@@ -538,17 +540,22 @@ config ZSWAP
 	  they have not be fully explored on the large set of potential
 	  configurations and workloads that exist.
 
-config MEM_SOFT_DIRTY
-	bool "Track memory changes"
-	depends on CHECKPOINT_RESTORE && HAVE_ARCH_SOFT_DIRTY && PROC_FS
-	select PROC_PAGE_MONITOR
+config ZPOOL
+	tristate "Common API for compressed memory storage"
+	default n
 	help
-	  This option enables memory changes tracking by introducing a
-	  soft-dirty bit on pte-s. This bit it set when someone writes
-	  into a page just as regular dirty bit, but unlike the latter
-	  it can be cleared by hands.
+	  Compressed memory storage API.  This allows using either zbud or
+	  zsmalloc.
 
-	  See Documentation/vm/soft-dirty.txt for more details.
+config ZBUD
+	tristate "Low density storage for compressed pages"
+	default n
+	help
+	  A special purpose allocator for storing compressed pages.
+	  It is designed to store up to two compressed pages per physical
+	  page.  While this design limits storage density, it has simple and
+	  deterministic reclaim properties that make it preferable to a higher
+	  density approach when reclaim will be used.
 
 config ZSMALLOC
 	tristate "Memory allocator for compressed pages"
diff --git a/mm/Makefile b/mm/Makefile
index 4064f3e..2f6eeb6 100644
--- a/mm/Makefile
+++ b/mm/Makefile
@@ -59,6 +59,7 @@ obj-$(CONFIG_DEBUG_KMEMLEAK) += kmemleak.o
 obj-$(CONFIG_DEBUG_KMEMLEAK_TEST) += kmemleak-test.o
 obj-$(CONFIG_CLEANCACHE) += cleancache.o
 obj-$(CONFIG_MEMORY_ISOLATION) += page_isolation.o
+obj-$(CONFIG_ZPOOL)	+= zpool.o
 obj-$(CONFIG_ZBUD)	+= zbud.o
 obj-$(CONFIG_ZSMALLOC)	+= zsmalloc.o
 obj-$(CONFIG_GENERIC_EARLY_IOREMAP) += early_ioremap.o
diff --git a/mm/zpool.c b/mm/zpool.c
new file mode 100644
index 0000000..e40612a
--- /dev/null
+++ b/mm/zpool.c
@@ -0,0 +1,364 @@
+/*
+ * zpool memory storage api
+ *
+ * Copyright (C) 2014 Dan Streetman
+ *
+ * This is a common frontend for memory storage pool implementations.
+ * Typically, this is used to store compressed memory.
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/zpool.h>
+
+struct zpool {
+	char *type;
+
+	struct zpool_driver *driver;
+	void *pool;
+	struct zpool_ops *ops;
+
+	struct list_head list;
+};
+
+static LIST_HEAD(drivers_head);
+static DEFINE_SPINLOCK(drivers_lock);
+
+static LIST_HEAD(pools_head);
+static DEFINE_SPINLOCK(pools_lock);
+
+/**
+ * zpool_register_driver() - register a zpool implementation.
+ * @driver:	driver to register
+ */
+void zpool_register_driver(struct zpool_driver *driver)
+{
+	spin_lock(&drivers_lock);
+	atomic_set(&driver->refcount, 0);
+	list_add(&driver->list, &drivers_head);
+	spin_unlock(&drivers_lock);
+}
+EXPORT_SYMBOL(zpool_register_driver);
+
+/**
+ * zpool_unregister_driver() - unregister a zpool implementation.
+ * @driver:	driver to unregister.
+ *
+ * Module usage counting is used to prevent using a driver
+ * while/after unloading, so if this is called from module
+ * exit function, this should never fail; if called from
+ * other than the module exit function, and this returns
+ * failure, the driver is in use and must remain available.
+ */
+int zpool_unregister_driver(struct zpool_driver *driver)
+{
+	int ret = 0, refcount;
+
+	spin_lock(&drivers_lock);
+	refcount = atomic_read(&driver->refcount);
+	WARN_ON(refcount < 0);
+	if (refcount > 0)
+		ret = -EBUSY;
+	else
+		list_del(&driver->list);
+	spin_unlock(&drivers_lock);
+
+	return ret;
+}
+EXPORT_SYMBOL(zpool_unregister_driver);
+
+/**
+ * zpool_evict() - evict callback from a zpool implementation.
+ * @pool:	pool to evict from.
+ * @handle:	handle to evict.
+ *
+ * This can be used by zpool implementations to call the
+ * user's evict zpool_ops struct evict callback.
+ */
+int zpool_evict(void *pool, unsigned long handle)
+{
+	struct zpool *zpool;
+
+	spin_lock(&pools_lock);
+	list_for_each_entry(zpool, &pools_head, list) {
+		if (zpool->pool == pool) {
+			spin_unlock(&pools_lock);
+			if (!zpool->ops || !zpool->ops->evict)
+				return -EINVAL;
+			return zpool->ops->evict(zpool, handle);
+		}
+	}
+	spin_unlock(&pools_lock);
+
+	return -ENOENT;
+}
+EXPORT_SYMBOL(zpool_evict);
+
+static struct zpool_driver *zpool_get_driver(char *type)
+{
+	struct zpool_driver *driver;
+
+	spin_lock(&drivers_lock);
+	list_for_each_entry(driver, &drivers_head, list) {
+		if (!strcmp(driver->type, type)) {
+			bool got = try_module_get(driver->owner);
+
+			if (got)
+				atomic_inc(&driver->refcount);
+			spin_unlock(&drivers_lock);
+			return got ? driver : NULL;
+		}
+	}
+
+	spin_unlock(&drivers_lock);
+	return NULL;
+}
+
+static void zpool_put_driver(struct zpool_driver *driver)
+{
+	atomic_dec(&driver->refcount);
+	module_put(driver->owner);
+}
+
+/**
+ * zpool_create_pool() - Create a new zpool
+ * @type	The type of the zpool to create (e.g. zbud, zsmalloc)
+ * @gfp		The GFP flags to use when allocating the pool.
+ * @ops		The optional ops callback.
+ *
+ * This creates a new zpool of the specified type.  The gfp flags will be
+ * used when allocating memory, if the implementation supports it.  If the
+ * ops param is NULL, then the created zpool will not be shrinkable.
+ *
+ * Implementations must guarantee this to be thread-safe.
+ *
+ * Returns: New zpool on success, NULL on failure.
+ */
+struct zpool *zpool_create_pool(char *type, gfp_t gfp, struct zpool_ops *ops)
+{
+	struct zpool_driver *driver;
+	struct zpool *zpool;
+
+	pr_info("creating pool type %s\n", type);
+
+	driver = zpool_get_driver(type);
+
+	if (!driver) {
+		request_module(type);
+		driver = zpool_get_driver(type);
+	}
+
+	if (!driver) {
+		pr_err("no driver for type %s\n", type);
+		return NULL;
+	}
+
+	zpool = kmalloc(sizeof(*zpool), gfp);
+	if (!zpool) {
+		pr_err("couldn't create zpool - out of memory\n");
+		zpool_put_driver(driver);
+		return NULL;
+	}
+
+	zpool->type = driver->type;
+	zpool->driver = driver;
+	zpool->pool = driver->create(gfp, ops);
+	zpool->ops = ops;
+
+	if (!zpool->pool) {
+		pr_err("couldn't create %s pool\n", type);
+		zpool_put_driver(driver);
+		kfree(zpool);
+		return NULL;
+	}
+
+	pr_info("created %s pool\n", type);
+
+	spin_lock(&pools_lock);
+	list_add(&zpool->list, &pools_head);
+	spin_unlock(&pools_lock);
+
+	return zpool;
+}
+
+/**
+ * zpool_destroy_pool() - Destroy a zpool
+ * @pool	The zpool to destroy.
+ *
+ * Implementations must guarantee this to be thread-safe,
+ * however only when destroying different pools.  The same
+ * pool should only be destroyed once, and should not be used
+ * after it is destroyed.
+ *
+ * This destroys an existing zpool.  The zpool should not be in use.
+ */
+void zpool_destroy_pool(struct zpool *zpool)
+{
+	pr_info("destroying pool type %s\n", zpool->type);
+
+	spin_lock(&pools_lock);
+	list_del(&zpool->list);
+	spin_unlock(&pools_lock);
+	zpool->driver->destroy(zpool->pool);
+	zpool_put_driver(zpool->driver);
+	kfree(zpool);
+}
+
+/**
+ * zpool_get_type() - Get the type of the zpool
+ * @pool	The zpool to check
+ *
+ * This returns the type of the pool.
+ *
+ * Implementations must guarantee this to be thread-safe.
+ *
+ * Returns: The type of zpool.
+ */
+char *zpool_get_type(struct zpool *zpool)
+{
+	return zpool->type;
+}
+
+/**
+ * zpool_malloc() - Allocate memory
+ * @pool	The zpool to allocate from.
+ * @size	The amount of memory to allocate.
+ * @gfp		The GFP flags to use when allocating memory.
+ * @handle	Pointer to the handle to set
+ *
+ * This allocates the requested amount of memory from the pool.
+ * The gfp flags will be used when allocating memory, if the
+ * implementation supports it.  The provided @handle will be
+ * set to the allocated object handle.
+ *
+ * Implementations must guarantee this to be thread-safe.
+ *
+ * Returns: 0 on success, negative value on error.
+ */
+int zpool_malloc(struct zpool *zpool, size_t size, gfp_t gfp,
+			unsigned long *handle)
+{
+	return zpool->driver->malloc(zpool->pool, size, gfp, handle);
+}
+
+/**
+ * zpool_free() - Free previously allocated memory
+ * @pool	The zpool that allocated the memory.
+ * @handle	The handle to the memory to free.
+ *
+ * This frees previously allocated memory.  This does not guarantee
+ * that the pool will actually free memory, only that the memory
+ * in the pool will become available for use by the pool.
+ *
+ * Implementations must guarantee this to be thread-safe,
+ * however only when freeing different handles.  The same
+ * handle should only be freed once, and should not be used
+ * after freeing.
+ */
+void zpool_free(struct zpool *zpool, unsigned long handle)
+{
+	zpool->driver->free(zpool->pool, handle);
+}
+
+/**
+ * zpool_shrink() - Shrink the pool size
+ * @pool	The zpool to shrink.
+ * @pages	The number of pages to shrink the pool.
+ * @reclaimed	The number of pages successfully evicted.
+ *
+ * This attempts to shrink the actual memory size of the pool
+ * by evicting currently used handle(s).  If the pool was
+ * created with no zpool_ops, or the evict call fails for any
+ * of the handles, this will fail.  If non-NULL, the @reclaimed
+ * parameter will be set to the number of pages reclaimed,
+ * which may be more than the number of pages requested.
+ *
+ * Implementations must guarantee this to be thread-safe.
+ *
+ * Returns: 0 on success, negative value on error/failure.
+ */
+int zpool_shrink(struct zpool *zpool, unsigned int pages,
+			unsigned int *reclaimed)
+{
+	return zpool->driver->shrink(zpool->pool, pages, reclaimed);
+}
+
+/**
+ * zpool_map_handle() - Map a previously allocated handle into memory
+ * @pool	The zpool that the handle was allocated from
+ * @handle	The handle to map
+ * @mm		How the memory should be mapped
+ *
+ * This maps a previously allocated handle into memory.  The @mm
+ * param indicates to the implementation how the memory will be
+ * used, i.e. read-only, write-only, read-write.  If the
+ * implementation does not support it, the memory will be treated
+ * as read-write.
+ *
+ * This may hold locks, disable interrupts, and/or preemption,
+ * and the zpool_unmap_handle() must be called to undo those
+ * actions.  The code that uses the mapped handle should complete
+ * its operatons on the mapped handle memory quickly and unmap
+ * as soon as possible.  As the implementation may use per-cpu
+ * data, multiple handles should not be mapped concurrently on
+ * any cpu.
+ *
+ * Returns: A pointer to the handle's mapped memory area.
+ */
+void *zpool_map_handle(struct zpool *zpool, unsigned long handle,
+			enum zpool_mapmode mapmode)
+{
+	return zpool->driver->map(zpool->pool, handle, mapmode);
+}
+
+/**
+ * zpool_unmap_handle() - Unmap a previously mapped handle
+ * @pool	The zpool that the handle was allocated from
+ * @handle	The handle to unmap
+ *
+ * This unmaps a previously mapped handle.  Any locks or other
+ * actions that the implementation took in zpool_map_handle()
+ * will be undone here.  The memory area returned from
+ * zpool_map_handle() should no longer be used after this.
+ */
+void zpool_unmap_handle(struct zpool *zpool, unsigned long handle)
+{
+	zpool->driver->unmap(zpool->pool, handle);
+}
+
+/**
+ * zpool_get_total_size() - The total size of the pool
+ * @pool	The zpool to check
+ *
+ * This returns the total size in bytes of the pool.
+ *
+ * Returns: Total size of the zpool in bytes.
+ */
+u64 zpool_get_total_size(struct zpool *zpool)
+{
+	return zpool->driver->total_size(zpool->pool);
+}
+
+static int __init init_zpool(void)
+{
+	pr_info("loaded\n");
+	return 0;
+}
+
+static void __exit exit_zpool(void)
+{
+	pr_info("unloaded\n");
+}
+
+module_init(init_zpool);
+module_exit(exit_zpool);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Dan Streetman <ddstreet@ieee.org>");
+MODULE_DESCRIPTION("Common API for compressed memory storage");
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index fe78189..4cd5479 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -240,7 +240,6 @@ struct mapping_area {
 	enum zs_mapmode vm_mm; /* mapping mode */
 };
 
-
 /* per-cpu VM mapping areas for zspage accesses that cross page boundaries */
 static DEFINE_PER_CPU(struct mapping_area, zs_map_area);
 
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCHv3 3/4] mm/zpool: zbud/zsmalloc implement zpool
  2014-07-02 21:45       ` Dan Streetman
  2014-07-02 21:45         ` [PATCHv2 1/4] mm/zbud: change zbud_alloc size type to size_t Dan Streetman
  2014-07-02 21:45         ` [PATCHv5 2/4] mm/zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
@ 2014-07-02 21:45         ` Dan Streetman
  2014-07-02 21:45         ` [PATCHv5 4/4] mm/zpool: update zswap to use zpool Dan Streetman
  2014-07-14 18:10         ` [PATCHv5 0/4] mm/zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
  4 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-07-02 21:45 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Update zbud and zsmalloc to implement the zpool api.

[Fengguang Wu <fengguang.wu@intel.com>: make functions static]
Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Cc: Seth Jennings <sjennings@variantweb.net>
Cc: Minchan Kim <minchan@kernel.org>
Cc: Nitin Gupta <ngupta@vflare.org>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

Note to Seth: We talked about removing the retries parameter from
zbud_reclaim_page(), but I did not include that in this patch.
I'll send a separate patch for that.

Changes since v2 : https://lkml.org/lkml/2014/6/2/801
  -make functions static per suggestion from Fengguang Wu
  -move module owner initialization from later patch
  -update to use gfp params for create and malloc

Changes since v1 : https://lkml.org/lkml/2014/5/24/136
  -Update zbud_zpool_shrink() to call zbud_reclaim_page()
   in a loop until number of pages requested has been
   reclaimed, or error
  -Update zbud_zpool_shrink() to update passed *reclaimed
   param with # pages actually reclaimed
  -Update zs_pool_shrink() with new param, although function
   is not implemented yet

 mm/zbud.c     | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 mm/zsmalloc.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 179 insertions(+)

diff --git a/mm/zbud.c b/mm/zbud.c
index d012261..a05790b 100644
--- a/mm/zbud.c
+++ b/mm/zbud.c
@@ -51,6 +51,7 @@
 #include <linux/slab.h>
 #include <linux/spinlock.h>
 #include <linux/zbud.h>
+#include <linux/zpool.h>
 
 /*****************
  * Structures
@@ -113,6 +114,90 @@ struct zbud_header {
 };
 
 /*****************
+ * zpool
+ ****************/
+
+#ifdef CONFIG_ZPOOL
+
+static int zbud_zpool_evict(struct zbud_pool *pool, unsigned long handle)
+{
+	return zpool_evict(pool, handle);
+}
+
+static struct zbud_ops zbud_zpool_ops = {
+	.evict =	zbud_zpool_evict
+};
+
+static void *zbud_zpool_create(gfp_t gfp, struct zpool_ops *zpool_ops)
+{
+	return zbud_create_pool(gfp, &zbud_zpool_ops);
+}
+
+static void zbud_zpool_destroy(void *pool)
+{
+	zbud_destroy_pool(pool);
+}
+
+static int zbud_zpool_malloc(void *pool, size_t size, gfp_t gfp,
+			unsigned long *handle)
+{
+	return zbud_alloc(pool, size, gfp, handle);
+}
+static void zbud_zpool_free(void *pool, unsigned long handle)
+{
+	zbud_free(pool, handle);
+}
+
+static int zbud_zpool_shrink(void *pool, unsigned int pages,
+			unsigned int *reclaimed)
+{
+	unsigned int total = 0;
+	int ret = -EINVAL;
+
+	while (total < pages) {
+		ret = zbud_reclaim_page(pool, 8);
+		if (ret < 0)
+			break;
+		total++;
+	}
+
+	if (reclaimed)
+		*reclaimed = total;
+
+	return ret;
+}
+
+static void *zbud_zpool_map(void *pool, unsigned long handle,
+			enum zpool_mapmode mm)
+{
+	return zbud_map(pool, handle);
+}
+static void zbud_zpool_unmap(void *pool, unsigned long handle)
+{
+	zbud_unmap(pool, handle);
+}
+
+static u64 zbud_zpool_total_size(void *pool)
+{
+	return zbud_get_pool_size(pool) * PAGE_SIZE;
+}
+
+static struct zpool_driver zbud_zpool_driver = {
+	.type =		"zbud",
+	.owner =	THIS_MODULE,
+	.create =	zbud_zpool_create,
+	.destroy =	zbud_zpool_destroy,
+	.malloc =	zbud_zpool_malloc,
+	.free =		zbud_zpool_free,
+	.shrink =	zbud_zpool_shrink,
+	.map =		zbud_zpool_map,
+	.unmap =	zbud_zpool_unmap,
+	.total_size =	zbud_zpool_total_size,
+};
+
+#endif /* CONFIG_ZPOOL */
+
+/*****************
  * Helpers
 *****************/
 /* Just to make the code easier to read */
@@ -511,11 +596,20 @@ static int __init init_zbud(void)
 	/* Make sure the zbud header will fit in one chunk */
 	BUILD_BUG_ON(sizeof(struct zbud_header) > ZHDR_SIZE_ALIGNED);
 	pr_info("loaded\n");
+
+#ifdef CONFIG_ZPOOL
+	zpool_register_driver(&zbud_zpool_driver);
+#endif
+
 	return 0;
 }
 
 static void __exit exit_zbud(void)
 {
+#ifdef CONFIG_ZPOOL
+	zpool_unregister_driver(&zbud_zpool_driver);
+#endif
+
 	pr_info("unloaded\n");
 }
 
diff --git a/mm/zsmalloc.c b/mm/zsmalloc.c
index 4cd5479..6c1e2a4 100644
--- a/mm/zsmalloc.c
+++ b/mm/zsmalloc.c
@@ -92,6 +92,7 @@
 #include <linux/spinlock.h>
 #include <linux/types.h>
 #include <linux/zsmalloc.h>
+#include <linux/zpool.h>
 
 /*
  * This must be power of 2 and greater than of equal to sizeof(link_free).
@@ -240,6 +241,82 @@ struct mapping_area {
 	enum zs_mapmode vm_mm; /* mapping mode */
 };
 
+/* zpool driver */
+
+#ifdef CONFIG_ZPOOL
+
+static void *zs_zpool_create(gfp_t gfp, struct zpool_ops *zpool_ops)
+{
+	return zs_create_pool(gfp);
+}
+
+static void zs_zpool_destroy(void *pool)
+{
+	zs_destroy_pool(pool);
+}
+
+static int zs_zpool_malloc(void *pool, size_t size, gfp_t gfp,
+			unsigned long *handle)
+{
+	*handle = zs_malloc(pool, size);
+	return *handle ? 0 : -1;
+}
+static void zs_zpool_free(void *pool, unsigned long handle)
+{
+	zs_free(pool, handle);
+}
+
+static int zs_zpool_shrink(void *pool, unsigned int pages,
+			unsigned int *reclaimed)
+{
+	return -EINVAL;
+}
+
+static void *zs_zpool_map(void *pool, unsigned long handle,
+			enum zpool_mapmode mm)
+{
+	enum zs_mapmode zs_mm;
+
+	switch (mm) {
+	case ZPOOL_MM_RO:
+		zs_mm = ZS_MM_RO;
+		break;
+	case ZPOOL_MM_WO:
+		zs_mm = ZS_MM_WO;
+		break;
+	case ZPOOL_MM_RW: /* fallthru */
+	default:
+		zs_mm = ZS_MM_RW;
+		break;
+	}
+
+	return zs_map_object(pool, handle, zs_mm);
+}
+static void zs_zpool_unmap(void *pool, unsigned long handle)
+{
+	zs_unmap_object(pool, handle);
+}
+
+static u64 zs_zpool_total_size(void *pool)
+{
+	return zs_get_total_size_bytes(pool);
+}
+
+static struct zpool_driver zs_zpool_driver = {
+	.type =		"zsmalloc",
+	.owner =	THIS_MODULE,
+	.create =	zs_zpool_create,
+	.destroy =	zs_zpool_destroy,
+	.malloc =	zs_zpool_malloc,
+	.free =		zs_zpool_free,
+	.shrink =	zs_zpool_shrink,
+	.map =		zs_zpool_map,
+	.unmap =	zs_zpool_unmap,
+	.total_size =	zs_zpool_total_size,
+};
+
+#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);
 
@@ -813,6 +890,10 @@ static void zs_exit(void)
 {
 	int cpu;
 
+#ifdef CONFIG_ZPOOL
+	zpool_unregister_driver(&zs_zpool_driver);
+#endif
+
 	cpu_notifier_register_begin();
 
 	for_each_online_cpu(cpu)
@@ -839,6 +920,10 @@ static int zs_init(void)
 
 	cpu_notifier_register_done();
 
+#ifdef CONFIG_ZPOOL
+	zpool_register_driver(&zs_zpool_driver);
+#endif
+
 	return 0;
 fail:
 	zs_exit();
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* [PATCHv5 4/4] mm/zpool: update zswap to use zpool
  2014-07-02 21:45       ` Dan Streetman
                           ` (2 preceding siblings ...)
  2014-07-02 21:45         ` [PATCHv3 3/4] mm/zpool: zbud/zsmalloc implement zpool Dan Streetman
@ 2014-07-02 21:45         ` Dan Streetman
  2014-07-14 18:10         ` [PATCHv5 0/4] mm/zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
  4 siblings, 0 replies; 65+ messages in thread
From: Dan Streetman @ 2014-07-02 21:45 UTC (permalink / raw)
  To: Seth Jennings, Minchan Kim, Weijie Yang, Nitin Gupta
  Cc: Dan Streetman, Andrew Morton, Bob Liu, Hugh Dickins, Mel Gorman,
	Rik van Riel, Johannes Weiner, Sergey Senozhatsky, Linux-MM,
	linux-kernel

Change zswap to use the zpool api instead of directly using zbud.
Add a boot-time param to allow selecting which zpool implementation
to use, with zbud as the default.

Signed-off-by: Dan Streetman <ddstreet@ieee.org>
Cc: Seth Jennings <sjennings@variantweb.net>
Cc: Weijie Yang <weijie.yang@samsung.com>
---

Changes since v4 : https://lkml.org/lkml/2014/6/2/709
  -update to use pass gfp params to create and malloc

Changes since v3 : https://lkml.org/lkml/2014/5/24/131
  -use new parameters in call to zpool_shrink()

Changes since v2 : https://lkml.org/lkml/2014/5/7/894
  -change zswap to select ZPOOL instead of ZBUD
  -only try zbud default if not already tried

Changes since v1 https://lkml.org/lkml/2014/4/19/102
 -since zpool fallback is removed, manually fall back to zbud if
  specified type fails


 mm/Kconfig |  2 +-
 mm/zswap.c | 75 +++++++++++++++++++++++++++++++++++++-------------------------
 2 files changed, 46 insertions(+), 31 deletions(-)

diff --git a/mm/Kconfig b/mm/Kconfig
index 865f91c..7fddb52 100644
--- a/mm/Kconfig
+++ b/mm/Kconfig
@@ -524,7 +524,7 @@ config ZSWAP
 	bool "Compressed cache for swap pages (EXPERIMENTAL)"
 	depends on FRONTSWAP && CRYPTO=y
 	select CRYPTO_LZO
-	select ZBUD
+	select ZPOOL
 	default n
 	help
 	  A lightweight compressed cache for swap pages.  It takes
diff --git a/mm/zswap.c b/mm/zswap.c
index 008388fe..032c21e 100644
--- a/mm/zswap.c
+++ b/mm/zswap.c
@@ -34,7 +34,7 @@
 #include <linux/swap.h>
 #include <linux/crypto.h>
 #include <linux/mempool.h>
-#include <linux/zbud.h>
+#include <linux/zpool.h>
 
 #include <linux/mm_types.h>
 #include <linux/page-flags.h>
@@ -45,8 +45,8 @@
 /*********************************
 * statistics
 **********************************/
-/* Number of memory pages used by the compressed pool */
-static u64 zswap_pool_pages;
+/* Total bytes used by the compressed storage */
+static u64 zswap_pool_total_size;
 /* The number of compressed pages currently stored in zswap */
 static atomic_t zswap_stored_pages = ATOMIC_INIT(0);
 
@@ -89,8 +89,13 @@ static unsigned int zswap_max_pool_percent = 20;
 module_param_named(max_pool_percent,
 			zswap_max_pool_percent, uint, 0644);
 
-/* zbud_pool is shared by all of zswap backend  */
-static struct zbud_pool *zswap_pool;
+/* Compressed storage to use */
+#define ZSWAP_ZPOOL_DEFAULT "zbud"
+static char *zswap_zpool_type = ZSWAP_ZPOOL_DEFAULT;
+module_param_named(zpool, zswap_zpool_type, charp, 0444);
+
+/* zpool is shared by all of zswap backend  */
+static struct zpool *zswap_pool;
 
 /*********************************
 * compression functions
@@ -168,7 +173,7 @@ static void zswap_comp_exit(void)
  *            be held while changing the refcount.  Since the lock must
  *            be held, there is no reason to also make refcount atomic.
  * offset - the swap offset for the entry.  Index into the red-black tree.
- * handle - zbud allocation handle that stores the compressed page data
+ * handle - zpool allocation handle that stores the compressed page data
  * length - the length in bytes of the compressed page data.  Needed during
  *          decompression
  */
@@ -284,15 +289,15 @@ static void zswap_rb_erase(struct rb_root *root, struct zswap_entry *entry)
 }
 
 /*
- * Carries out the common pattern of freeing and entry's zbud allocation,
+ * Carries out the common pattern of freeing and entry's zpool allocation,
  * freeing the entry itself, and decrementing the number of stored pages.
  */
 static void zswap_free_entry(struct zswap_entry *entry)
 {
-	zbud_free(zswap_pool, entry->handle);
+	zpool_free(zswap_pool, entry->handle);
 	zswap_entry_cache_free(entry);
 	atomic_dec(&zswap_stored_pages);
-	zswap_pool_pages = zbud_get_pool_size(zswap_pool);
+	zswap_pool_total_size = zpool_get_total_size(zswap_pool);
 }
 
 /* caller must hold the tree lock */
@@ -409,7 +414,7 @@ cleanup:
 static bool zswap_is_full(void)
 {
 	return totalram_pages * zswap_max_pool_percent / 100 <
-		zswap_pool_pages;
+		DIV_ROUND_UP(zswap_pool_total_size, PAGE_SIZE);
 }
 
 /*********************************
@@ -525,7 +530,7 @@ static int zswap_get_swap_cache_page(swp_entry_t entry,
  * the swap cache, the compressed version stored by zswap can be
  * freed.
  */
-static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle)
+static int zswap_writeback_entry(struct zpool *pool, unsigned long handle)
 {
 	struct zswap_header *zhdr;
 	swp_entry_t swpentry;
@@ -541,9 +546,9 @@ static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle)
 	};
 
 	/* extract swpentry from data */
-	zhdr = zbud_map(pool, handle);
+	zhdr = zpool_map_handle(pool, handle, ZPOOL_MM_RO);
 	swpentry = zhdr->swpentry; /* here */
-	zbud_unmap(pool, handle);
+	zpool_unmap_handle(pool, handle);
 	tree = zswap_trees[swp_type(swpentry)];
 	offset = swp_offset(swpentry);
 
@@ -573,13 +578,13 @@ static int zswap_writeback_entry(struct zbud_pool *pool, unsigned long handle)
 	case ZSWAP_SWAPCACHE_NEW: /* page is locked */
 		/* decompress */
 		dlen = PAGE_SIZE;
-		src = (u8 *)zbud_map(zswap_pool, entry->handle) +
-			sizeof(struct zswap_header);
+		src = (u8 *)zpool_map_handle(zswap_pool, entry->handle,
+				ZPOOL_MM_RO) + sizeof(struct zswap_header);
 		dst = kmap_atomic(page);
 		ret = zswap_comp_op(ZSWAP_COMPOP_DECOMPRESS, src,
 				entry->length, dst, &dlen);
 		kunmap_atomic(dst);
-		zbud_unmap(zswap_pool, entry->handle);
+		zpool_unmap_handle(zswap_pool, entry->handle);
 		BUG_ON(ret);
 		BUG_ON(dlen != PAGE_SIZE);
 
@@ -652,7 +657,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 	/* reclaim space if needed */
 	if (zswap_is_full()) {
 		zswap_pool_limit_hit++;
-		if (zbud_reclaim_page(zswap_pool, 8)) {
+		if (zpool_shrink(zswap_pool, 1, NULL)) {
 			zswap_reject_reclaim_fail++;
 			ret = -ENOMEM;
 			goto reject;
@@ -679,7 +684,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 
 	/* store */
 	len = dlen + sizeof(struct zswap_header);
-	ret = zbud_alloc(zswap_pool, len, __GFP_NORETRY | __GFP_NOWARN,
+	ret = zpool_malloc(zswap_pool, len, __GFP_NORETRY | __GFP_NOWARN,
 		&handle);
 	if (ret == -ENOSPC) {
 		zswap_reject_compress_poor++;
@@ -689,11 +694,11 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 		zswap_reject_alloc_fail++;
 		goto freepage;
 	}
-	zhdr = zbud_map(zswap_pool, handle);
+	zhdr = zpool_map_handle(zswap_pool, handle, ZPOOL_MM_RW);
 	zhdr->swpentry = swp_entry(type, offset);
 	buf = (u8 *)(zhdr + 1);
 	memcpy(buf, dst, dlen);
-	zbud_unmap(zswap_pool, handle);
+	zpool_unmap_handle(zswap_pool, handle);
 	put_cpu_var(zswap_dstmem);
 
 	/* populate entry */
@@ -716,7 +721,7 @@ static int zswap_frontswap_store(unsigned type, pgoff_t offset,
 
 	/* update stats */
 	atomic_inc(&zswap_stored_pages);
-	zswap_pool_pages = zbud_get_pool_size(zswap_pool);
+	zswap_pool_total_size = zpool_get_total_size(zswap_pool);
 
 	return 0;
 
@@ -752,13 +757,13 @@ static int zswap_frontswap_load(unsigned type, pgoff_t offset,
 
 	/* decompress */
 	dlen = PAGE_SIZE;
-	src = (u8 *)zbud_map(zswap_pool, entry->handle) +
-			sizeof(struct zswap_header);
+	src = (u8 *)zpool_map_handle(zswap_pool, entry->handle,
+			ZPOOL_MM_RO) + sizeof(struct zswap_header);
 	dst = kmap_atomic(page);
 	ret = zswap_comp_op(ZSWAP_COMPOP_DECOMPRESS, src, entry->length,
 		dst, &dlen);
 	kunmap_atomic(dst);
-	zbud_unmap(zswap_pool, entry->handle);
+	zpool_unmap_handle(zswap_pool, entry->handle);
 	BUG_ON(ret);
 
 	spin_lock(&tree->lock);
@@ -811,7 +816,7 @@ static void zswap_frontswap_invalidate_area(unsigned type)
 	zswap_trees[type] = NULL;
 }
 
-static struct zbud_ops zswap_zbud_ops = {
+static struct zpool_ops zswap_zpool_ops = {
 	.evict = zswap_writeback_entry
 };
 
@@ -869,8 +874,8 @@ static int __init zswap_debugfs_init(void)
 			zswap_debugfs_root, &zswap_written_back_pages);
 	debugfs_create_u64("duplicate_entry", S_IRUGO,
 			zswap_debugfs_root, &zswap_duplicate_entry);
-	debugfs_create_u64("pool_pages", S_IRUGO,
-			zswap_debugfs_root, &zswap_pool_pages);
+	debugfs_create_u64("pool_total_size", S_IRUGO,
+			zswap_debugfs_root, &zswap_pool_total_size);
 	debugfs_create_atomic_t("stored_pages", S_IRUGO,
 			zswap_debugfs_root, &zswap_stored_pages);
 
@@ -895,16 +900,26 @@ static void __exit zswap_debugfs_exit(void) { }
 **********************************/
 static int __init init_zswap(void)
 {
+	gfp_t gfp = __GFP_NORETRY | __GFP_NOWARN;
+
 	if (!zswap_enabled)
 		return 0;
 
 	pr_info("loading zswap\n");
 
-	zswap_pool = zbud_create_pool(GFP_KERNEL, &zswap_zbud_ops);
+	zswap_pool = zpool_create_pool(zswap_zpool_type, gfp, &zswap_zpool_ops);
+	if (!zswap_pool && strcmp(zswap_zpool_type, ZSWAP_ZPOOL_DEFAULT)) {
+		pr_info("%s zpool not available\n", zswap_zpool_type);
+		zswap_zpool_type = ZSWAP_ZPOOL_DEFAULT;
+		zswap_pool = zpool_create_pool(zswap_zpool_type, gfp,
+					&zswap_zpool_ops);
+	}
 	if (!zswap_pool) {
-		pr_err("zbud pool creation failed\n");
+		pr_err("%s zpool not available\n", zswap_zpool_type);
+		pr_err("zpool creation failed\n");
 		goto error;
 	}
+	pr_info("using %s pool\n", zswap_zpool_type);
 
 	if (zswap_entry_cache_create()) {
 		pr_err("entry cache creation failed\n");
@@ -928,7 +943,7 @@ pcpufail:
 compfail:
 	zswap_entry_cache_destory();
 cachefail:
-	zbud_destroy_pool(zswap_pool);
+	zpool_destroy_pool(zswap_pool);
 error:
 	return -ENOMEM;
 }
-- 
1.8.3.1


^ permalink raw reply related	[flat|nested] 65+ messages in thread

* Re: [PATCHv5 0/4] mm/zpool: add common api for zswap to use zbud/zsmalloc
  2014-07-02 21:45       ` Dan Streetman
                           ` (3 preceding siblings ...)
  2014-07-02 21:45         ` [PATCHv5 4/4] mm/zpool: update zswap to use zpool Dan Streetman
@ 2014-07-14 18:10         ` Dan Streetman
  2014-07-16 20:59           ` Seth Jennings
  4 siblings, 1 reply; 65+ messages in thread
From: Dan Streetman @ 2014-07-14 18:10 UTC (permalink / raw)
  To: Seth Jennings, Andrew Morton
  Cc: Dan Streetman, Bob Liu, Nitin Gupta, Hugh Dickins, Minchan Kim,
	Weijie Yang, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

Andrew, any thoughts on this latest version of the patch set?  Let me
know if I missed anything or you have any other suggestions.

Seth, did you get a chance to review this and/or test it out?



On Wed, Jul 2, 2014 at 5:45 PM, Dan Streetman <ddstreet@ieee.org> wrote:
> In order to allow zswap users to choose between zbud and zsmalloc for
> the compressed storage pool, this patch set adds a new api "zpool" that
> provides an interface to both zbud and zsmalloc.  This does not include
> implementing shrinking in zsmalloc, which will be sent separately.
>
> I believe Seth originally was using zsmalloc for swap, but there were
> concerns about how significant the impact of shrinking zsmalloc would
> be when zswap had to start reclaiming pages.  That still may be an
> issue, but this at least allows users to choose themselves whether
> they want a lower-density or higher-density compressed storage medium.
> At least for situations where zswap reclaim is never or rarely reached,
> it probably makes sense to use the higher density of zsmalloc.
>
> Note this patch set does not change zram to use zpool, although that
> change should be possible as well.
>
> ---
> Changes since v4 : https://lkml.org/lkml/2014/6/2/711
>   -omit first patch, that removed gfp_t param from zpool_malloc()
>   -move function doc from zpool.h to zpool.c
>   -move module usage refcounting into patch that adds zpool
>   -add extra refcounting to prevent driver unregister if in use
>   -add doc clarifying concurrency usage
>   -make zbud/zsmalloc zpool functions static
>   -typo corrections
>
> Changes since v3 : https://lkml.org/lkml/2014/5/24/130
>   -In zpool_shrink() use # pages instead of # bytes
>   -Add reclaimed param to zpool_shrink() to indicate to caller
>    # pages actually reclaimed
>   -move module usage counting to zpool, from zbud/zsmalloc
>   -update zbud_zpool_shrink() to call zbud_reclaim_page() in a
>    loop until requested # pages have been reclaimed (or error)
>
> Changes since v2 : https://lkml.org/lkml/2014/5/7/927
>   -Change zpool to use driver registration instead of hardcoding
>    implementations
>   -Add module use counting in zbud/zsmalloc
>
> Changes since v1 https://lkml.org/lkml/2014/4/19/97
>  -remove zsmalloc shrinking
>  -change zbud size param type from unsigned int to size_t
>  -remove zpool fallback creation
>  -zswap manually falls back to zbud if specified type fails
>
>
> Dan Streetman (4):
>   mm/zbud: change zbud_alloc size type to size_t
>   mm/zpool: implement common zpool api to zbud/zsmalloc
>   mm/zpool: zbud/zsmalloc implement zpool
>   mm/zpool: update zswap to use zpool
>
>  include/linux/zbud.h  |   2 +-
>  include/linux/zpool.h | 106 +++++++++++++++
>  mm/Kconfig            |  43 +++---
>  mm/Makefile           |   1 +
>  mm/zbud.c             |  98 +++++++++++++-
>  mm/zpool.c            | 364 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  mm/zsmalloc.c         |  84 ++++++++++++
>  mm/zswap.c            |  75 ++++++-----
>  8 files changed, 722 insertions(+), 51 deletions(-)
>  create mode 100644 include/linux/zpool.h
>  create mode 100644 mm/zpool.c
>
> --
> 1.8.3.1
>

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv5 0/4] mm/zpool: add common api for zswap to use zbud/zsmalloc
  2014-07-14 18:10         ` [PATCHv5 0/4] mm/zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
@ 2014-07-16 20:59           ` Seth Jennings
  2014-07-16 21:05             ` Dan Streetman
  0 siblings, 1 reply; 65+ messages in thread
From: Seth Jennings @ 2014-07-16 20:59 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Andrew Morton, Bob Liu, Nitin Gupta, Hugh Dickins, Minchan Kim,
	Weijie Yang, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Mon, Jul 14, 2014 at 02:10:42PM -0400, Dan Streetman wrote:
> Andrew, any thoughts on this latest version of the patch set?  Let me
> know if I missed anything or you have any other suggestions.
> 
> Seth, did you get a chance to review this and/or test it out?

I did have a chance to test it out quickly and didn't run into any
issues.  Your patchset is already in linux-next so I'll test more from
there.

Seth

> 
> 
> 
> On Wed, Jul 2, 2014 at 5:45 PM, Dan Streetman <ddstreet@ieee.org> wrote:
> > In order to allow zswap users to choose between zbud and zsmalloc for
> > the compressed storage pool, this patch set adds a new api "zpool" that
> > provides an interface to both zbud and zsmalloc.  This does not include
> > implementing shrinking in zsmalloc, which will be sent separately.
> >
> > I believe Seth originally was using zsmalloc for swap, but there were
> > concerns about how significant the impact of shrinking zsmalloc would
> > be when zswap had to start reclaiming pages.  That still may be an
> > issue, but this at least allows users to choose themselves whether
> > they want a lower-density or higher-density compressed storage medium.
> > At least for situations where zswap reclaim is never or rarely reached,
> > it probably makes sense to use the higher density of zsmalloc.
> >
> > Note this patch set does not change zram to use zpool, although that
> > change should be possible as well.
> >
> > ---
> > Changes since v4 : https://lkml.org/lkml/2014/6/2/711
> >   -omit first patch, that removed gfp_t param from zpool_malloc()
> >   -move function doc from zpool.h to zpool.c
> >   -move module usage refcounting into patch that adds zpool
> >   -add extra refcounting to prevent driver unregister if in use
> >   -add doc clarifying concurrency usage
> >   -make zbud/zsmalloc zpool functions static
> >   -typo corrections
> >
> > Changes since v3 : https://lkml.org/lkml/2014/5/24/130
> >   -In zpool_shrink() use # pages instead of # bytes
> >   -Add reclaimed param to zpool_shrink() to indicate to caller
> >    # pages actually reclaimed
> >   -move module usage counting to zpool, from zbud/zsmalloc
> >   -update zbud_zpool_shrink() to call zbud_reclaim_page() in a
> >    loop until requested # pages have been reclaimed (or error)
> >
> > Changes since v2 : https://lkml.org/lkml/2014/5/7/927
> >   -Change zpool to use driver registration instead of hardcoding
> >    implementations
> >   -Add module use counting in zbud/zsmalloc
> >
> > Changes since v1 https://lkml.org/lkml/2014/4/19/97
> >  -remove zsmalloc shrinking
> >  -change zbud size param type from unsigned int to size_t
> >  -remove zpool fallback creation
> >  -zswap manually falls back to zbud if specified type fails
> >
> >
> > Dan Streetman (4):
> >   mm/zbud: change zbud_alloc size type to size_t
> >   mm/zpool: implement common zpool api to zbud/zsmalloc
> >   mm/zpool: zbud/zsmalloc implement zpool
> >   mm/zpool: update zswap to use zpool
> >
> >  include/linux/zbud.h  |   2 +-
> >  include/linux/zpool.h | 106 +++++++++++++++
> >  mm/Kconfig            |  43 +++---
> >  mm/Makefile           |   1 +
> >  mm/zbud.c             |  98 +++++++++++++-
> >  mm/zpool.c            | 364 ++++++++++++++++++++++++++++++++++++++++++++++++++
> >  mm/zsmalloc.c         |  84 ++++++++++++
> >  mm/zswap.c            |  75 ++++++-----
> >  8 files changed, 722 insertions(+), 51 deletions(-)
> >  create mode 100644 include/linux/zpool.h
> >  create mode 100644 mm/zpool.c
> >
> > --
> > 1.8.3.1
> >

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv5 0/4] mm/zpool: add common api for zswap to use zbud/zsmalloc
  2014-07-16 20:59           ` Seth Jennings
@ 2014-07-16 21:05             ` Dan Streetman
  2014-07-16 22:00               ` Seth Jennings
  0 siblings, 1 reply; 65+ messages in thread
From: Dan Streetman @ 2014-07-16 21:05 UTC (permalink / raw)
  To: Seth Jennings
  Cc: Andrew Morton, Bob Liu, Nitin Gupta, Hugh Dickins, Minchan Kim,
	Weijie Yang, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Wed, Jul 16, 2014 at 4:59 PM, Seth Jennings <sjennings@variantweb.net> wrote:
> On Mon, Jul 14, 2014 at 02:10:42PM -0400, Dan Streetman wrote:
>> Andrew, any thoughts on this latest version of the patch set?  Let me
>> know if I missed anything or you have any other suggestions.
>>
>> Seth, did you get a chance to review this and/or test it out?
>
> I did have a chance to test it out quickly and didn't run into any
> issues.  Your patchset is already in linux-next so I'll test more from
> there.

This latest version has a few changes that Andrew requested, which
presumably will replace the patches that are currently in -mm and
-next; can you test with these patches instead of (or in addition to)
what's in -next?

>
> Seth
>
>>
>>
>>
>> On Wed, Jul 2, 2014 at 5:45 PM, Dan Streetman <ddstreet@ieee.org> wrote:
>> > In order to allow zswap users to choose between zbud and zsmalloc for
>> > the compressed storage pool, this patch set adds a new api "zpool" that
>> > provides an interface to both zbud and zsmalloc.  This does not include
>> > implementing shrinking in zsmalloc, which will be sent separately.
>> >
>> > I believe Seth originally was using zsmalloc for swap, but there were
>> > concerns about how significant the impact of shrinking zsmalloc would
>> > be when zswap had to start reclaiming pages.  That still may be an
>> > issue, but this at least allows users to choose themselves whether
>> > they want a lower-density or higher-density compressed storage medium.
>> > At least for situations where zswap reclaim is never or rarely reached,
>> > it probably makes sense to use the higher density of zsmalloc.
>> >
>> > Note this patch set does not change zram to use zpool, although that
>> > change should be possible as well.
>> >
>> > ---
>> > Changes since v4 : https://lkml.org/lkml/2014/6/2/711
>> >   -omit first patch, that removed gfp_t param from zpool_malloc()
>> >   -move function doc from zpool.h to zpool.c
>> >   -move module usage refcounting into patch that adds zpool
>> >   -add extra refcounting to prevent driver unregister if in use
>> >   -add doc clarifying concurrency usage
>> >   -make zbud/zsmalloc zpool functions static
>> >   -typo corrections
>> >
>> > Changes since v3 : https://lkml.org/lkml/2014/5/24/130
>> >   -In zpool_shrink() use # pages instead of # bytes
>> >   -Add reclaimed param to zpool_shrink() to indicate to caller
>> >    # pages actually reclaimed
>> >   -move module usage counting to zpool, from zbud/zsmalloc
>> >   -update zbud_zpool_shrink() to call zbud_reclaim_page() in a
>> >    loop until requested # pages have been reclaimed (or error)
>> >
>> > Changes since v2 : https://lkml.org/lkml/2014/5/7/927
>> >   -Change zpool to use driver registration instead of hardcoding
>> >    implementations
>> >   -Add module use counting in zbud/zsmalloc
>> >
>> > Changes since v1 https://lkml.org/lkml/2014/4/19/97
>> >  -remove zsmalloc shrinking
>> >  -change zbud size param type from unsigned int to size_t
>> >  -remove zpool fallback creation
>> >  -zswap manually falls back to zbud if specified type fails
>> >
>> >
>> > Dan Streetman (4):
>> >   mm/zbud: change zbud_alloc size type to size_t
>> >   mm/zpool: implement common zpool api to zbud/zsmalloc
>> >   mm/zpool: zbud/zsmalloc implement zpool
>> >   mm/zpool: update zswap to use zpool
>> >
>> >  include/linux/zbud.h  |   2 +-
>> >  include/linux/zpool.h | 106 +++++++++++++++
>> >  mm/Kconfig            |  43 +++---
>> >  mm/Makefile           |   1 +
>> >  mm/zbud.c             |  98 +++++++++++++-
>> >  mm/zpool.c            | 364 ++++++++++++++++++++++++++++++++++++++++++++++++++
>> >  mm/zsmalloc.c         |  84 ++++++++++++
>> >  mm/zswap.c            |  75 ++++++-----
>> >  8 files changed, 722 insertions(+), 51 deletions(-)
>> >  create mode 100644 include/linux/zpool.h
>> >  create mode 100644 mm/zpool.c
>> >
>> > --
>> > 1.8.3.1
>> >

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv5 0/4] mm/zpool: add common api for zswap to use zbud/zsmalloc
  2014-07-16 21:05             ` Dan Streetman
@ 2014-07-16 22:00               ` Seth Jennings
  2014-07-25 16:59                 ` Dan Streetman
  0 siblings, 1 reply; 65+ messages in thread
From: Seth Jennings @ 2014-07-16 22:00 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Andrew Morton, Bob Liu, Nitin Gupta, Hugh Dickins, Minchan Kim,
	Weijie Yang, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Wed, Jul 16, 2014 at 05:05:45PM -0400, Dan Streetman wrote:
> On Wed, Jul 16, 2014 at 4:59 PM, Seth Jennings <sjennings@variantweb.net> wrote:
> > On Mon, Jul 14, 2014 at 02:10:42PM -0400, Dan Streetman wrote:
> >> Andrew, any thoughts on this latest version of the patch set?  Let me
> >> know if I missed anything or you have any other suggestions.
> >>
> >> Seth, did you get a chance to review this and/or test it out?
> >
> > I did have a chance to test it out quickly and didn't run into any
> > issues.  Your patchset is already in linux-next so I'll test more from
> > there.
> 
> This latest version has a few changes that Andrew requested, which
> presumably will replace the patches that are currently in -mm and
> -next; can you test with these patches instead of (or in addition to)
> what's in -next?

Looks like Andrew just did the legwork for me to get the new patches
into mmotm.  When the hit there (tomorrow?), I'll put it down and test
with that.

Thanks,
Seth

> 
> >
> > Seth
> >
> >>
> >>
> >>
> >> On Wed, Jul 2, 2014 at 5:45 PM, Dan Streetman <ddstreet@ieee.org> wrote:
> >> > In order to allow zswap users to choose between zbud and zsmalloc for
> >> > the compressed storage pool, this patch set adds a new api "zpool" that
> >> > provides an interface to both zbud and zsmalloc.  This does not include
> >> > implementing shrinking in zsmalloc, which will be sent separately.
> >> >
> >> > I believe Seth originally was using zsmalloc for swap, but there were
> >> > concerns about how significant the impact of shrinking zsmalloc would
> >> > be when zswap had to start reclaiming pages.  That still may be an
> >> > issue, but this at least allows users to choose themselves whether
> >> > they want a lower-density or higher-density compressed storage medium.
> >> > At least for situations where zswap reclaim is never or rarely reached,
> >> > it probably makes sense to use the higher density of zsmalloc.
> >> >
> >> > Note this patch set does not change zram to use zpool, although that
> >> > change should be possible as well.
> >> >
> >> > ---
> >> > Changes since v4 : https://lkml.org/lkml/2014/6/2/711
> >> >   -omit first patch, that removed gfp_t param from zpool_malloc()
> >> >   -move function doc from zpool.h to zpool.c
> >> >   -move module usage refcounting into patch that adds zpool
> >> >   -add extra refcounting to prevent driver unregister if in use
> >> >   -add doc clarifying concurrency usage
> >> >   -make zbud/zsmalloc zpool functions static
> >> >   -typo corrections
> >> >
> >> > Changes since v3 : https://lkml.org/lkml/2014/5/24/130
> >> >   -In zpool_shrink() use # pages instead of # bytes
> >> >   -Add reclaimed param to zpool_shrink() to indicate to caller
> >> >    # pages actually reclaimed
> >> >   -move module usage counting to zpool, from zbud/zsmalloc
> >> >   -update zbud_zpool_shrink() to call zbud_reclaim_page() in a
> >> >    loop until requested # pages have been reclaimed (or error)
> >> >
> >> > Changes since v2 : https://lkml.org/lkml/2014/5/7/927
> >> >   -Change zpool to use driver registration instead of hardcoding
> >> >    implementations
> >> >   -Add module use counting in zbud/zsmalloc
> >> >
> >> > Changes since v1 https://lkml.org/lkml/2014/4/19/97
> >> >  -remove zsmalloc shrinking
> >> >  -change zbud size param type from unsigned int to size_t
> >> >  -remove zpool fallback creation
> >> >  -zswap manually falls back to zbud if specified type fails
> >> >
> >> >
> >> > Dan Streetman (4):
> >> >   mm/zbud: change zbud_alloc size type to size_t
> >> >   mm/zpool: implement common zpool api to zbud/zsmalloc
> >> >   mm/zpool: zbud/zsmalloc implement zpool
> >> >   mm/zpool: update zswap to use zpool
> >> >
> >> >  include/linux/zbud.h  |   2 +-
> >> >  include/linux/zpool.h | 106 +++++++++++++++
> >> >  mm/Kconfig            |  43 +++---
> >> >  mm/Makefile           |   1 +
> >> >  mm/zbud.c             |  98 +++++++++++++-
> >> >  mm/zpool.c            | 364 ++++++++++++++++++++++++++++++++++++++++++++++++++
> >> >  mm/zsmalloc.c         |  84 ++++++++++++
> >> >  mm/zswap.c            |  75 ++++++-----
> >> >  8 files changed, 722 insertions(+), 51 deletions(-)
> >> >  create mode 100644 include/linux/zpool.h
> >> >  create mode 100644 mm/zpool.c
> >> >
> >> > --
> >> > 1.8.3.1
> >> >

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv5 0/4] mm/zpool: add common api for zswap to use zbud/zsmalloc
  2014-07-16 22:00               ` Seth Jennings
@ 2014-07-25 16:59                 ` Dan Streetman
  2014-07-28 20:40                   ` Seth Jennings
  0 siblings, 1 reply; 65+ messages in thread
From: Dan Streetman @ 2014-07-25 16:59 UTC (permalink / raw)
  To: Seth Jennings
  Cc: Andrew Morton, Bob Liu, Nitin Gupta, Hugh Dickins, Minchan Kim,
	Weijie Yang, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

Hey Seth,

have a chance to test yet?

On Wed, Jul 16, 2014 at 6:00 PM, Seth Jennings <sjennings@variantweb.net> wrote:
> On Wed, Jul 16, 2014 at 05:05:45PM -0400, Dan Streetman wrote:
>> On Wed, Jul 16, 2014 at 4:59 PM, Seth Jennings <sjennings@variantweb.net> wrote:
>> > On Mon, Jul 14, 2014 at 02:10:42PM -0400, Dan Streetman wrote:
>> >> Andrew, any thoughts on this latest version of the patch set?  Let me
>> >> know if I missed anything or you have any other suggestions.
>> >>
>> >> Seth, did you get a chance to review this and/or test it out?
>> >
>> > I did have a chance to test it out quickly and didn't run into any
>> > issues.  Your patchset is already in linux-next so I'll test more from
>> > there.
>>
>> This latest version has a few changes that Andrew requested, which
>> presumably will replace the patches that are currently in -mm and
>> -next; can you test with these patches instead of (or in addition to)
>> what's in -next?
>
> Looks like Andrew just did the legwork for me to get the new patches
> into mmotm.  When the hit there (tomorrow?), I'll put it down and test
> with that.
>
> Thanks,
> Seth
>
>>
>> >
>> > Seth
>> >
>> >>
>> >>
>> >>
>> >> On Wed, Jul 2, 2014 at 5:45 PM, Dan Streetman <ddstreet@ieee.org> wrote:
>> >> > In order to allow zswap users to choose between zbud and zsmalloc for
>> >> > the compressed storage pool, this patch set adds a new api "zpool" that
>> >> > provides an interface to both zbud and zsmalloc.  This does not include
>> >> > implementing shrinking in zsmalloc, which will be sent separately.
>> >> >
>> >> > I believe Seth originally was using zsmalloc for swap, but there were
>> >> > concerns about how significant the impact of shrinking zsmalloc would
>> >> > be when zswap had to start reclaiming pages.  That still may be an
>> >> > issue, but this at least allows users to choose themselves whether
>> >> > they want a lower-density or higher-density compressed storage medium.
>> >> > At least for situations where zswap reclaim is never or rarely reached,
>> >> > it probably makes sense to use the higher density of zsmalloc.
>> >> >
>> >> > Note this patch set does not change zram to use zpool, although that
>> >> > change should be possible as well.
>> >> >
>> >> > ---
>> >> > Changes since v4 : https://lkml.org/lkml/2014/6/2/711
>> >> >   -omit first patch, that removed gfp_t param from zpool_malloc()
>> >> >   -move function doc from zpool.h to zpool.c
>> >> >   -move module usage refcounting into patch that adds zpool
>> >> >   -add extra refcounting to prevent driver unregister if in use
>> >> >   -add doc clarifying concurrency usage
>> >> >   -make zbud/zsmalloc zpool functions static
>> >> >   -typo corrections
>> >> >
>> >> > Changes since v3 : https://lkml.org/lkml/2014/5/24/130
>> >> >   -In zpool_shrink() use # pages instead of # bytes
>> >> >   -Add reclaimed param to zpool_shrink() to indicate to caller
>> >> >    # pages actually reclaimed
>> >> >   -move module usage counting to zpool, from zbud/zsmalloc
>> >> >   -update zbud_zpool_shrink() to call zbud_reclaim_page() in a
>> >> >    loop until requested # pages have been reclaimed (or error)
>> >> >
>> >> > Changes since v2 : https://lkml.org/lkml/2014/5/7/927
>> >> >   -Change zpool to use driver registration instead of hardcoding
>> >> >    implementations
>> >> >   -Add module use counting in zbud/zsmalloc
>> >> >
>> >> > Changes since v1 https://lkml.org/lkml/2014/4/19/97
>> >> >  -remove zsmalloc shrinking
>> >> >  -change zbud size param type from unsigned int to size_t
>> >> >  -remove zpool fallback creation
>> >> >  -zswap manually falls back to zbud if specified type fails
>> >> >
>> >> >
>> >> > Dan Streetman (4):
>> >> >   mm/zbud: change zbud_alloc size type to size_t
>> >> >   mm/zpool: implement common zpool api to zbud/zsmalloc
>> >> >   mm/zpool: zbud/zsmalloc implement zpool
>> >> >   mm/zpool: update zswap to use zpool
>> >> >
>> >> >  include/linux/zbud.h  |   2 +-
>> >> >  include/linux/zpool.h | 106 +++++++++++++++
>> >> >  mm/Kconfig            |  43 +++---
>> >> >  mm/Makefile           |   1 +
>> >> >  mm/zbud.c             |  98 +++++++++++++-
>> >> >  mm/zpool.c            | 364 ++++++++++++++++++++++++++++++++++++++++++++++++++
>> >> >  mm/zsmalloc.c         |  84 ++++++++++++
>> >> >  mm/zswap.c            |  75 ++++++-----
>> >> >  8 files changed, 722 insertions(+), 51 deletions(-)
>> >> >  create mode 100644 include/linux/zpool.h
>> >> >  create mode 100644 mm/zpool.c
>> >> >
>> >> > --
>> >> > 1.8.3.1
>> >> >

^ permalink raw reply	[flat|nested] 65+ messages in thread

* Re: [PATCHv5 0/4] mm/zpool: add common api for zswap to use zbud/zsmalloc
  2014-07-25 16:59                 ` Dan Streetman
@ 2014-07-28 20:40                   ` Seth Jennings
  0 siblings, 0 replies; 65+ messages in thread
From: Seth Jennings @ 2014-07-28 20:40 UTC (permalink / raw)
  To: Dan Streetman
  Cc: Andrew Morton, Bob Liu, Nitin Gupta, Hugh Dickins, Minchan Kim,
	Weijie Yang, Mel Gorman, Rik van Riel, Johannes Weiner,
	Sergey Senozhatsky, Linux-MM, linux-kernel

On Fri, Jul 25, 2014 at 12:59:05PM -0400, Dan Streetman wrote:
> Hey Seth,
> 
> have a chance to test yet?

Sorry for the delay. Things have been crazy.

Just pulled down -next and tested with a 8-thread kernel build on a
machine restricted to 512MB of RAM.  Thrashed pretty well but completed
without any problems. The compressed pool mitigating the swapping for
the most part.  swapoff at the end and all the statistics were sane.

Looks stable afaict.  Good work! :)

Again sorry about dragging my feet on this.

Thanks,
Seth

> 
> On Wed, Jul 16, 2014 at 6:00 PM, Seth Jennings <sjennings@variantweb.net> wrote:
> > On Wed, Jul 16, 2014 at 05:05:45PM -0400, Dan Streetman wrote:
> >> On Wed, Jul 16, 2014 at 4:59 PM, Seth Jennings <sjennings@variantweb.net> wrote:
> >> > On Mon, Jul 14, 2014 at 02:10:42PM -0400, Dan Streetman wrote:
> >> >> Andrew, any thoughts on this latest version of the patch set?  Let me
> >> >> know if I missed anything or you have any other suggestions.
> >> >>
> >> >> Seth, did you get a chance to review this and/or test it out?
> >> >
> >> > I did have a chance to test it out quickly and didn't run into any
> >> > issues.  Your patchset is already in linux-next so I'll test more from
> >> > there.
> >>
> >> This latest version has a few changes that Andrew requested, which
> >> presumably will replace the patches that are currently in -mm and
> >> -next; can you test with these patches instead of (or in addition to)
> >> what's in -next?
> >
> > Looks like Andrew just did the legwork for me to get the new patches
> > into mmotm.  When the hit there (tomorrow?), I'll put it down and test
> > with that.
> >
> > Thanks,
> > Seth
> >
> >>
> >> >
> >> > Seth
> >> >
> >> >>
> >> >>
> >> >>
> >> >> On Wed, Jul 2, 2014 at 5:45 PM, Dan Streetman <ddstreet@ieee.org> wrote:
> >> >> > In order to allow zswap users to choose between zbud and zsmalloc for
> >> >> > the compressed storage pool, this patch set adds a new api "zpool" that
> >> >> > provides an interface to both zbud and zsmalloc.  This does not include
> >> >> > implementing shrinking in zsmalloc, which will be sent separately.
> >> >> >
> >> >> > I believe Seth originally was using zsmalloc for swap, but there were
> >> >> > concerns about how significant the impact of shrinking zsmalloc would
> >> >> > be when zswap had to start reclaiming pages.  That still may be an
> >> >> > issue, but this at least allows users to choose themselves whether
> >> >> > they want a lower-density or higher-density compressed storage medium.
> >> >> > At least for situations where zswap reclaim is never or rarely reached,
> >> >> > it probably makes sense to use the higher density of zsmalloc.
> >> >> >
> >> >> > Note this patch set does not change zram to use zpool, although that
> >> >> > change should be possible as well.
> >> >> >
> >> >> > ---
> >> >> > Changes since v4 : https://lkml.org/lkml/2014/6/2/711
> >> >> >   -omit first patch, that removed gfp_t param from zpool_malloc()
> >> >> >   -move function doc from zpool.h to zpool.c
> >> >> >   -move module usage refcounting into patch that adds zpool
> >> >> >   -add extra refcounting to prevent driver unregister if in use
> >> >> >   -add doc clarifying concurrency usage
> >> >> >   -make zbud/zsmalloc zpool functions static
> >> >> >   -typo corrections
> >> >> >
> >> >> > Changes since v3 : https://lkml.org/lkml/2014/5/24/130
> >> >> >   -In zpool_shrink() use # pages instead of # bytes
> >> >> >   -Add reclaimed param to zpool_shrink() to indicate to caller
> >> >> >    # pages actually reclaimed
> >> >> >   -move module usage counting to zpool, from zbud/zsmalloc
> >> >> >   -update zbud_zpool_shrink() to call zbud_reclaim_page() in a
> >> >> >    loop until requested # pages have been reclaimed (or error)
> >> >> >
> >> >> > Changes since v2 : https://lkml.org/lkml/2014/5/7/927
> >> >> >   -Change zpool to use driver registration instead of hardcoding
> >> >> >    implementations
> >> >> >   -Add module use counting in zbud/zsmalloc
> >> >> >
> >> >> > Changes since v1 https://lkml.org/lkml/2014/4/19/97
> >> >> >  -remove zsmalloc shrinking
> >> >> >  -change zbud size param type from unsigned int to size_t
> >> >> >  -remove zpool fallback creation
> >> >> >  -zswap manually falls back to zbud if specified type fails
> >> >> >
> >> >> >
> >> >> > Dan Streetman (4):
> >> >> >   mm/zbud: change zbud_alloc size type to size_t
> >> >> >   mm/zpool: implement common zpool api to zbud/zsmalloc
> >> >> >   mm/zpool: zbud/zsmalloc implement zpool
> >> >> >   mm/zpool: update zswap to use zpool
> >> >> >
> >> >> >  include/linux/zbud.h  |   2 +-
> >> >> >  include/linux/zpool.h | 106 +++++++++++++++
> >> >> >  mm/Kconfig            |  43 +++---
> >> >> >  mm/Makefile           |   1 +
> >> >> >  mm/zbud.c             |  98 +++++++++++++-
> >> >> >  mm/zpool.c            | 364 ++++++++++++++++++++++++++++++++++++++++++++++++++
> >> >> >  mm/zsmalloc.c         |  84 ++++++++++++
> >> >> >  mm/zswap.c            |  75 ++++++-----
> >> >> >  8 files changed, 722 insertions(+), 51 deletions(-)
> >> >> >  create mode 100644 include/linux/zpool.h
> >> >> >  create mode 100644 mm/zpool.c
> >> >> >
> >> >> > --
> >> >> > 1.8.3.1
> >> >> >

^ permalink raw reply	[flat|nested] 65+ messages in thread

end of thread, other threads:[~2014-07-28 20:41 UTC | newest]

Thread overview: 65+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-04-19 15:52 [PATCH 0/4] mm: zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
2014-04-19 15:52 ` [PATCH 1/4] mm: zpool: zbud_alloc() minor param change Dan Streetman
2014-04-19 15:52 ` [PATCH 2/4] mm: zpool: implement zsmalloc shrinking Dan Streetman
2014-04-26  8:37   ` Weijie Yang
2014-04-27  4:13     ` Dan Streetman
2014-05-02 20:01     ` Seth Jennings
2014-05-04 20:38       ` Dan Streetman
2014-04-19 15:52 ` [PATCH 3/4] mm: zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
2014-04-22 10:05   ` Sergey Senozhatsky
2014-04-22 13:43     ` Dan Streetman
2014-04-19 15:52 ` [PATCH 4/4] mm: zpool: update zswap to use zpool Dan Streetman
2014-04-21  2:47 ` [PATCH 0/4] mm: zpool: add common api for zswap to use zbud/zsmalloc Weijie Yang
2014-05-07 21:51 ` [PATCHv2 0/4] mm/zpool: " Dan Streetman
2014-05-07 21:51   ` [PATCHv2 1/4] mm/zbud: zbud_alloc() minor param change Dan Streetman
2014-05-09  3:33     ` Seth Jennings
2014-05-07 21:51   ` [PATCH 2/4] mm/zbud: change zbud_alloc size type to size_t Dan Streetman
2014-05-09  3:33     ` Seth Jennings
2014-05-07 21:51   ` [PATCHv2 3/4] mm/zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
2014-05-09  4:13     ` Seth Jennings
2014-05-10 16:06       ` Dan Streetman
2014-05-07 21:51   ` [PATCHv2 4/4] mm/zswap: update zswap to use zpool Dan Streetman
2014-05-24 19:06   ` [PATCHv3 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
2014-05-24 19:06     ` [PATCHv2 1/6] mm/zbud: zbud_alloc() minor param change Dan Streetman
2014-05-24 19:06     ` [PATCH 2/6] mm/zbud: change zbud_alloc size type to size_t Dan Streetman
2014-05-24 19:06     ` [PATCHv3 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
2014-05-27 22:06       ` Seth Jennings
2014-05-27 22:48         ` Seth Jennings
2014-05-28  0:06         ` Dan Streetman
2014-05-29  3:48           ` Seth Jennings
2014-05-24 19:06     ` [PATCH 4/6] mm/zpool: zbud/zsmalloc implement zpool Dan Streetman
2014-05-24 19:06     ` [PATCHv3 5/6] mm/zpool: update zswap to use zpool Dan Streetman
2014-05-24 19:06     ` [PATCH 6/6] mm/zpool: prevent zbud/zsmalloc from unloading when used Dan Streetman
2014-05-27 22:40       ` Seth Jennings
2014-05-28  0:40         ` Dan Streetman
2014-05-27 22:44     ` [PATCHv3 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc Seth Jennings
2014-06-02 22:19     ` [PATCHv4 " Dan Streetman
2014-06-02 22:19       ` [PATCHv2 1/6] mm/zbud: zbud_alloc() minor param change Dan Streetman
2014-06-23 21:19         ` Andrew Morton
2014-06-24 15:24           ` Dan Streetman
2014-06-02 22:19       ` [PATCH 2/6] mm/zbud: change zbud_alloc size type to size_t Dan Streetman
2014-06-02 22:19       ` [PATCHv4 3/6] mm/zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
2014-06-23 21:46         ` Andrew Morton
2014-06-24 15:39           ` Dan Streetman
2014-06-24 23:08             ` Andrew Morton
2014-06-27 17:11               ` Dan Streetman
2014-06-27 19:17                 ` Andrew Morton
2014-06-02 22:19       ` [PATCHv2 4/6] mm/zpool: zbud/zsmalloc implement zpool Dan Streetman
2014-06-02 22:19       ` [PATCHv4 5/6] mm/zpool: update zswap to use zpool Dan Streetman
2014-06-02 22:19       ` [PATCHv2 6/6] mm/zpool: prevent zbud/zsmalloc from unloading when used Dan Streetman
2014-06-23 21:48         ` Andrew Morton
2014-06-24 15:41           ` Dan Streetman
2014-06-04  1:38       ` [PATCHv4 0/6] mm/zpool: add common api for zswap to use zbud/zsmalloc Bob Liu
2014-06-06 21:01       ` Seth Jennings
2014-07-02 21:43       ` [PATCHv5 0/4] " Dan Streetman
2014-07-02 21:45       ` Dan Streetman
2014-07-02 21:45         ` [PATCHv2 1/4] mm/zbud: change zbud_alloc size type to size_t Dan Streetman
2014-07-02 21:45         ` [PATCHv5 2/4] mm/zpool: implement common zpool api to zbud/zsmalloc Dan Streetman
2014-07-02 21:45         ` [PATCHv3 3/4] mm/zpool: zbud/zsmalloc implement zpool Dan Streetman
2014-07-02 21:45         ` [PATCHv5 4/4] mm/zpool: update zswap to use zpool Dan Streetman
2014-07-14 18:10         ` [PATCHv5 0/4] mm/zpool: add common api for zswap to use zbud/zsmalloc Dan Streetman
2014-07-16 20:59           ` Seth Jennings
2014-07-16 21:05             ` Dan Streetman
2014-07-16 22:00               ` Seth Jennings
2014-07-25 16:59                 ` Dan Streetman
2014-07-28 20:40                   ` Seth Jennings

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).