* [PATCHv3 0/4] add compression backend abstraction
@ 2014-02-11 22:21 Sergey Senozhatsky
2014-02-11 22:21 ` [PATCHv3 1/4] zram: introduce compressing " Sergey Senozhatsky
` (3 more replies)
0 siblings, 4 replies; 9+ messages in thread
From: Sergey Senozhatsky @ 2014-02-11 22:21 UTC (permalink / raw)
To: Minchan Kim
Cc: Jerome Marchand, Nitin Gupta, linux-kernel, Sergey Senozhatsky
This patchset introduces zram_comp compression backend abstraction
adding ability to support compression algorithms other than LZO,
and support for multi buffers, making parallel compressions possible.
Sergey Senozhatsky (4):
zram: introduce compressing backend abstraction
zram: use zram_comp compressing backends
zram: support multi compressing buffers
zram: document max_buffers
Documentation/ABI/testing/sysfs-block-zram | 9 +-
Documentation/blockdev/zram.txt | 21 +++-
drivers/block/zram/Makefile | 2 +-
drivers/block/zram/zcomp_lzo.c | 33 +++++++
drivers/block/zram/zram_comp.c | 150 +++++++++++++++++++++++++++++
drivers/block/zram/zram_comp.h | 58 +++++++++++
drivers/block/zram/zram_drv.c | 102 +++++++++++++-------
drivers/block/zram/zram_drv.h | 10 +-
8 files changed, 339 insertions(+), 46 deletions(-)
create mode 100644 drivers/block/zram/zcomp_lzo.c
create mode 100644 drivers/block/zram/zram_comp.c
create mode 100644 drivers/block/zram/zram_comp.h
--
1.9.0.rc3.244.g3497008
^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCHv3 1/4] zram: introduce compressing backend abstraction
2014-02-11 22:21 [PATCHv3 0/4] add compression backend abstraction Sergey Senozhatsky
@ 2014-02-11 22:21 ` Sergey Senozhatsky
2014-02-12 7:06 ` Minchan Kim
2014-02-12 7:08 ` Minchan Kim
2014-02-11 22:21 ` [PATCHv3 2/4] zram: use zram_comp compressing backends Sergey Senozhatsky
` (2 subsequent siblings)
3 siblings, 2 replies; 9+ messages in thread
From: Sergey Senozhatsky @ 2014-02-11 22:21 UTC (permalink / raw)
To: Minchan Kim
Cc: Jerome Marchand, Nitin Gupta, linux-kernel, Sergey Senozhatsky
ZRAM performs direct LZO compression algorithm calls, making it the one
and only option. Introduce abstract struct zram_comp in order to support
multiple compression algorithms. struct zram_comp defines the following
set of compressing backend operations:
.create
.destroy
.compress
.decompress
Schematically zram write() usually contains the following steps:
0) preparation (decompression of partioal IO, etc.)
1) lock buffer_lock mutex (protects meta compress buffers)
2) compress (using meta compress buffers)
3) alloc and map zs_pool object
4) copy compressed data (from meta compress buffers) to object allocated by 3)
5) free previous pool page, assign a new one
6) unlock buffer_lock mutex
As we can see, compressing buffers must remain untouched from 1) to 4),
because, otherwise, concurrent write() can overwrite data. At the same
time, zram_meta must be aware of a) specific compression algorithm
memory requirements and b) necessary locking to protect compression
buffers. Besides, zram holds buffer_lock almost through the whole write()
function, making parallel compression impossible. To remove requirement
a) new struct zcomp_workmem introduced, which contain buffers required by
compression algorithm. While struct zram_comp implements workmem handling
and locking by means of get() and put() semantics and removes requirement
b) from zram meta. zram_comp ->create() and ->destroy(), respectively,
allocate and deallocate algorithm specific zcomp_workmem `private' buffer.
Every zram_comp has a list of idle workmem buffers (at least 1 workmem),
spinlock to protect idle list and wait queue, making it possible to perform
parallel compressions. Each time zram issues a zcomp_workmem_get() call, the
following set of operations performed:
- spin lock buffer_lock
- if idle list is not empty, remove workmem from idle list, spin
unlock and return workmem pointer
- if idle list is empty, current adds itself to wait queue. it will be
awaken by zcomp_workmem_put() caller.
zcomp_workmem_put():
- spin lock buffer_lock
- add workmem to idle list
- spin unlock, wake up sleeper (if any)
In other words, zcomp_workmem_get() turns caller into exclusive user of workmem
and zcomp_workem_put() makes a particular workmem available.
Usage examples.
To initialize compressing backend:
comp = zcomp_create(NAME) /* NAME e.g. lzo */
which initialises compressing backend if requested algorithm is supported.
Compress:
wm = zcomp_workmem_get(comp)
zcomp_compress(comp, wm, src, src_len, &dst_len)
[..] /* copy compressed data */
zcomp_workmem_put(comp, wm)
Decompress:
zcomp_decompress(comp, src, src_len, dst, &dst_len);
Free compessing backend and its workmem:
zcomp_destroy(comp)
Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
---
drivers/block/zram/zcomp_lzo.c | 33 +++++++++
drivers/block/zram/zram_comp.c | 147 +++++++++++++++++++++++++++++++++++++++++
drivers/block/zram/zram_comp.h | 58 ++++++++++++++++
3 files changed, 238 insertions(+)
create mode 100644 drivers/block/zram/zcomp_lzo.c
create mode 100644 drivers/block/zram/zram_comp.c
create mode 100644 drivers/block/zram/zram_comp.h
diff --git a/drivers/block/zram/zcomp_lzo.c b/drivers/block/zram/zcomp_lzo.c
new file mode 100644
index 0000000..bbde74e
--- /dev/null
+++ b/drivers/block/zram/zcomp_lzo.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 Sergey Senozhatsky.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/lzo.h>
+
+#include "zram_comp.h"
+
+static void * lzo_create(void)
+{
+ return kmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
+}
+
+static void lzo_destroy(void *workmem)
+{
+ kfree(workmem);
+}
+
+extern struct zram_comp_backend zcomp_lzo;
+struct zram_comp_backend zcomp_lzo = {
+ .compress = lzo1x_1_compress,
+ .decompress = lzo1x_decompress_safe,
+ .create = lzo_create,
+ .destroy = lzo_destroy,
+ .name = "lzo",
+};
diff --git a/drivers/block/zram/zram_comp.c b/drivers/block/zram/zram_comp.c
new file mode 100644
index 0000000..139a468
--- /dev/null
+++ b/drivers/block/zram/zram_comp.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2014 Sergey Senozhatsky.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/wait.h>
+#include <linux/sched.h>
+
+#include "zram_comp.h"
+
+extern struct zram_comp_backend zcomp_lzo;
+
+static void workmem_free(struct zram_comp *comp, struct zcomp_workmem *workmem)
+{
+ comp->backend->destroy(workmem->private);
+ free_pages((unsigned long)workmem->buffer, 1);
+ kfree(workmem);
+}
+
+/* allocate new workmem structure with ->mem of requested size,
+ * return NULL on error */
+static struct zcomp_workmem *workmem_alloc(struct zram_comp *comp)
+{
+ struct zcomp_workmem *workmem = kmalloc(sizeof(*workmem), GFP_NOFS);
+ if (!workmem)
+ return NULL;
+
+ INIT_LIST_HEAD(&workmem->list);
+ /* algorithm specific working memory buffer */
+ workmem->private = comp->backend->create();
+ /* allocate 2 pages. 1 for compressed data, plus 1 extra for the
+ * case when compressed size is larger than the original one. */
+ workmem->buffer = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
+ if (!workmem->private || !workmem->buffer)
+ goto fail;
+
+ return workmem;
+fail:
+ workmem_free(comp, workmem);
+ return NULL;
+}
+
+/* get existing idle workmem or wait until other process release
+ * (workmem_put()) one for us */
+struct zcomp_workmem *zcomp_workmem_get(struct zram_comp *comp)
+{
+ struct zcomp_workmem *wm;
+retry:
+ spin_lock(&comp->buffer_lock);
+ if (list_empty(&comp->idle_workmem)) {
+ spin_unlock(&comp->buffer_lock);
+ wait_event(comp->workmem_wait,
+ !list_empty(&comp->idle_workmem));
+ goto retry;
+ }
+
+ wm = list_entry(comp->idle_workmem.next,
+ struct zcomp_workmem, list);
+ list_del(&wm->list);
+ spin_unlock(&comp->buffer_lock);
+ return wm;
+}
+
+/* add workmem back to idle list and wake up waiter (if any) */
+void zcomp_workmem_put(struct zram_comp *comp,
+ struct zcomp_workmem *workmem)
+{
+ spin_lock(&comp->buffer_lock);
+ list_add_tail(&workmem->list, &comp->idle_workmem);
+ spin_unlock(&comp->buffer_lock);
+
+ if (waitqueue_active(&comp->workmem_wait))
+ wake_up(&comp->workmem_wait);
+}
+
+int zcomp_compress(struct zram_comp *comp, struct zcomp_workmem *workmem,
+ const unsigned char *src, size_t src_len, size_t *dst_len)
+{
+ return comp->backend->compress(src, src_len, workmem->buffer,
+ dst_len, workmem->private);
+}
+
+int zcomp_decompress(struct zram_comp *comp, const unsigned char *src,
+ size_t src_len, unsigned char *dst, size_t *dst_len)
+{
+ return comp->backend->decompress(src, src_len, dst, dst_len);
+}
+
+/* free allocated workmem buffers and zram_comp */
+void zcomp_destroy(struct zram_comp *comp)
+{
+ struct zcomp_workmem *wm;
+ while (!list_empty(&comp->idle_workmem)) {
+ wm = list_entry(comp->idle_workmem.next,
+ struct zcomp_workmem, list);
+ list_del(&wm->list);
+ workmem_free(comp, wm);
+ }
+ kfree(comp);
+}
+
+static struct zram_comp_backend *find_backend(const char *compress)
+{
+ if (sysfs_streq(compress, "lzo"))
+ return &zcomp_lzo;
+ return NULL;
+}
+
+/* search available compressors for requested algorithm.
+ * allocate new zram_comp and initialize it. return NULL
+ * if requested algorithm is not supported or in case
+ * of init error */
+struct zram_comp *zcomp_create(const char *compress)
+{
+ struct zram_comp *comp;
+ struct zram_comp_backend *backend;
+ struct zcomp_workmem *wm;
+
+ backend = find_backend(compress);
+ if (!backend)
+ return NULL;
+
+ comp = kzalloc(sizeof(struct zram_comp), GFP_KERNEL);
+ if (!comp)
+ return NULL;
+
+ comp->backend = backend;
+ spin_lock_init(&comp->buffer_lock);
+ INIT_LIST_HEAD(&comp->idle_workmem);
+ init_waitqueue_head(&comp->workmem_wait);
+
+ wm = workmem_alloc(comp);
+ if (!wm) {
+ zcomp_destroy(comp);
+ return NULL;
+ }
+ list_add_tail(&wm->list, &comp->idle_workmem);
+ return comp;
+}
diff --git a/drivers/block/zram/zram_comp.h b/drivers/block/zram/zram_comp.h
new file mode 100644
index 0000000..90355e8
--- /dev/null
+++ b/drivers/block/zram/zram_comp.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2014 Sergey Senozhatsky.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _ZRAM_COMP_H_
+#define _ZRAM_COMP_H_
+
+#include <linux/types.h>
+#include <linux/spinlock.h>
+
+struct zcomp_workmem {
+ void *buffer; /* compression/decompression buffer */
+ void *private; /* algorithm workmem */
+ struct list_head list;
+};
+
+/* static compression backend */
+struct zram_comp_backend {
+ int (*compress)(const unsigned char *src, size_t src_len,
+ unsigned char *dst, size_t *dst_len, void *wrkmem);
+
+ int (*decompress)(const unsigned char *src, size_t src_len,
+ unsigned char *dst, size_t *dst_len);
+
+ void *(*create)(void);
+ void (*destroy)(void *workmem);
+
+ const char *name;
+};
+
+/* dynamic per-device compression frontend */
+struct zram_comp {
+ /* protect workmem list */
+ spinlock_t buffer_lock;
+ /* list of available workmems */
+ struct list_head idle_workmem;
+ wait_queue_head_t workmem_wait;
+ struct zram_comp_backend *backend;
+};
+
+struct zram_comp *zcomp_create(const char *comp);
+void zcomp_destroy(struct zram_comp *comp);
+
+struct zcomp_workmem *zcomp_workmem_get(struct zram_comp *comp);
+void zcomp_workmem_put(struct zram_comp *comp,
+ struct zcomp_workmem *workmem);
+
+int zcomp_compress(struct zram_comp *comp, struct zcomp_workmem *workmem,
+ const unsigned char *src, size_t src_len, size_t *dst_len);
+
+int zcomp_decompress(struct zram_comp *comp, const unsigned char *src,
+ size_t src_len, unsigned char *dst, size_t *dst_len);
+#endif /* _ZRAM_COMP_H_ */
--
1.9.0.rc3.244.g3497008
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCHv3 2/4] zram: use zram_comp compressing backends
2014-02-11 22:21 [PATCHv3 0/4] add compression backend abstraction Sergey Senozhatsky
2014-02-11 22:21 ` [PATCHv3 1/4] zram: introduce compressing " Sergey Senozhatsky
@ 2014-02-11 22:21 ` Sergey Senozhatsky
2014-02-11 22:21 ` [PATCHv3 3/4] zram: support multi compressing buffers Sergey Senozhatsky
2014-02-11 22:21 ` [PATCHv3 4/4] zram: document max_buffers Sergey Senozhatsky
3 siblings, 0 replies; 9+ messages in thread
From: Sergey Senozhatsky @ 2014-02-11 22:21 UTC (permalink / raw)
To: Minchan Kim
Cc: Jerome Marchand, Nitin Gupta, linux-kernel, Sergey Senozhatsky
Do not perform direct LZO compress/decompress calls,
initialise and use zram_comp LZO backend instead.
Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
---
drivers/block/zram/Makefile | 2 +-
drivers/block/zram/zram_drv.c | 62 +++++++++++++++++++------------------------
drivers/block/zram/zram_drv.h | 8 +++---
3 files changed, 33 insertions(+), 39 deletions(-)
diff --git a/drivers/block/zram/Makefile b/drivers/block/zram/Makefile
index cb0f9ce..4c2e829 100644
--- a/drivers/block/zram/Makefile
+++ b/drivers/block/zram/Makefile
@@ -1,3 +1,3 @@
-zram-y := zram_drv.o
+zram-y := zcomp_lzo.o zram_comp.o zram_drv.o
obj-$(CONFIG_ZRAM) += zram.o
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 9baac5b..4875a16 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -29,15 +29,14 @@
#include <linux/genhd.h>
#include <linux/highmem.h>
#include <linux/slab.h>
-#include <linux/lzo.h>
#include <linux/string.h>
-#include <linux/vmalloc.h>
#include "zram_drv.h"
/* Globals */
static int zram_major;
static struct zram *zram_devices;
+static const char *default_compressor = "lzo";
/* Module params (documentation at end) */
static unsigned int num_devices = 1;
@@ -160,8 +159,6 @@ static inline int valid_io_request(struct zram *zram, struct bio *bio)
static void zram_meta_free(struct zram_meta *meta)
{
zs_destroy_pool(meta->mem_pool);
- kfree(meta->compress_workmem);
- free_pages((unsigned long)meta->compress_buffer, 1);
vfree(meta->table);
kfree(meta);
}
@@ -173,22 +170,11 @@ static struct zram_meta *zram_meta_alloc(u64 disksize)
if (!meta)
goto out;
- meta->compress_workmem = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
- if (!meta->compress_workmem)
- goto free_meta;
-
- meta->compress_buffer =
- (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
- if (!meta->compress_buffer) {
- pr_err("Error allocating compressor buffer space\n");
- goto free_workmem;
- }
-
num_pages = disksize >> PAGE_SHIFT;
meta->table = vzalloc(num_pages * sizeof(*meta->table));
if (!meta->table) {
pr_err("Error allocating zram address table\n");
- goto free_buffer;
+ goto free_meta;
}
meta->mem_pool = zs_create_pool(GFP_NOIO | __GFP_HIGHMEM);
@@ -198,15 +184,10 @@ static struct zram_meta *zram_meta_alloc(u64 disksize)
}
rwlock_init(&meta->tb_lock);
- mutex_init(&meta->buffer_lock);
return meta;
free_table:
vfree(meta->table);
-free_buffer:
- free_pages((unsigned long)meta->compress_buffer, 1);
-free_workmem:
- kfree(meta->compress_workmem);
free_meta:
kfree(meta);
meta = NULL;
@@ -280,7 +261,7 @@ static void zram_free_page(struct zram *zram, size_t index)
static int zram_decompress_page(struct zram *zram, char *mem, u32 index)
{
- int ret = LZO_E_OK;
+ int ret = 0;
size_t clen = PAGE_SIZE;
unsigned char *cmem;
struct zram_meta *meta = zram->meta;
@@ -301,12 +282,12 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index)
if (size == PAGE_SIZE)
copy_page(mem, cmem);
else
- ret = lzo1x_decompress_safe(cmem, size, mem, &clen);
+ ret = zcomp_decompress(zram->comp, cmem, size, mem, &clen);
zs_unmap_object(meta->mem_pool, handle);
read_unlock(&meta->tb_lock);
/* Should NEVER happen. Return bio error if it does. */
- if (unlikely(ret != LZO_E_OK)) {
+ if (unlikely(ret)) {
pr_err("Decompression failed! err=%d, page=%u\n", ret, index);
atomic64_inc(&zram->stats.failed_reads);
return ret;
@@ -349,7 +330,7 @@ static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec,
ret = zram_decompress_page(zram, uncmem, index);
/* Should NEVER happen. Return bio error if it does. */
- if (unlikely(ret != LZO_E_OK))
+ if (unlikely(ret))
goto out_cleanup;
if (is_partial_io(bvec))
@@ -374,11 +355,10 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
struct page *page;
unsigned char *user_mem, *cmem, *src, *uncmem = NULL;
struct zram_meta *meta = zram->meta;
+ struct zcomp_workmem *wm;
bool locked = false;
page = bvec->bv_page;
- src = meta->compress_buffer;
-
if (is_partial_io(bvec)) {
/*
* This is a partial IO. We need to read the full page
@@ -394,7 +374,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
goto out;
}
- mutex_lock(&meta->buffer_lock);
+ wm = zcomp_workmem_get(zram->comp);
locked = true;
user_mem = kmap_atomic(page);
@@ -420,19 +400,18 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
goto out;
}
- ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen,
- meta->compress_workmem);
+ ret = zcomp_compress(zram->comp, wm, uncmem, bvec->bv_len, &clen);
if (!is_partial_io(bvec)) {
kunmap_atomic(user_mem);
user_mem = NULL;
uncmem = NULL;
}
- if (unlikely(ret != LZO_E_OK)) {
+ if (unlikely(ret)) {
pr_err("Compression failed! err=%d\n", ret);
goto out;
}
-
+ src = wm->buffer;
if (unlikely(clen > max_zpage_size)) {
clen = PAGE_SIZE;
src = NULL;
@@ -457,6 +436,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
memcpy(cmem, src, clen);
}
+ zcomp_workmem_put(zram->comp, wm);
+ locked = false;
zs_unmap_object(meta->mem_pool, handle);
/*
@@ -475,10 +456,9 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
atomic64_inc(&zram->stats.pages_stored);
out:
if (locked)
- mutex_unlock(&meta->buffer_lock);
+ zcomp_workmem_put(zram->comp, wm);
if (is_partial_io(bvec))
kfree(uncmem);
-
if (ret)
atomic64_inc(&zram->stats.failed_writes);
return ret;
@@ -522,6 +502,10 @@ static void zram_reset_device(struct zram *zram, bool reset_capacity)
zs_free(meta->mem_pool, handle);
}
+ if (zram->comp)
+ zcomp_destroy(zram->comp);
+ zram->comp = NULL;
+
zram_meta_free(zram->meta);
zram->meta = NULL;
/* Reset stats */
@@ -550,9 +534,18 @@ static ssize_t disksize_store(struct device *dev,
return -EBUSY;
}
+ zram->comp = zcomp_create(default_compressor);
+ if (!zram->comp) {
+ up_write(&zram->init_lock);
+ pr_info("Cannot initialise compressing backend\n");
+ return -EINVAL;
+ }
+
disksize = PAGE_ALIGN(disksize);
zram->meta = zram_meta_alloc(disksize);
if (!zram->meta) {
+ zcomp_destroy(zram->comp);
+ zram->comp = NULL;
up_write(&zram->init_lock);
return -ENOMEM;
}
@@ -790,6 +783,7 @@ static int create_device(struct zram *zram, int device_id)
}
zram->meta = NULL;
+ zram->comp = NULL;
return 0;
out_free_disk:
diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h
index 1d5b1f5..a2e17d7 100644
--- a/drivers/block/zram/zram_drv.h
+++ b/drivers/block/zram/zram_drv.h
@@ -16,9 +16,10 @@
#define _ZRAM_DRV_H_
#include <linux/spinlock.h>
-#include <linux/mutex.h>
#include <linux/zsmalloc.h>
+#include "zram_comp.h"
+
/*
* Some arbitrary value. This is just to catch
* invalid value for num_devices module parameter.
@@ -81,17 +82,16 @@ struct zram_stats {
struct zram_meta {
rwlock_t tb_lock; /* protect table */
- void *compress_workmem;
- void *compress_buffer;
struct table *table;
struct zs_pool *mem_pool;
- struct mutex buffer_lock; /* protect compress buffers */
};
struct zram {
struct zram_meta *meta;
struct request_queue *queue;
struct gendisk *disk;
+ struct zram_comp *comp;
+
/* Prevent concurrent execution of device init, reset and R/W request */
struct rw_semaphore init_lock;
/*
--
1.9.0.rc3.244.g3497008
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCHv3 3/4] zram: support multi compressing buffers
2014-02-11 22:21 [PATCHv3 0/4] add compression backend abstraction Sergey Senozhatsky
2014-02-11 22:21 ` [PATCHv3 1/4] zram: introduce compressing " Sergey Senozhatsky
2014-02-11 22:21 ` [PATCHv3 2/4] zram: use zram_comp compressing backends Sergey Senozhatsky
@ 2014-02-11 22:21 ` Sergey Senozhatsky
2014-02-11 22:21 ` [PATCHv3 4/4] zram: document max_buffers Sergey Senozhatsky
3 siblings, 0 replies; 9+ messages in thread
From: Sergey Senozhatsky @ 2014-02-11 22:21 UTC (permalink / raw)
To: Minchan Kim
Cc: Jerome Marchand, Nitin Gupta, linux-kernel, Sergey Senozhatsky
1) Extend zram_comp zcomp_create() with `num_wm' parameter.
`num_wm' limits the number of workmem structs in compression
backend's idle list (max buffers).
2) Introduce zram device attribute max_buffers to show and
store current device's zram_comp max number (num_wm) of workmems.
Usage example:
echo 4 > /sys/block/zram0/max_buffers
set max_buffers to 4
cat /sys/block/zram0/max_buffers
show current max_buffers (default value is 1).
iozone -t -T -R -l 3 -u 3 -r 16K -s 60M -I +Z
(write tests. ext4, lzo, each test performed on newly created
zram0 device)
test max_buffers 1 max_buffers 3
---------------------------------------------------
" Initial write " 631583.39 | 727834.88
" Rewrite " 456248.23 | 2105054.97
" Mixed workload " 3370016.43 | 3509382.86
" Random write " 467703.56 | 1953464.78
" Pwrite " 629146.88 | 775372.46
" Fwrite " 2573117.39 | 2529637.62
test max_buffers 1 max_buffers 3
---------------------------------------------------
" Initial write " 601112.91 | 765866.05
" Rewrite " 447834.33 | 2163449.58
" Mixed workload " 2575128.08 | 3257711.77
" Random write " 461327.45 | 1974731.12
" Pwrite " 567038.36 | 740242.52
" Fwrite " 2527107.44 | 2754336.64
Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
---
drivers/block/zram/zram_comp.c | 15 +++++++++------
drivers/block/zram/zram_comp.h | 2 +-
drivers/block/zram/zram_drv.c | 42 +++++++++++++++++++++++++++++++++++++++++-
drivers/block/zram/zram_drv.h | 2 +-
4 files changed, 52 insertions(+), 9 deletions(-)
diff --git a/drivers/block/zram/zram_comp.c b/drivers/block/zram/zram_comp.c
index 139a468..d9b34ba 100644
--- a/drivers/block/zram/zram_comp.c
+++ b/drivers/block/zram/zram_comp.c
@@ -118,11 +118,12 @@ static struct zram_comp_backend *find_backend(const char *compress)
* allocate new zram_comp and initialize it. return NULL
* if requested algorithm is not supported or in case
* of init error */
-struct zram_comp *zcomp_create(const char *compress)
+struct zram_comp *zcomp_create(const char *compress, int num_wm)
{
struct zram_comp *comp;
struct zram_comp_backend *backend;
struct zcomp_workmem *wm;
+ int i;
backend = find_backend(compress);
if (!backend)
@@ -137,11 +138,13 @@ struct zram_comp *zcomp_create(const char *compress)
INIT_LIST_HEAD(&comp->idle_workmem);
init_waitqueue_head(&comp->workmem_wait);
- wm = workmem_alloc(comp);
- if (!wm) {
- zcomp_destroy(comp);
- return NULL;
+ for (i = 0; i < num_wm; i++) {
+ wm = workmem_alloc(comp);
+ if (!wm) {
+ zcomp_destroy(comp);
+ return NULL;
+ }
+ list_add_tail(&wm->list, &comp->idle_workmem);
}
- list_add_tail(&wm->list, &comp->idle_workmem);
return comp;
}
diff --git a/drivers/block/zram/zram_comp.h b/drivers/block/zram/zram_comp.h
index 90355e8..277afa0 100644
--- a/drivers/block/zram/zram_comp.h
+++ b/drivers/block/zram/zram_comp.h
@@ -43,7 +43,7 @@ struct zram_comp {
struct zram_comp_backend *backend;
};
-struct zram_comp *zcomp_create(const char *comp);
+struct zram_comp *zcomp_create(const char *comp, int num_wm);
void zcomp_destroy(struct zram_comp *comp);
struct zcomp_workmem *zcomp_workmem_get(struct zram_comp *comp);
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 4875a16..a065c1b 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -107,6 +107,41 @@ static ssize_t mem_used_total_show(struct device *dev,
return sprintf(buf, "%llu\n", val);
}
+static ssize_t max_buffers_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ long val;
+ struct zram *zram = dev_to_zram(dev);
+
+ down_read(&zram->init_lock);
+ val = zram->max_buffers;
+ up_read(&zram->init_lock);
+
+ return sprintf(buf, "%ld\n", val);
+}
+
+static ssize_t max_buffers_store(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t len)
+{
+ long num;
+ struct zram *zram = dev_to_zram(dev);
+
+ if (kstrtol(buf, 0, &num))
+ return -EINVAL;
+ if (num < 1 || num > 100)
+ return -EINVAL;
+ down_write(&zram->init_lock);
+ if (init_done(zram)) {
+ up_write(&zram->init_lock);
+ pr_info("Cannot set max buffers for initialized device\n");
+ return -EBUSY;
+ }
+ zram->max_buffers = num;
+ up_write(&zram->init_lock);
+
+ return len;
+}
+
/* flag operations needs meta->tb_lock */
static int zram_test_flag(struct zram_meta *meta, u32 index,
enum zram_pageflags flag)
@@ -505,6 +540,7 @@ static void zram_reset_device(struct zram *zram, bool reset_capacity)
if (zram->comp)
zcomp_destroy(zram->comp);
zram->comp = NULL;
+ zram->max_buffers = 1;
zram_meta_free(zram->meta);
zram->meta = NULL;
@@ -534,7 +570,7 @@ static ssize_t disksize_store(struct device *dev,
return -EBUSY;
}
- zram->comp = zcomp_create(default_compressor);
+ zram->comp = zcomp_create(default_compressor, zram->max_buffers);
if (!zram->comp) {
up_write(&zram->init_lock);
pr_info("Cannot initialise compressing backend\n");
@@ -697,6 +733,8 @@ static DEVICE_ATTR(initstate, S_IRUGO, initstate_show, NULL);
static DEVICE_ATTR(reset, S_IWUSR, NULL, reset_store);
static DEVICE_ATTR(orig_data_size, S_IRUGO, orig_data_size_show, NULL);
static DEVICE_ATTR(mem_used_total, S_IRUGO, mem_used_total_show, NULL);
+static DEVICE_ATTR(max_buffers, S_IRUGO | S_IWUSR,
+ max_buffers_show, max_buffers_store);
ZRAM_ATTR_RO(num_reads);
ZRAM_ATTR_RO(num_writes);
@@ -721,6 +759,7 @@ static struct attribute *zram_disk_attrs[] = {
&dev_attr_orig_data_size.attr,
&dev_attr_compr_data_size.attr,
&dev_attr_mem_used_total.attr,
+ &dev_attr_max_buffers.attr,
NULL,
};
@@ -784,6 +823,7 @@ static int create_device(struct zram *zram, int device_id)
zram->meta = NULL;
zram->comp = NULL;
+ zram->max_buffers = 1;
return 0;
out_free_disk:
diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h
index a2e17d7..68a5421 100644
--- a/drivers/block/zram/zram_drv.h
+++ b/drivers/block/zram/zram_drv.h
@@ -99,7 +99,7 @@ struct zram {
* we can store in a disk.
*/
u64 disksize; /* bytes */
-
+ long max_buffers;
struct zram_stats stats;
};
#endif
--
1.9.0.rc3.244.g3497008
^ permalink raw reply related [flat|nested] 9+ messages in thread
* [PATCHv3 4/4] zram: document max_buffers
2014-02-11 22:21 [PATCHv3 0/4] add compression backend abstraction Sergey Senozhatsky
` (2 preceding siblings ...)
2014-02-11 22:21 ` [PATCHv3 3/4] zram: support multi compressing buffers Sergey Senozhatsky
@ 2014-02-11 22:21 ` Sergey Senozhatsky
3 siblings, 0 replies; 9+ messages in thread
From: Sergey Senozhatsky @ 2014-02-11 22:21 UTC (permalink / raw)
To: Minchan Kim
Cc: Jerome Marchand, Nitin Gupta, linux-kernel, Sergey Senozhatsky
Add max_buffers device attribute documentation.
Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
---
Documentation/ABI/testing/sysfs-block-zram | 9 ++++++++-
Documentation/blockdev/zram.txt | 21 ++++++++++++++++-----
2 files changed, 24 insertions(+), 6 deletions(-)
diff --git a/Documentation/ABI/testing/sysfs-block-zram b/Documentation/ABI/testing/sysfs-block-zram
index 8aa0468..0e36bf7 100644
--- a/Documentation/ABI/testing/sysfs-block-zram
+++ b/Documentation/ABI/testing/sysfs-block-zram
@@ -50,7 +50,6 @@ Description:
The failed_reads file is read-only and specifies the number of
failed reads happened on this device.
-
What: /sys/block/zram<id>/failed_writes
Date: February 2014
Contact: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
@@ -58,6 +57,14 @@ Description:
The failed_writes file is read-only and specifies the number of
failed writes happened on this device.
+What: /sys/block/zram<id>/max_buffers
+Date: February 2014
+Contact: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
+Description:
+ The max_buffers file is read-write and specifies the number of
+ compression backend's workmem buffers (number of concurrent
+ compress/decompress operations).
+
What: /sys/block/zram<id>/notify_free
Date: August 2010
Contact: Nitin Gupta <ngupta@vflare.org>
diff --git a/Documentation/blockdev/zram.txt b/Documentation/blockdev/zram.txt
index b31ac5e..b525c8e 100644
--- a/Documentation/blockdev/zram.txt
+++ b/Documentation/blockdev/zram.txt
@@ -21,7 +21,18 @@ Following shows a typical sequence of steps for using zram.
This creates 4 devices: /dev/zram{0,1,2,3}
(num_devices parameter is optional. Default: 1)
-2) Set Disksize
+2) Set max number of compression buffers
+ Compression backend may use up to max_buffers workmem buffers,
+ thus allowing up to max_buffers concurrent compression/decompression
+ operations.
+ Examples:
+ #set max buffers to 3
+ echo 3 > /sys/block/zram0/max_buffers
+
+ #show max buffers
+ cat /sys/block/zram0/max_buffers
+
+3) Set Disksize
Set disk size by writing the value to sysfs node 'disksize'.
The value can be either in bytes or you can use mem suffixes.
Examples:
@@ -38,14 +49,14 @@ There is little point creating a zram of greater than twice the size of memory
since we expect a 2:1 compression ratio. Note that zram uses about 0.1% of the
size of the disk when not in use so a huge zram is wasteful.
-3) Activate:
+4) Activate:
mkswap /dev/zram0
swapon /dev/zram0
mkfs.ext4 /dev/zram1
mount /dev/zram1 /tmp
-4) Stats:
+5) Stats:
Per-device statistics are exported as various nodes under
/sys/block/zram<id>/
disksize
@@ -60,11 +71,11 @@ size of the disk when not in use so a huge zram is wasteful.
compr_data_size
mem_used_total
-5) Deactivate:
+6) Deactivate:
swapoff /dev/zram0
umount /dev/zram1
-6) Reset:
+7) Reset:
Write any positive value to 'reset' sysfs node
echo 1 > /sys/block/zram0/reset
echo 1 > /sys/block/zram1/reset
--
1.9.0.rc3.244.g3497008
^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCHv3 1/4] zram: introduce compressing backend abstraction
2014-02-11 22:21 ` [PATCHv3 1/4] zram: introduce compressing " Sergey Senozhatsky
@ 2014-02-12 7:06 ` Minchan Kim
2014-02-12 9:02 ` Sergey Senozhatsky
2014-02-12 7:08 ` Minchan Kim
1 sibling, 1 reply; 9+ messages in thread
From: Minchan Kim @ 2014-02-12 7:06 UTC (permalink / raw)
To: Sergey Senozhatsky; +Cc: Jerome Marchand, Nitin Gupta, linux-kernel
Hello Sergey,
On Wed, Feb 12, 2014 at 01:21:03AM +0300, Sergey Senozhatsky wrote:
> ZRAM performs direct LZO compression algorithm calls, making it the one
> and only option. Introduce abstract struct zram_comp in order to support
> multiple compression algorithms. struct zram_comp defines the following
> set of compressing backend operations:
> .create
> .destroy
> .compress
> .decompress
>
> Schematically zram write() usually contains the following steps:
> 0) preparation (decompression of partioal IO, etc.)
> 1) lock buffer_lock mutex (protects meta compress buffers)
> 2) compress (using meta compress buffers)
> 3) alloc and map zs_pool object
> 4) copy compressed data (from meta compress buffers) to object allocated by 3)
> 5) free previous pool page, assign a new one
> 6) unlock buffer_lock mutex
>
> As we can see, compressing buffers must remain untouched from 1) to 4),
> because, otherwise, concurrent write() can overwrite data. At the same
> time, zram_meta must be aware of a) specific compression algorithm
> memory requirements and b) necessary locking to protect compression
> buffers. Besides, zram holds buffer_lock almost through the whole write()
> function, making parallel compression impossible. To remove requirement
> a) new struct zcomp_workmem introduced, which contain buffers required by
> compression algorithm. While struct zram_comp implements workmem handling
> and locking by means of get() and put() semantics and removes requirement
> b) from zram meta. zram_comp ->create() and ->destroy(), respectively,
> allocate and deallocate algorithm specific zcomp_workmem `private' buffer.
>
> Every zram_comp has a list of idle workmem buffers (at least 1 workmem),
> spinlock to protect idle list and wait queue, making it possible to perform
> parallel compressions. Each time zram issues a zcomp_workmem_get() call, the
> following set of operations performed:
> - spin lock buffer_lock
> - if idle list is not empty, remove workmem from idle list, spin
> unlock and return workmem pointer
> - if idle list is empty, current adds itself to wait queue. it will be
> awaken by zcomp_workmem_put() caller.
>
> zcomp_workmem_put():
> - spin lock buffer_lock
> - add workmem to idle list
> - spin unlock, wake up sleeper (if any)
>
> In other words, zcomp_workmem_get() turns caller into exclusive user of workmem
> and zcomp_workem_put() makes a particular workmem available.
>
> Usage examples.
>
> To initialize compressing backend:
> comp = zcomp_create(NAME) /* NAME e.g. lzo */
>
> which initialises compressing backend if requested algorithm is supported.
>
> Compress:
> wm = zcomp_workmem_get(comp)
> zcomp_compress(comp, wm, src, src_len, &dst_len)
> [..] /* copy compressed data */
> zcomp_workmem_put(comp, wm)
>
> Decompress:
> zcomp_decompress(comp, src, src_len, dst, &dst_len);
>
> Free compessing backend and its workmem:
> zcomp_destroy(comp)
>
> Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
> ---
> drivers/block/zram/zcomp_lzo.c | 33 +++++++++
> drivers/block/zram/zram_comp.c | 147 +++++++++++++++++++++++++++++++++++++++++
> drivers/block/zram/zram_comp.h | 58 ++++++++++++++++
> 3 files changed, 238 insertions(+)
> create mode 100644 drivers/block/zram/zcomp_lzo.c
> create mode 100644 drivers/block/zram/zram_comp.c
> create mode 100644 drivers/block/zram/zram_comp.h
>
> diff --git a/drivers/block/zram/zcomp_lzo.c b/drivers/block/zram/zcomp_lzo.c
> new file mode 100644
> index 0000000..bbde74e
> --- /dev/null
> +++ b/drivers/block/zram/zcomp_lzo.c
> @@ -0,0 +1,33 @@
> +/*
> + * Copyright (C) 2014 Sergey Senozhatsky.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/lzo.h>
> +
> +#include "zram_comp.h"
> +
> +static void * lzo_create(void)
> +{
> + return kmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
> +}
> +
> +static void lzo_destroy(void *workmem)
> +{
> + kfree(workmem);
> +}
It's okay to name "workmem" for lzo because lzo needs only work buffer to work
but let's think about zlib. It needs z_streamp which has many feilds
to work as well as work memory. That's why I don't like "workmem" name which
is very specific. Anyway, it's okay for lzo backend now.
> +
> +extern struct zram_comp_backend zcomp_lzo;
> +struct zram_comp_backend zcomp_lzo = {
> + .compress = lzo1x_1_compress,
> + .decompress = lzo1x_decompress_safe,
> + .create = lzo_create,
> + .destroy = lzo_destroy,
> + .name = "lzo",
> +};
> diff --git a/drivers/block/zram/zram_comp.c b/drivers/block/zram/zram_comp.c
> new file mode 100644
> index 0000000..139a468
> --- /dev/null
> +++ b/drivers/block/zram/zram_comp.c
> @@ -0,0 +1,147 @@
> +/*
> + * Copyright (C) 2014 Sergey Senozhatsky.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/string.h>
> +#include <linux/slab.h>
> +#include <linux/vmalloc.h>
> +#include <linux/wait.h>
> +#include <linux/sched.h>
> +
> +#include "zram_comp.h"
> +
> +extern struct zram_comp_backend zcomp_lzo;
> +
> +static void workmem_free(struct zram_comp *comp, struct zcomp_workmem *workmem)
> +{
> + comp->backend->destroy(workmem->private);
> + free_pages((unsigned long)workmem->buffer, 1);
> + kfree(workmem);
> +}
> +
> +/* allocate new workmem structure with ->mem of requested size,
> + * return NULL on error */
> +static struct zcomp_workmem *workmem_alloc(struct zram_comp *comp)
> +{
> + struct zcomp_workmem *workmem = kmalloc(sizeof(*workmem), GFP_NOFS);
> + if (!workmem)
> + return NULL;
> +
> + INIT_LIST_HEAD(&workmem->list);
> + /* algorithm specific working memory buffer */
> + workmem->private = comp->backend->create();
> + /* allocate 2 pages. 1 for compressed data, plus 1 extra for the
> + * case when compressed size is larger than the original one. */
> + workmem->buffer = (void *)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1);
> + if (!workmem->private || !workmem->buffer)
> + goto fail;
> +
> + return workmem;
> +fail:
> + workmem_free(comp, workmem);
> + return NULL;
> +}
Hmm, you introduced workmem again. :(
As I said above, workmem is too specific.
It's only valid for lzo and lz4 so I don't want to expose workmem out of zcomp
and that's why I suggested zcomp_strm or zcomp_stream.
It seems "stream" is more general term for compressor.
> +
> +/* get existing idle workmem or wait until other process release
> + * (workmem_put()) one for us */
> +struct zcomp_workmem *zcomp_workmem_get(struct zram_comp *comp)
> +{
> + struct zcomp_workmem *wm;
> +retry:
> + spin_lock(&comp->buffer_lock);
> + if (list_empty(&comp->idle_workmem)) {
> + spin_unlock(&comp->buffer_lock);
> + wait_event(comp->workmem_wait,
> + !list_empty(&comp->idle_workmem));
> + goto retry;
> + }
> +
> + wm = list_entry(comp->idle_workmem.next,
> + struct zcomp_workmem, list);
> + list_del(&wm->list);
> + spin_unlock(&comp->buffer_lock);
> + return wm;
> +}
> +
> +/* add workmem back to idle list and wake up waiter (if any) */
> +void zcomp_workmem_put(struct zram_comp *comp,
> + struct zcomp_workmem *workmem)
> +{
> + spin_lock(&comp->buffer_lock);
> + list_add_tail(&workmem->list, &comp->idle_workmem);
Why do you implement queue instead of stack?
> + spin_unlock(&comp->buffer_lock);
> +
> + if (waitqueue_active(&comp->workmem_wait))
Possible losting wakeup?
> + wake_up(&comp->workmem_wait);
> +}
> +
> +int zcomp_compress(struct zram_comp *comp, struct zcomp_workmem *workmem,
> + const unsigned char *src, size_t src_len, size_t *dst_len)
> +{
As I said, I don't like zcomp_workmem.
Please use zcomp_strm and let's make naming clear.
Something is zcomp and sometime is zram_comp.
I prefer zcomp but it depends on you.
> + return comp->backend->compress(src, src_len, workmem->buffer,
> + dst_len, workmem->private);
> +}
> +
> +int zcomp_decompress(struct zram_comp *comp, const unsigned char *src,
> + size_t src_len, unsigned char *dst, size_t *dst_len)
> +{
> + return comp->backend->decompress(src, src_len, dst, dst_len);
> +}
> +
> +/* free allocated workmem buffers and zram_comp */
> +void zcomp_destroy(struct zram_comp *comp)
> +{
> + struct zcomp_workmem *wm;
> + while (!list_empty(&comp->idle_workmem)) {
> + wm = list_entry(comp->idle_workmem.next,
> + struct zcomp_workmem, list);
> + list_del(&wm->list);
> + workmem_free(comp, wm);
> + }
> + kfree(comp);
> +}
> +
> +static struct zram_comp_backend *find_backend(const char *compress)
> +{
> + if (sysfs_streq(compress, "lzo"))
> + return &zcomp_lzo;
> + return NULL;
> +}
> +
> +/* search available compressors for requested algorithm.
Please correct comment style.
> + * allocate new zram_comp and initialize it. return NULL
> + * if requested algorithm is not supported or in case
> + * of init error */
> +struct zram_comp *zcomp_create(const char *compress)
> +{
> + struct zram_comp *comp;
> + struct zram_comp_backend *backend;
> + struct zcomp_workmem *wm;
> +
> + backend = find_backend(compress);
> + if (!backend)
> + return NULL;
> +
> + comp = kzalloc(sizeof(struct zram_comp), GFP_KERNEL);
> + if (!comp)
> + return NULL;
> +
> + comp->backend = backend;
> + spin_lock_init(&comp->buffer_lock);
> + INIT_LIST_HEAD(&comp->idle_workmem);
> + init_waitqueue_head(&comp->workmem_wait);
> +
> + wm = workmem_alloc(comp);
> + if (!wm) {
> + zcomp_destroy(comp);
> + return NULL;
> + }
> + list_add_tail(&wm->list, &comp->idle_workmem);
> + return comp;
> +}
> diff --git a/drivers/block/zram/zram_comp.h b/drivers/block/zram/zram_comp.h
> new file mode 100644
> index 0000000..90355e8
> --- /dev/null
> +++ b/drivers/block/zram/zram_comp.h
> @@ -0,0 +1,58 @@
> +/*
> + * Copyright (C) 2014 Sergey Senozhatsky.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
> + */
> +
> +#ifndef _ZRAM_COMP_H_
> +#define _ZRAM_COMP_H_
> +
> +#include <linux/types.h>
> +#include <linux/spinlock.h>
> +
> +struct zcomp_workmem {
> + void *buffer; /* compression/decompression buffer */
> + void *private; /* algorithm workmem */
> + struct list_head list;
> +};
> +
> +/* static compression backend */
> +struct zram_comp_backend {
> + int (*compress)(const unsigned char *src, size_t src_len,
> + unsigned char *dst, size_t *dst_len, void *wrkmem);
zram works based on PAGE so I like compress_page so maybe we couldn't
reduce a argument "src_len".
> +
> + int (*decompress)(const unsigned char *src, size_t src_len,
> + unsigned char *dst, size_t *dst_len);
> +
> + void *(*create)(void);
> + void (*destroy)(void *workmem);
> +
> + const char *name;
> +};
> +
> +/* dynamic per-device compression frontend */
> +struct zram_comp {
> + /* protect workmem list */
> + spinlock_t buffer_lock;
> + /* list of available workmems */
> + struct list_head idle_workmem;
> + wait_queue_head_t workmem_wait;
> + struct zram_comp_backend *backend;
> +};
> +
> +struct zram_comp *zcomp_create(const char *comp);
> +void zcomp_destroy(struct zram_comp *comp);
> +
> +struct zcomp_workmem *zcomp_workmem_get(struct zram_comp *comp);
> +void zcomp_workmem_put(struct zram_comp *comp,
> + struct zcomp_workmem *workmem);
> +
> +int zcomp_compress(struct zram_comp *comp, struct zcomp_workmem *workmem,
> + const unsigned char *src, size_t src_len, size_t *dst_len);
> +
> +int zcomp_decompress(struct zram_comp *comp, const unsigned char *src,
> + size_t src_len, unsigned char *dst, size_t *dst_len);
> +#endif /* _ZRAM_COMP_H_ */
> --
> 1.9.0.rc3.244.g3497008
>
> --
> 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/
--
Kind regards,
Minchan Kim
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCHv3 1/4] zram: introduce compressing backend abstraction
2014-02-11 22:21 ` [PATCHv3 1/4] zram: introduce compressing " Sergey Senozhatsky
2014-02-12 7:06 ` Minchan Kim
@ 2014-02-12 7:08 ` Minchan Kim
2014-02-12 8:38 ` Sergey Senozhatsky
1 sibling, 1 reply; 9+ messages in thread
From: Minchan Kim @ 2014-02-12 7:08 UTC (permalink / raw)
To: Sergey Senozhatsky; +Cc: Jerome Marchand, Nitin Gupta, linux-kernel
On Wed, Feb 12, 2014 at 01:21:03AM +0300, Sergey Senozhatsky wrote:
> ZRAM performs direct LZO compression algorithm calls, making it the one
> and only option. Introduce abstract struct zram_comp in order to support
> multiple compression algorithms. struct zram_comp defines the following
> set of compressing backend operations:
> .create
> .destroy
> .compress
> .decompress
>
> Schematically zram write() usually contains the following steps:
> 0) preparation (decompression of partioal IO, etc.)
> 1) lock buffer_lock mutex (protects meta compress buffers)
> 2) compress (using meta compress buffers)
> 3) alloc and map zs_pool object
> 4) copy compressed data (from meta compress buffers) to object allocated by 3)
> 5) free previous pool page, assign a new one
> 6) unlock buffer_lock mutex
>
> As we can see, compressing buffers must remain untouched from 1) to 4),
> because, otherwise, concurrent write() can overwrite data. At the same
> time, zram_meta must be aware of a) specific compression algorithm
> memory requirements and b) necessary locking to protect compression
> buffers. Besides, zram holds buffer_lock almost through the whole write()
> function, making parallel compression impossible. To remove requirement
> a) new struct zcomp_workmem introduced, which contain buffers required by
> compression algorithm. While struct zram_comp implements workmem handling
> and locking by means of get() and put() semantics and removes requirement
> b) from zram meta. zram_comp ->create() and ->destroy(), respectively,
> allocate and deallocate algorithm specific zcomp_workmem `private' buffer.
>
> Every zram_comp has a list of idle workmem buffers (at least 1 workmem),
> spinlock to protect idle list and wait queue, making it possible to perform
> parallel compressions. Each time zram issues a zcomp_workmem_get() call, the
> following set of operations performed:
> - spin lock buffer_lock
> - if idle list is not empty, remove workmem from idle list, spin
> unlock and return workmem pointer
> - if idle list is empty, current adds itself to wait queue. it will be
> awaken by zcomp_workmem_put() caller.
>
> zcomp_workmem_put():
> - spin lock buffer_lock
> - add workmem to idle list
> - spin unlock, wake up sleeper (if any)
>
> In other words, zcomp_workmem_get() turns caller into exclusive user of workmem
> and zcomp_workem_put() makes a particular workmem available.
>
> Usage examples.
>
> To initialize compressing backend:
> comp = zcomp_create(NAME) /* NAME e.g. lzo */
>
> which initialises compressing backend if requested algorithm is supported.
>
> Compress:
> wm = zcomp_workmem_get(comp)
> zcomp_compress(comp, wm, src, src_len, &dst_len)
> [..] /* copy compressed data */
> zcomp_workmem_put(comp, wm)
>
> Decompress:
> zcomp_decompress(comp, src, src_len, dst, &dst_len);
>
> Free compessing backend and its workmem:
> zcomp_destroy(comp)
>
> Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
> ---
> drivers/block/zram/zcomp_lzo.c | 33 +++++++++
> drivers/block/zram/zram_comp.c | 147 +++++++++++++++++++++++++++++++++++++++++
> drivers/block/zram/zram_comp.h | 58 ++++++++++++++++
> 3 files changed, 238 insertions(+)
> create mode 100644 drivers/block/zram/zcomp_lzo.c
> create mode 100644 drivers/block/zram/zram_comp.c
> create mode 100644 drivers/block/zram/zram_comp.h
>
> diff --git a/drivers/block/zram/zcomp_lzo.c b/drivers/block/zram/zcomp_lzo.c
> new file mode 100644
> index 0000000..bbde74e
> --- /dev/null
> +++ b/drivers/block/zram/zcomp_lzo.c
> @@ -0,0 +1,33 @@
> +/*
> + * Copyright (C) 2014 Sergey Senozhatsky.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/slab.h>
> +#include <linux/lzo.h>
> +
> +#include "zram_comp.h"
> +
> +static void * lzo_create(void)
> +{
> + return kmalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL);
> +}
> +
> +static void lzo_destroy(void *workmem)
> +{
> + kfree(workmem);
> +}
> +
> +extern struct zram_comp_backend zcomp_lzo;
> +struct zram_comp_backend zcomp_lzo = {
> + .compress = lzo1x_1_compress,
> + .decompress = lzo1x_decompress_safe,
> + .create = lzo_create,
> + .destroy = lzo_destroy,
> + .name = "lzo",
> +};
> diff --git a/drivers/block/zram/zram_comp.c b/drivers/block/zram/zram_comp.c
> new file mode 100644
> index 0000000..139a468
> --- /dev/null
> +++ b/drivers/block/zram/zram_comp.c
> @@ -0,0 +1,147 @@
> +/*
> + * Copyright (C) 2014 Sergey Senozhatsky.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public License
> + * as published by the Free Software Foundation; either version
> + * 2 of the License, or (at your option) any later version.
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/string.h>
> +#include <linux/slab.h>
> +#include <linux/vmalloc.h>
> +#include <linux/wait.h>
> +#include <linux/sched.h>
> +
> +#include "zram_comp.h"
> +
> +extern struct zram_comp_backend zcomp_lzo;
> +
> +static void workmem_free(struct zram_comp *comp, struct zcomp_workmem *workmem)
> +{
> + comp->backend->destroy(workmem->private);
> + free_pages((unsigned long)workmem->buffer, 1);
> + kfree(workmem);
> +}
> +
> +/* allocate new workmem structure with ->mem of requested size,
> + * return NULL on error */
> +static struct zcomp_workmem *workmem_alloc(struct zram_comp *comp)
> +{
> + struct zcomp_workmem *workmem = kmalloc(sizeof(*workmem), GFP_NOFS);
One more thing, pz, say why we need GFP_NOFS in here.
--
Kind regards,
Minchan Kim
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCHv3 1/4] zram: introduce compressing backend abstraction
2014-02-12 7:08 ` Minchan Kim
@ 2014-02-12 8:38 ` Sergey Senozhatsky
0 siblings, 0 replies; 9+ messages in thread
From: Sergey Senozhatsky @ 2014-02-12 8:38 UTC (permalink / raw)
To: Minchan Kim; +Cc: Jerome Marchand, Nitin Gupta, linux-kernel
On (02/12/14 16:08), Minchan Kim wrote:
[..]
> > +#include <linux/kernel.h>
> > +#include <linux/string.h>
> > +#include <linux/slab.h>
> > +#include <linux/vmalloc.h>
> > +#include <linux/wait.h>
> > +#include <linux/sched.h>
> > +
> > +#include "zram_comp.h"
> > +
> > +extern struct zram_comp_backend zcomp_lzo;
> > +
> > +static void workmem_free(struct zram_comp *comp, struct zcomp_workmem *workmem)
> > +{
> > + comp->backend->destroy(workmem->private);
> > + free_pages((unsigned long)workmem->buffer, 1);
> > + kfree(workmem);
> > +}
> > +
> > +/* allocate new workmem structure with ->mem of requested size,
> > + * return NULL on error */
> > +static struct zcomp_workmem *workmem_alloc(struct zram_comp *comp)
> > +{
> > + struct zcomp_workmem *workmem = kmalloc(sizeof(*workmem), GFP_NOFS);
>
>
> One more thing, pz, say why we need GFP_NOFS in here.
>
my bad. it definitely should be GFP_KERNEL.
-ss
^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCHv3 1/4] zram: introduce compressing backend abstraction
2014-02-12 7:06 ` Minchan Kim
@ 2014-02-12 9:02 ` Sergey Senozhatsky
0 siblings, 0 replies; 9+ messages in thread
From: Sergey Senozhatsky @ 2014-02-12 9:02 UTC (permalink / raw)
To: Minchan Kim; +Cc: Jerome Marchand, Nitin Gupta, linux-kernel
On (02/12/14 16:06), Minchan Kim wrote:
[..]
> > +{
> > + kfree(workmem);
> > +}
>
> It's okay to name "workmem" for lzo because lzo needs only work buffer to work
> but let's think about zlib. It needs z_streamp which has many feilds
> to work as well as work memory. That's why I don't like "workmem" name which
> is very specific. Anyway, it's okay for lzo backend now.
>
ok. will use zcomp_strm
[..]
> > + return workmem;
> > +fail:
> > + workmem_free(comp, workmem);
> > + return NULL;
> > +}
>
> Hmm, you introduced workmem again. :(
> As I said above, workmem is too specific.
> It's only valid for lzo and lz4 so I don't want to expose workmem out of zcomp
> and that's why I suggested zcomp_strm or zcomp_stream.
> It seems "stream" is more general term for compressor.
>
> > +/* add workmem back to idle list and wake up waiter (if any) */
> > +void zcomp_workmem_put(struct zram_comp *comp,
> > + struct zcomp_workmem *workmem)
> > +{
> > + spin_lock(&comp->buffer_lock);
> > + list_add_tail(&workmem->list, &comp->idle_workmem);
>
> Why do you implement queue instead of stack?
>
zcomp gets the first element from a list and never iterates it,
so it's O(1).
kfifo with copying and write memory barriers... I don't think
it will be a better choice here. and we don't have `first in,
first out' pattern, zcomp does not care about order.
> > + spin_unlock(&comp->buffer_lock);
> > +
> > + if (waitqueue_active(&comp->workmem_wait))
>
> Possible losting wakeup?
hm, could be.
> > + wake_up(&comp->workmem_wait);
> > +}
> > +
> > +int zcomp_compress(struct zram_comp *comp, struct zcomp_workmem *workmem,
> > + const unsigned char *src, size_t src_len, size_t *dst_len)
> > +{
>
> As I said, I don't like zcomp_workmem.
> Please use zcomp_strm and let's make naming clear.
> Something is zcomp and sometime is zram_comp.
> I prefer zcomp but it depends on you.
ok. I drop zram part. zcomp and zcomp_strm
> > + return comp->backend->compress(src, src_len, workmem->buffer,
> > + dst_len, workmem->private);
> > +}
> > +
> > +int zcomp_decompress(struct zram_comp *comp, const unsigned char *src,
> > + size_t src_len, unsigned char *dst, size_t *dst_len)
> > +{
> > + return comp->backend->decompress(src, src_len, dst, dst_len);
> > +}
> > +
> > +/* free allocated workmem buffers and zram_comp */
> > +void zcomp_destroy(struct zram_comp *comp)
> > +{
> > + struct zcomp_workmem *wm;
> > + while (!list_empty(&comp->idle_workmem)) {
> > + wm = list_entry(comp->idle_workmem.next,
> > + struct zcomp_workmem, list);
> > + list_del(&wm->list);
> > + workmem_free(comp, wm);
> > + }
> > + kfree(comp);
> > +}
> > +
> > +static struct zram_comp_backend *find_backend(const char *compress)
> > +{
> > + if (sysfs_streq(compress, "lzo"))
> > + return &zcomp_lzo;
> > + return NULL;
> > +}
> > +
> > +/* search available compressors for requested algorithm.
>
> Please correct comment style.
I'll take a look (checkpatch didn't complain).
> > +/* static compression backend */
> > +struct zram_comp_backend {
> > + int (*compress)(const unsigned char *src, size_t src_len,
> > + unsigned char *dst, size_t *dst_len, void *wrkmem);
>
> zram works based on PAGE so I like compress_page so maybe we couldn't
> reduce a argument "src_len".
>
yes and no.
zram can see partial IOs
static inline int is_partial_io(struct bio_vec *bvec)
{
return bvec->bv_len != PAGE_SIZE;
}
so, in general yes -- we can drop src_len and compress PAGE_SIZE bytes,
but at the same time `bvec->bv_len < PAGE_SIZE' is possible (if it's not
then we can greatly simplify zram).
-ss
> > +
> > + int (*decompress)(const unsigned char *src, size_t src_len,
> > + unsigned char *dst, size_t *dst_len);
> > +
> > + void *(*create)(void);
> > + void (*destroy)(void *workmem);
> > +
> > + const char *name;
> > +};
> > +
> > +/* dynamic per-device compression frontend */
> > +struct zram_comp {
> > + /* protect workmem list */
> > + spinlock_t buffer_lock;
> > + /* list of available workmems */
> > + struct list_head idle_workmem;
> > + wait_queue_head_t workmem_wait;
> > + struct zram_comp_backend *backend;
> > +};
> > +
> > +struct zram_comp *zcomp_create(const char *comp);
> > +void zcomp_destroy(struct zram_comp *comp);
> > +
> > +struct zcomp_workmem *zcomp_workmem_get(struct zram_comp *comp);
> > +void zcomp_workmem_put(struct zram_comp *comp,
> > + struct zcomp_workmem *workmem);
> > +
> > +int zcomp_compress(struct zram_comp *comp, struct zcomp_workmem *workmem,
> > + const unsigned char *src, size_t src_len, size_t *dst_len);
> > +
> > +int zcomp_decompress(struct zram_comp *comp, const unsigned char *src,
> > + size_t src_len, unsigned char *dst, size_t *dst_len);
> > +#endif /* _ZRAM_COMP_H_ */
> > --
> > 1.9.0.rc3.244.g3497008
> >
> > --
> > 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/
>
> --
> Kind regards,
> Minchan Kim
>
^ permalink raw reply [flat|nested] 9+ messages in thread
end of thread, other threads:[~2014-02-12 9:06 UTC | newest]
Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-02-11 22:21 [PATCHv3 0/4] add compression backend abstraction Sergey Senozhatsky
2014-02-11 22:21 ` [PATCHv3 1/4] zram: introduce compressing " Sergey Senozhatsky
2014-02-12 7:06 ` Minchan Kim
2014-02-12 9:02 ` Sergey Senozhatsky
2014-02-12 7:08 ` Minchan Kim
2014-02-12 8:38 ` Sergey Senozhatsky
2014-02-11 22:21 ` [PATCHv3 2/4] zram: use zram_comp compressing backends Sergey Senozhatsky
2014-02-11 22:21 ` [PATCHv3 3/4] zram: support multi compressing buffers Sergey Senozhatsky
2014-02-11 22:21 ` [PATCHv3 4/4] zram: document max_buffers Sergey Senozhatsky
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).