* [PATCHv2 0/2] add compression backend abstraction @ 2014-01-30 19:28 Sergey Senozhatsky 2014-01-30 19:28 ` [PATCHv2 1/2] zram: introduce compressing " Sergey Senozhatsky 2014-01-30 19:28 ` [PATCHv2 2/2] zram: use zram_comp compressing backends Sergey Senozhatsky 0 siblings, 2 replies; 5+ messages in thread From: Sergey Senozhatsky @ 2014-01-30 19:28 UTC (permalink / raw) To: Minchan Kim Cc: Jerome Marchand, Nitin Gupta, linux-kernel, Sergey Senozhatsky This patchset add abstraction layer (zram_comp) to avoid direct LZO calls and use a common compression backend interface instead. Patchset introduces LZ4 support and new device attribute to switch used compression algorithm. Sergey Senozhatsky (2): zram: introduce compressing backend abstraction zram: use zram_comp compressing backends drivers/block/zram/Kconfig | 20 +++- drivers/block/zram/Makefile | 6 +- drivers/block/zram/zcomp_lz4.c | 49 ++++++++++ drivers/block/zram/zcomp_lz4.h | 18 ++++ drivers/block/zram/zcomp_lzo.c | 49 ++++++++++ drivers/block/zram/zcomp_lzo.h | 18 ++++ drivers/block/zram/zram_comp.c | 204 +++++++++++++++++++++++++++++++++++++++++ drivers/block/zram/zram_comp.h | 64 +++++++++++++ drivers/block/zram/zram_drv.c | 96 ++++++++++++------- drivers/block/zram/zram_drv.h | 8 +- 10 files changed, 492 insertions(+), 40 deletions(-) create mode 100644 drivers/block/zram/zcomp_lz4.c create mode 100644 drivers/block/zram/zcomp_lz4.h create mode 100644 drivers/block/zram/zcomp_lzo.c create mode 100644 drivers/block/zram/zcomp_lzo.h create mode 100644 drivers/block/zram/zram_comp.c create mode 100644 drivers/block/zram/zram_comp.h -- 1.9.rc1.183.g614c158 ^ permalink raw reply [flat|nested] 5+ messages in thread
* [PATCHv2 1/2] zram: introduce compressing backend abstraction 2014-01-30 19:28 [PATCHv2 0/2] add compression backend abstraction Sergey Senozhatsky @ 2014-01-30 19:28 ` Sergey Senozhatsky 2014-02-06 5:16 ` Minchan Kim 2014-01-30 19:28 ` [PATCHv2 2/2] zram: use zram_comp compressing backends Sergey Senozhatsky 1 sibling, 1 reply; 5+ messages in thread From: Sergey Senozhatsky @ 2014-01-30 19:28 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 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 .workmem_get .workmem_put 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 new object 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() will 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 (workmem is a common term used by lzo, lz4 and zlib) contains buffers required by compression algorithm, while new struct zcomp_wm_policy implements workmem handling and locking by means of get() and put() semantics and removes requirement b) from zram meta. In general, workmem_get() turns caller into exclusive user of workmem and workem_put() makes a particular workmem available. Each compressing backend may use a default workmem policy or provide custom implementation. Default workmem policy (struct zcomp_wm_policy) has a list of idle workmem buffers (at least 1 workmem), spinlock to protect idle list and wait queue, making it possible to have parallel compressions. Each time zram issues a 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 workmem_put() caller. workmem_put(): - spin lock buffer_lock - add workmem to idle list - spin unlock, wake up sleeper (if any) zram_comp file contains array of supported compressing backends, which can be altered according to user selection. Usage examples. To initialize compressing backend: comp = zcomp_create(NAME) /* NAME e.g. lzo, lz4 */ which performs NAME lookup in array of supported compressing backends and initialises compressing backend if requested algorithm is supported. Compressing: wm = comp->workmem_get(comp) comp->compress(...) [..] /* copy compressed data */ comp->workmem_put(comp, wm) Free compessing backend and all ot its workmem buffers: zcomp_destroy(comp) The patch implements LZO and LZ4 backends. At this point zcomp_wm_policy keeps only one workmem in the idle list, support for multi workmem buffers will be introduced later. Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com> --- drivers/block/zram/zcomp_lz4.c | 49 ++++++++++ drivers/block/zram/zcomp_lz4.h | 18 ++++ drivers/block/zram/zcomp_lzo.c | 49 ++++++++++ drivers/block/zram/zcomp_lzo.h | 18 ++++ drivers/block/zram/zram_comp.c | 204 +++++++++++++++++++++++++++++++++++++++++ drivers/block/zram/zram_comp.h | 64 +++++++++++++ 6 files changed, 402 insertions(+) create mode 100644 drivers/block/zram/zcomp_lz4.c create mode 100644 drivers/block/zram/zcomp_lz4.h create mode 100644 drivers/block/zram/zcomp_lzo.c create mode 100644 drivers/block/zram/zcomp_lzo.h 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_lz4.c b/drivers/block/zram/zcomp_lz4.c new file mode 100644 index 0000000..0987e70 --- /dev/null +++ b/drivers/block/zram/zcomp_lz4.c @@ -0,0 +1,49 @@ +/* + * 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/lz4.h> + +#include "zcomp_lz4.h" + +static struct zcomp_workmem *workmem_get(struct zram_comp *comp) +{ + struct zcomp_wm_policy *policy = comp->private; + return wm_policy_workmem_get(policy); +} + +static void workmem_put(struct zram_comp *comp, struct zcomp_workmem *workmem) +{ + struct zcomp_wm_policy *policy = comp->private; + return wm_policy_workmem_put(policy, workmem); +} + +int zcomp_lz4_create(struct zram_comp *comp) +{ + comp->compress = lz4_compress; + comp->decompress = lz4_decompress_unknownoutputsize; + comp->workmem_get = workmem_get; + comp->workmem_put = workmem_put; + + /* lz4 backend uses default wm policy and calls default policy + * workmem get() and put() functions */ + comp->private = kzalloc(sizeof(struct zcomp_wm_policy), GFP_KERNEL); + if (!comp->private) + return -ENOMEM; + if (wm_policy_init(comp->private, LZ4_MEM_COMPRESS)) + return -EINVAL; + return 0; +} + +void zcomp_lz4_destroy(struct zram_comp *comp) +{ + wm_policy_free(comp->private); + kfree(comp->private); +} diff --git a/drivers/block/zram/zcomp_lz4.h b/drivers/block/zram/zcomp_lz4.h new file mode 100644 index 0000000..7988492 --- /dev/null +++ b/drivers/block/zram/zcomp_lz4.h @@ -0,0 +1,18 @@ +/* + * 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 _ZCOMP_LZ4_H_ +#define _ZCOMP_LZ4_H_ + +#include "zram_comp.h" + +int zcomp_lz4_create(struct zram_comp *comp); +void zcomp_lz4_destroy(struct zram_comp *comp); + +#endif /* _ZCOMP_LZ4_H_ */ diff --git a/drivers/block/zram/zcomp_lzo.c b/drivers/block/zram/zcomp_lzo.c new file mode 100644 index 0000000..b2f46d1 --- /dev/null +++ b/drivers/block/zram/zcomp_lzo.c @@ -0,0 +1,49 @@ +/* + * 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 "zcomp_lzo.h" + +static struct zcomp_workmem *workmem_get(struct zram_comp *comp) +{ + struct zcomp_wm_policy *policy = comp->private; + return wm_policy_workmem_get(policy); +} + +static void workmem_put(struct zram_comp *comp, struct zcomp_workmem *workmem) +{ + struct zcomp_wm_policy *policy = comp->private; + return wm_policy_workmem_put(policy, workmem); +} + +int zcomp_lzo_create(struct zram_comp *comp) +{ + comp->compress = lzo1x_1_compress; + comp->decompress = lzo1x_decompress_safe; + comp->workmem_get = workmem_get; + comp->workmem_put = workmem_put; + + /* lzo backend uses default wm policy and calls default policy + * workmem get() and put() functions */ + comp->private = kzalloc(sizeof(struct zcomp_wm_policy), GFP_KERNEL); + if (!comp->private) + return -ENOMEM; + if (wm_policy_init(comp->private, LZO1X_MEM_COMPRESS)) + return -EINVAL; + return 0; +} + +void zcomp_lzo_destroy(struct zram_comp *comp) +{ + wm_policy_free(comp->private); + kfree(comp->private); +} diff --git a/drivers/block/zram/zcomp_lzo.h b/drivers/block/zram/zcomp_lzo.h new file mode 100644 index 0000000..18c174e --- /dev/null +++ b/drivers/block/zram/zcomp_lzo.h @@ -0,0 +1,18 @@ +/* + * 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 _ZCOMP_LZO_H_ +#define _ZCOMP_LZO_H_ + +#include "zram_comp.h" + +int zcomp_lzo_create(struct zram_comp *comp); +void zcomp_lzo_destroy(struct zram_comp *comp); + +#endif /* _ZCOMP_LZO_H_ */ diff --git a/drivers/block/zram/zram_comp.c b/drivers/block/zram/zram_comp.c new file mode 100644 index 0000000..f751394 --- /dev/null +++ b/drivers/block/zram/zram_comp.c @@ -0,0 +1,204 @@ +/* + * 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" + +#ifdef CONFIG_ZRAM_LZO_COMPRESS +#include "zcomp_lzo.h" +#endif + +#ifdef CONFIG_ZRAM_LZ4_COMPRESS +#include "zcomp_lz4.h" +#endif + +struct zcomp { + const char *name; + int (*create)(struct zram_comp *comp); + void (*destroy)(struct zram_comp *comp); +}; + +static struct zcomp compressors[] = { +#ifdef CONFIG_ZRAM_LZO_COMPRESS + { + .name = "lzo", + .create = zcomp_lzo_create, + .destroy = zcomp_lzo_destroy + }, +#endif +#ifdef CONFIG_ZRAM_LZ4_COMPRESS + { + .name = "lz4", + .create = zcomp_lz4_create, + .destroy = zcomp_lz4_destroy + }, +#endif + {} +}; + +static void workmem_free(struct zcomp_workmem *workmem) +{ + vfree(workmem->buf); + vfree(workmem->mem); + kfree(workmem); +} + +/* allocate new workmem structure with ->mem of requested size, + * return NULL on error */ +static struct zcomp_workmem *workmem_alloc(size_t sz) +{ + struct zcomp_workmem *workmem = kmalloc(sizeof(*workmem), GFP_NOFS); + if (!workmem) + return NULL; + + INIT_LIST_HEAD(&workmem->list); + /* algorithm specific working memory buffer */ + workmem->mem = vmalloc(sz); + /* allocate 2 pages. 1 for compressed data, plus 1 extra for the + * case when compressed size is larger than the original one. */ + workmem->buf = vmalloc(2 * PAGE_SIZE); + if (!workmem->mem || !workmem->buf) + goto fail; + + return workmem; +fail: + workmem_free(workmem); + return NULL; +} + +/* get existing idle workmem or wait until other process release + * (workmem_put()) one for us */ +struct zcomp_workmem *wm_policy_workmem_get(struct zcomp_wm_policy *policy) +{ + struct zcomp_workmem *wm; +retry: + spin_lock(&policy->buffer_lock); + if (!list_empty(&policy->idle_workmem)) { + wm = list_entry(policy->idle_workmem.next, + struct zcomp_workmem, list); + list_del(&wm->list); + spin_unlock(&policy->buffer_lock); + return wm; + } else { + DEFINE_WAIT(wait); + + spin_unlock(&policy->buffer_lock); + prepare_to_wait_exclusive(&policy->workmem_wait, &wait, + TASK_UNINTERRUPTIBLE); + if (list_empty(&policy->idle_workmem)) + schedule(); + finish_wait(&policy->workmem_wait, &wait); + goto retry; + } + /* should never happen */ + return NULL; +} + +/* add workmem back to idle list and wake up waiter (if any) */ +void wm_policy_workmem_put(struct zcomp_wm_policy *policy, + struct zcomp_workmem *workmem) +{ + spin_lock(&policy->buffer_lock); + list_add_tail(&workmem->list, &policy->idle_workmem); + spin_unlock(&policy->buffer_lock); + + if (waitqueue_active(&policy->workmem_wait)) + wake_up(&policy->workmem_wait); +} + +int wm_policy_init(struct zcomp_wm_policy *policy, size_t sz) +{ + struct zcomp_workmem *wm; + + spin_lock_init(&policy->buffer_lock); + INIT_LIST_HEAD(&policy->idle_workmem); + init_waitqueue_head(&policy->workmem_wait); + + /* allocate at least one workmem during initialisation, + * so zram write() will not get into trouble in case of + * low memory */ + wm = workmem_alloc(sz); + if (!wm) + return -EINVAL; + list_add_tail(&wm->list, &policy->idle_workmem); + return 0; +} + +void wm_policy_free(struct zcomp_wm_policy *policy) +{ + struct zcomp_workmem *wm; + while (!list_empty(&policy->idle_workmem)) { + wm = list_entry(policy->idle_workmem.next, + struct zcomp_workmem, list); + list_del(&wm->list); + workmem_free(wm); + } +} + +/* free allocated workmem buffers and zram_comp */ +void zcomp_destroy(struct zram_comp *comp) +{ + comp->destroy(comp); + kfree(comp); +} + +/* 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; + int i; + + BUILD_BUG_ON(ARRAY_SIZE(compressors) == 1); + + for (i = 0; i < ARRAY_SIZE(compressors) - 1; i++) { + if (sysfs_streq(compress, compressors[i].name)) + break; + } + /* nothing found */ + if (i == ARRAY_SIZE(compressors) - 1) + return NULL; + + comp = kzalloc(sizeof(struct zram_comp), GFP_KERNEL); + if (!comp) + return NULL; + + comp->name = compressors[i].name; + comp->create = compressors[i].create; + comp->destroy = compressors[i].destroy; + + if (comp->create(comp)) { + zcomp_destroy(comp); + return NULL; + } + return comp; +} + +/* show available compressors */ +ssize_t zcomp_available_show(struct zram_comp *comp, char *buf) +{ + ssize_t sz = 0; + int i; + for (i = 0; i < ARRAY_SIZE(compressors) - 1; i++) { + if (comp && !strcmp(comp->name, compressors[i].name)) + sz += sprintf(buf + sz, "<%s> ", compressors[i].name); + else + sz += sprintf(buf + sz, "%s ", compressors[i].name); + } + sz += sprintf(buf + sz, "\n"); + return sz; +} diff --git a/drivers/block/zram/zram_comp.h b/drivers/block/zram/zram_comp.h new file mode 100644 index 0000000..3446a63 --- /dev/null +++ b/drivers/block/zram/zram_comp.h @@ -0,0 +1,64 @@ +/* + * 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 *mem; /* algorithm workmem */ + void *buf; /* compression/decompression buffer */ + struct list_head list; +}; + +/* default device's compressing backend workmem control + * policy (usually device ->private)*/ +struct zcomp_wm_policy { + /* protect workmem list */ + spinlock_t buffer_lock; + /* list of available workmems */ + struct list_head idle_workmem; + wait_queue_head_t workmem_wait; +}; + +/* workmem get() and put() for default zcomp_wm_policy. compressing backend + * may define its own wm policy and call custom get() and put() */ +struct zcomp_workmem *wm_policy_workmem_get(struct zcomp_wm_policy *policy); +void wm_policy_workmem_put(struct zcomp_wm_policy *policy, + struct zcomp_workmem *workmem); + +int wm_policy_init(struct zcomp_wm_policy *policy, size_t sz); +void wm_policy_free(struct zcomp_wm_policy *policy); + +/* per-device compression frontend */ +struct zram_comp { + 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); + + struct zcomp_workmem *(*workmem_get)(struct zram_comp *comp); + void (*workmem_put)(struct zram_comp *comp, + struct zcomp_workmem *workmem); + + int (*create)(struct zram_comp *); + void (*destroy)(struct zram_comp *); + + void *private; + const char *name; +}; + +struct zram_comp *zcomp_create(const char *comp); +void zcomp_destroy(struct zram_comp *comp); + +ssize_t zcomp_available_show(struct zram_comp *comp, char *buf); +#endif /* _ZRAM_COMP_H_ */ -- 1.9.rc1.183.g614c158 ^ permalink raw reply related [flat|nested] 5+ messages in thread
* Re: [PATCHv2 1/2] zram: introduce compressing backend abstraction 2014-01-30 19:28 ` [PATCHv2 1/2] zram: introduce compressing " Sergey Senozhatsky @ 2014-02-06 5:16 ` Minchan Kim 2014-02-07 2:02 ` Minchan Kim 0 siblings, 1 reply; 5+ messages in thread From: Minchan Kim @ 2014-02-06 5:16 UTC (permalink / raw) To: Sergey Senozhatsky; +Cc: Jerome Marchand, Nitin Gupta, linux-kernel Hello Sergey, Sorry for late response. On Thu, Jan 30, 2014 at 10:28:07PM +0300, Sergey Senozhatsky wrote: > ZRAM performs direct LZO compression algorithm calls, making it the one > and only option. Introduce 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 > .workmem_get > .workmem_put > > 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 new object 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() will 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 (workmem is a common term used by lzo, lz4 > and zlib) contains buffers required by compression algorithm, while new > struct zcomp_wm_policy implements workmem handling and locking by means > of get() and put() semantics and removes requirement b) from zram meta. > In general, workmem_get() turns caller into exclusive user of workmem > and workem_put() makes a particular workmem available. > > Each compressing backend may use a default workmem policy or provide > custom implementation. Default workmem policy (struct zcomp_wm_policy) > has a list of idle workmem buffers (at least 1 workmem), spinlock to > protect idle list and wait queue, making it possible to have parallel > compressions. Each time zram issues a workmem_get() call, the following > set of operations performed: I'm still really not sure why backend should know workmem policy. I think it's matter of upper layer, not backend. Yeb, surely, you have a reason but it's very hard for me to know it by this patchset so I'd like to divide the patchset. (You don't need to explain it in here and I expect it would be clear if you separate it like I suggested below). Pz, see below. > - 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 workmem_put() caller. > > workmem_put(): > - spin lock buffer_lock > - add workmem to idle list > - spin unlock, wake up sleeper (if any) Good. > > zram_comp file contains array of supported compressing backends, which > can be altered according to user selection. > > Usage examples. To initialize compressing backend: > comp = zcomp_create(NAME) /* NAME e.g. lzo, lz4 */ > > which performs NAME lookup in array of supported compressing backends > and initialises compressing backend if requested algorithm is supported. > Compressing: > wm = comp->workmem_get(comp) > comp->compress(...) > [..] /* copy compressed data */ > comp->workmem_put(comp, wm) > > Free compessing backend and all ot its workmem buffers: > zcomp_destroy(comp) > > The patch implements LZO and LZ4 backends. At this point zcomp_wm_policy > keeps only one workmem in the idle list, support for multi workmem buffers > will be introduced later. > > Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com> > --- > drivers/block/zram/zcomp_lz4.c | 49 ++++++++++ > drivers/block/zram/zcomp_lz4.h | 18 ++++ Please don't include lz4 in this patch. It should be separated and description of the patch surely should include the number to prove lz4 is better than lzo in *what* workload so it should make everybody easy to convince. And let's separate this patchset following as 1. abstract compressor with zram_comp. 2. Support configurable compressor 3. support zcomp multi buffers 4. support lz4 Please don't add workmem policy in patch 1 because we still use only a buffer until 3 so patch 1, 2 would be very simple. Patch 3 might introduce wm policy. Then, it would be very clear why we need it for zomp_multi so that it would make review easy. If 1,2,3 have no problem and apparenlty lz4 has a benefit, patch 4 will be merged easily but If lz4 were rejected by some reason, we could support another compression easily since patch 1,2,3 is merged. Thanks. -- Kind regards, Minchan Kim ^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: [PATCHv2 1/2] zram: introduce compressing backend abstraction 2014-02-06 5:16 ` Minchan Kim @ 2014-02-07 2:02 ` Minchan Kim 0 siblings, 0 replies; 5+ messages in thread From: Minchan Kim @ 2014-02-07 2:02 UTC (permalink / raw) To: Sergey Senozhatsky; +Cc: Jerome Marchand, Nitin Gupta, linux-kernel On Thu, Feb 06, 2014 at 02:16:28PM +0900, Minchan Kim wrote: > Hello Sergey, > > Sorry for late response. > > On Thu, Jan 30, 2014 at 10:28:07PM +0300, Sergey Senozhatsky wrote: > > ZRAM performs direct LZO compression algorithm calls, making it the one > > and only option. Introduce 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 > > .workmem_get > > .workmem_put > > > > 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 new object > > 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() will 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 (workmem is a common term used by lzo, lz4 > > and zlib) contains buffers required by compression algorithm, while new > > struct zcomp_wm_policy implements workmem handling and locking by means > > of get() and put() semantics and removes requirement b) from zram meta. > > In general, workmem_get() turns caller into exclusive user of workmem > > and workem_put() makes a particular workmem available. > > > > Each compressing backend may use a default workmem policy or provide > > custom implementation. Default workmem policy (struct zcomp_wm_policy) > > has a list of idle workmem buffers (at least 1 workmem), spinlock to > > protect idle list and wait queue, making it possible to have parallel > > compressions. Each time zram issues a workmem_get() call, the following > > set of operations performed: > > I'm still really not sure why backend should know workmem policy. > I think it's matter of upper layer, not backend. > Yeb, surely, you have a reason but it's very hard for me to know it > by this patchset so I'd like to divide the patchset. > (You don't need to explain it in here and I expect it would be clear > if you separate it like I suggested below). > Pz, see below. > > > - 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 workmem_put() caller. > > > > workmem_put(): > > - spin lock buffer_lock > > - add workmem to idle list > > - spin unlock, wake up sleeper (if any) > > Good. > > > > > zram_comp file contains array of supported compressing backends, which > > can be altered according to user selection. > > > > Usage examples. To initialize compressing backend: > > comp = zcomp_create(NAME) /* NAME e.g. lzo, lz4 */ > > > > which performs NAME lookup in array of supported compressing backends > > and initialises compressing backend if requested algorithm is supported. > > Compressing: > > wm = comp->workmem_get(comp) > > comp->compress(...) > > [..] /* copy compressed data */ > > comp->workmem_put(comp, wm) > > > > Free compessing backend and all ot its workmem buffers: > > zcomp_destroy(comp) > > > > The patch implements LZO and LZ4 backends. At this point zcomp_wm_policy > > keeps only one workmem in the idle list, support for multi workmem buffers > > will be introduced later. > > > > Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com> > > --- > > drivers/block/zram/zcomp_lz4.c | 49 ++++++++++ > > drivers/block/zram/zcomp_lz4.h | 18 ++++ > > Please don't include lz4 in this patch. It should be separated and > description of the patch surely should include the number to prove > lz4 is better than lzo in *what* workload so it should make > everybody easy to convince. > > And let's separate this patchset following as > > 1. abstract compressor with zram_comp. > 2. Support configurable compressor > 3. support zcomp multi buffers > 4. support lz4 > > Please don't add workmem policy in patch 1 because we still use only > a buffer until 3 so patch 1, 2 would be very simple. > Patch 3 might introduce wm policy. Then, it would be very clear > why we need it for zomp_multi so that it would make review easy. > > If 1,2,3 have no problem and apparenlty lz4 has a benefit, patch 4 > will be merged easily but If lz4 were rejected by some reason, > we could support another compression easily since patch 1,2,3 is > merged. Hello Sergey, If I don't show my brain to you, I guess we need more iterations to discuss and it's really waste our time so I send prototype patch to show the concept I am thinking. Surely, It wouldn't work but it's enough to show my concept. It's really simple. Backend could just focus comp/decomp with own algorithm buffer. (ie, backend don't need to know compress_buffer and workmem policy because it's caller's matter, not compression backed). I hope you complete this patch to work well with your SOB/Copyright if you don't object the direction because most of concept and code are from your idea and implementation. Thanks. >From 7f042055a30eb0ab22377634520a39568a7d16ac Mon Sep 17 00:00:00 2001 From: Minchan Kim <minchan@kernel.org> Date: Tue, 4 Feb 2014 17:27:25 +0900 Subject: [PATCH] introduce zram_comp Signed-off-by: Minchan Kim <minchan@kernel.org> --- drivers/block/zram/Makefile | 5 ++- drivers/block/zram/zcomp_lzo.c | 29 +++++++++++++++ drivers/block/zram/zram_comp.c | 84 ++++++++++++++++++++++++++++++++++++++++++ drivers/block/zram/zram_comp.h | 36 ++++++++++++++++++ drivers/block/zram/zram_drv.c | 45 ++++++++++------------ drivers/block/zram/zram_drv.h | 6 +-- 6 files changed, 175 insertions(+), 30 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 diff --git a/drivers/block/zram/Makefile b/drivers/block/zram/Makefile index cb0f9ced6a93..7a4de6cce4e4 100644 --- a/drivers/block/zram/Makefile +++ b/drivers/block/zram/Makefile @@ -1,3 +1,4 @@ -zram-y := zram_drv.o +zram-y := zram_drv.o +zram-y += zcomp_lzo.o zram_comp.o -obj-$(CONFIG_ZRAM) += zram.o +obj-$(CONFIG_ZRAM) += zram.o diff --git a/drivers/block/zram/zcomp_lzo.c b/drivers/block/zram/zcomp_lzo.c new file mode 100644 index 000000000000..2a58d5392d4d --- /dev/null +++ b/drivers/block/zram/zcomp_lzo.c @@ -0,0 +1,29 @@ +#include "zram_comp.h" + +#include <linux/lzo.h> + +void *lzo_init(void) +{ + void *private = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL); + return private; +} + +void lzo_free(void *private) +{ + kfree(private); +} + +int lzo_compress_page(unsigned char *in, size_t in_len, + unsigned char *out, size_t *out_len, + void *private) +{ + return lzo1x_1_compress(in, in_len, out, out_len, private); +} + +const struct zram_comp zcomp_lzo = { + .init = lzo_init, + .destroy = lzo_free, + .compress_page = lzo_compress_page, + .decompress_page = lzo1x_decompress_safe, + .name = "lzo", +}; diff --git a/drivers/block/zram/zram_comp.c b/drivers/block/zram/zram_comp.c new file mode 100644 index 000000000000..c6af6cda7529 --- /dev/null +++ b/drivers/block/zram/zram_comp.c @@ -0,0 +1,84 @@ +#include "zram_comp.h" +#include <linux/sched.h> + +extern struct zram_comp zcomp_lzo; + +struct zcomp_strm *zcomp_get_strm(struct zram_comp *comp) +{ + struct zcomp_strm *strm; +again: + mutex_lock(&comp->lock); + if (list_empty(&comp->strm_list)) { + mutex_unlock(&comp->lock); + /* wait */ + wait_event(comp->wait, !list_empty(&comp->strm_list)); + goto again; + } + + strm = list_entry(comp->strm_list.prev, + struct zcomp_strm, list); + list_del(&strm->list); + mutex_unlock(&comp->lock); + return strm; +} + +void zcomp_put_strm(struct zram_comp *comp, struct zcomp_strm *strm) +{ + mutex_lock(&comp->lock); + list_add(&strm->list, &comp->strm_list); + mutex_unlock(&comp->lock); + wake_up(&comp->wait); +} + +static struct zram_comp *find_comp(char *comp_name) +{ + if (!strcmp(comp_name, "lzo")) + return &zcomp_lzo; + + return NULL; +} + +int zcomp_compress_page(struct zram_comp *comp, struct zcomp_strm *strm, + unsigned char *in, size_t *clen) +{ + return comp->compress_page(in, PAGE_SIZE, strm->compress_buffer, + clen, strm->private); +} + +struct zram_comp *zcomp_create(char *comp_name, int nr_strm) +{ + int i; + struct zram_comp *comp; + /* Look up supported backend */ + comp = find_comp(comp_name); + if (!comp) + return NULL; + + INIT_LIST_HEAD(&comp->strm_list); + mutex_init(&comp->lock); + init_waitqueue_head(&comp->wait); + + for (i = 0; i < nr_strm; i++) { + struct zcomp_strm *strm = kmalloc(sizeof(*strm), GFP_KERNEL); + strm->compress_buffer = + (void*)__get_free_pages(GFP_KERNEL | __GFP_ZERO, 1); + strm->private = comp->init(); + list_add(&strm->list, &comp->strm_list); + } + + return comp; +} + +void zcomp_destroy(struct zram_comp *comp) +{ + struct zcomp_strm *strm; + + while (!list_empty(&comp->strm_list)) { + strm = list_entry(comp->strm_list.prev, + struct zcomp_strm, list); + free_pages((unsigned long)strm->compress_buffer, 1); + comp->destroy(strm->private); + list_del(&strm->list); + kfree(strm); + } +} diff --git a/drivers/block/zram/zram_comp.h b/drivers/block/zram/zram_comp.h new file mode 100644 index 000000000000..705b8231f4d5 --- /dev/null +++ b/drivers/block/zram/zram_comp.h @@ -0,0 +1,36 @@ +#ifndef __ZRAM_COMP_H_ +#define __ZRAM_COMP_H_ + +#include <linux/slab.h> +#include "zram_drv.h" + +struct zcomp_strm { + void *private; + void *compress_buffer; + struct list_head list; +}; + +struct zram_comp { + + struct list_head strm_list; + struct mutex lock; + wait_queue_head_t wait; + + int (*compress_page)(unsigned char *in, size_t in_len, + unsigned char *out, size_t *out_len, void *private); + int (*decompress_page)(const unsigned char *in, size_t in_len, + unsigned char *out, size_t *out_len); + + void *(*init)(void); + void (*destroy)(void *private); + char name[10]; +}; + +struct zcomp_strm *zcomp_get_strm(struct zram_comp *comp); +void zcomp_put_strm(struct zram_comp *comp, struct zcomp_strm *strm); +int zcomp_compress_page(struct zram_comp *comp, struct zcomp_strm *strm, + unsigned char *in, size_t *clen); +struct zram_comp *zcomp_create(char *comp_name, int nr_strm); +void zcomp_destroy(struct zram_comp *comp); + +#endif diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 25bbc59486ff..34a72903f808 100644 --- a/drivers/block/zram/zram_drv.c +++ b/drivers/block/zram/zram_drv.c @@ -33,6 +33,7 @@ #include <linux/string.h> #include <linux/vmalloc.h> +#include "zram_comp.h" #include "zram_drv.h" /* Globals */ @@ -160,35 +161,29 @@ 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); } -static struct zram_meta *zram_meta_alloc(u64 disksize) +static struct zram_meta *zram_meta_alloc(u64 disksize, char *comp_name, + int nr_comp) { size_t num_pages; + struct zram_comp *comp; struct zram_meta *meta = kmalloc(sizeof(*meta), GFP_KERNEL); + if (!meta) goto out; - meta->compress_workmem = kzalloc(LZO1X_MEM_COMPRESS, GFP_KERNEL); - if (!meta->compress_workmem) + comp = zcomp_create(comp_name, 1); + if (!comp) 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_comp; } meta->mem_pool = zs_create_pool(GFP_NOIO | __GFP_HIGHMEM); @@ -198,15 +193,12 @@ 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_comp: + zcomp_destroy(comp); free_meta: kfree(meta); meta = NULL; @@ -284,6 +276,7 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index) size_t clen = PAGE_SIZE; unsigned char *cmem; struct zram_meta *meta = zram->meta; + struct zram_comp *comp = zram->comp; unsigned long handle; u16 size; @@ -301,7 +294,8 @@ 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 = comp->decompress_page(cmem, size, mem, &clen); + zs_unmap_object(meta->mem_pool, handle); read_unlock(&meta->tb_lock); @@ -373,10 +367,11 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, unsigned long handle; struct page *page; unsigned char *user_mem, *cmem, *src, *uncmem = NULL; + struct zram_comp *comp = zram->comp; struct zram_meta *meta = zram->meta; + struct zcomp_strm *strm; page = bvec->bv_page; - src = meta->compress_buffer; if (is_partial_io(bvec)) { /* @@ -393,7 +388,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, goto free_res; } - mutex_lock(&meta->buffer_lock); + strm = zcomp_get_strm(comp); + src = strm->compress_buffer; user_mem = kmap_atomic(page); if (is_partial_io(bvec)) { @@ -418,8 +414,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, goto free_res; } - ret = lzo1x_1_compress(uncmem, PAGE_SIZE, src, &clen, - meta->compress_workmem); + ret = zcomp_compress_page(comp, strm, uncmem, &clen); if (!is_partial_io(bvec)) { kunmap_atomic(user_mem); user_mem = NULL; @@ -471,7 +466,7 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, /* Update stats */ atomic64_add(clen, &zram->stats.compr_data_size); atomic64_inc(&zram->stats.pages_stored); - mutex_unlock(&meta->buffer_lock); + zcomp_put_strm(comp, strm); free_res: if (is_partial_io(bvec)) @@ -549,7 +544,7 @@ static ssize_t disksize_store(struct device *dev, } disksize = PAGE_ALIGN(disksize); - zram->meta = zram_meta_alloc(disksize); + zram->meta = zram_meta_alloc(disksize, "lzo", 1); if (!zram->meta) { up_write(&zram->init_lock); return -ENOMEM; diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h index 1d5b1f5786a8..57d5a63456e7 100644 --- a/drivers/block/zram/zram_drv.h +++ b/drivers/block/zram/zram_drv.h @@ -18,6 +18,8 @@ #include <linux/spinlock.h> #include <linux/mutex.h> #include <linux/zsmalloc.h> +#include <linux/rwsem.h> +#include <linux/wait.h> /* * Some arbitrary value. This is just to catch @@ -81,15 +83,13 @@ 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 zram_comp *comp; struct request_queue *queue; struct gendisk *disk; /* Prevent concurrent execution of device init, reset and R/W request */ -- 1.8.5.2 -- Kind regards, Minchan Kim ^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCHv2 2/2] zram: use zram_comp compressing backends 2014-01-30 19:28 [PATCHv2 0/2] add compression backend abstraction Sergey Senozhatsky 2014-01-30 19:28 ` [PATCHv2 1/2] zram: introduce compressing " Sergey Senozhatsky @ 2014-01-30 19:28 ` Sergey Senozhatsky 1 sibling, 0 replies; 5+ messages in thread From: Sergey Senozhatsky @ 2014-01-30 19:28 UTC (permalink / raw) To: Minchan Kim Cc: Jerome Marchand, Nitin Gupta, linux-kernel, Sergey Senozhatsky 1) Use zram_comp in zram instead of direct LZO calls. 2) Introduce Kconfig ZRAM_LZO_COMPRESS and ZRAM_LZ4_COMPRESS options to enable/disable LZO and LZ4 backends respectively. 3) Add compressor device attr that allows to list and select compression algorithms. usage example: List available compression algorithms (currently selected one is LZO): cat /sys/block/zram0/compressor <lzo> lz4 Change compression algorithm to LZ4: echo lz4 > /sys/block/zram0/compressor cat /sys/block/zram0/compressor lzo <lz4> iozone test `buffer_lock mutex' vs `single workmem and spinlock' (each test was performed on cold, freashly created zram device, ext4 file system, LZO): ./iozone -t -T -R -l 3 -u 3 -r 16K -s 60M -I +Z test unpatсhed patched ------------------------------------------------- Initial write: 341274.03 | 465984.82 Rewrite: 527752.14 | 587844.27 Read: 2668083.00 | 3527865.95 Re-read: 2479428.12 | 3497152.56 Reverse Read: 1563745.16 | 2463020.33 Stride read: 1916311.84 | 2675575.14 Random read: 2099906.09 | 2758833.45 Mixed workload: 1631288.54 | 2439302.68 Random write: 428555.50 | 578720.39 Pwrite: 339857.66 | 415078.43 Pread: 1523965.55 | 1417191.77 Fwrite: 1571574.03 | 1447609.02 Fread: 5449851.44 | 5563909.53 ./iozone -t -T -R -l 3 -u 3 -r 16K -s 60M -I +Z test unpatсhed patched ------------------------------------------------- Initial write: 435205.70 | 513995.38 Rewrite: 617956.68 | 600445.49 Read: 3379175.66 | 3354820.31 Re-read: 3344446.66 | 3484581.59 Reverse Read: 2215915.09 | 2689347.80 Stride read: 2570020.28 | 2797849.55 Random read: 2751191.27 | 2830580.06 Mixed workload: 1647681.38 | 2382402.29 Random write: 591968.77 | 597967.23 Pwrite: 441869.31 | 526409.72 Pread: 1530294.05 | 1466025.38 Fwrite: 1634969.22 | 1757762.27 Fread: 4782938.06 | 5643076.25 Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com> --- drivers/block/zram/Kconfig | 20 ++++++++- drivers/block/zram/Makefile | 6 ++- drivers/block/zram/zram_drv.c | 96 ++++++++++++++++++++++++++++--------------- drivers/block/zram/zram_drv.h | 8 ++-- 4 files changed, 90 insertions(+), 40 deletions(-) diff --git a/drivers/block/zram/Kconfig b/drivers/block/zram/Kconfig index 3450be8..4de410d 100644 --- a/drivers/block/zram/Kconfig +++ b/drivers/block/zram/Kconfig @@ -1,8 +1,6 @@ config ZRAM tristate "Compressed RAM block device support" depends on BLOCK && SYSFS && ZSMALLOC - select LZO_COMPRESS - select LZO_DECOMPRESS default n help Creates virtual block devices called /dev/zramX (X = 0, 1, ...). @@ -15,6 +13,24 @@ config ZRAM See zram.txt for more information. +config ZRAM_LZO_COMPRESS + bool "Compressed RAM LZO support" + depends on ZRAM + select LZO_COMPRESS + select LZO_DECOMPRESS + default y + help + This option adds LZO compression algorithm support. + +config ZRAM_LZ4_COMPRESS + bool "Compressed RAM LZ4 support" + depends on ZRAM + select LZ4_COMPRESS + select LZ4_DECOMPRESS + default n + help + This option adds LZ4 compression algorithm support. + config ZRAM_DEBUG bool "Compressed RAM block device debug support" depends on ZRAM diff --git a/drivers/block/zram/Makefile b/drivers/block/zram/Makefile index cb0f9ce..c649600 100644 --- a/drivers/block/zram/Makefile +++ b/drivers/block/zram/Makefile @@ -1,3 +1,7 @@ -zram-y := zram_drv.o +zram-y := zram_comp.o zram_drv.o + +zram-$(CONFIG_ZRAM_LZO_COMPRESS) += zcomp_lzo.o + +zram-$(CONFIG_ZRAM_LZ4_COMPRESS) += zcomp_lz4.o obj-$(CONFIG_ZRAM) += zram.o diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c index 9baac5b..46b1a44 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; @@ -108,6 +107,39 @@ static ssize_t mem_used_total_show(struct device *dev, return sprintf(buf, "%llu\n", val); } +static ssize_t compressor_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct zram *zram = dev_to_zram(dev); + ssize_t sz = 0; + down_read(&zram->init_lock); + sz = zcomp_available_show(zram->comp, buf); + up_read(&zram->init_lock); + return sz; +} + +static ssize_t compressor_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t len) +{ + struct zram *zram = dev_to_zram(dev); + + down_write(&zram->init_lock); + if (init_done(zram) || zram->comp) { + up_write(&zram->init_lock); + pr_info("Cannot change compressor for initialized device\n"); + return -EBUSY; + } + + zram->comp = zcomp_create(buf); + if (!zram->comp) { + up_write(&zram->init_lock); + pr_info("Cannot initialise compressing backend"); + return -EINVAL; + } + 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) @@ -160,8 +192,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 +203,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 +217,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 +294,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 +315,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 = zram->comp->decompress(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 +363,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 +388,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 +407,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, goto out; } - mutex_lock(&meta->buffer_lock); + wm = zram->comp->workmem_get(zram->comp); + src = wm->buf; locked = true; user_mem = kmap_atomic(page); @@ -420,15 +434,14 @@ 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 = zram->comp->compress(uncmem, PAGE_SIZE, src, &clen, wm->mem); 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; } @@ -457,6 +470,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index, memcpy(cmem, src, clen); } + zram->comp->workmem_put(zram->comp, wm); + locked = false; zs_unmap_object(meta->mem_pool, handle); /* @@ -475,10 +490,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); + zram->comp->workmem_put(zram->comp, wm); if (is_partial_io(bvec)) kfree(uncmem); - if (ret) atomic64_inc(&zram->stats.failed_writes); return ret; @@ -522,6 +536,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,6 +568,14 @@ static ssize_t disksize_store(struct device *dev, return -EBUSY; } + if (!zram->comp) + 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) { @@ -704,6 +730,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(compressor, S_IRUGO | S_IWUSR, + compressor_show, compressor_store); ZRAM_ATTR_RO(num_reads); ZRAM_ATTR_RO(num_writes); @@ -718,6 +746,7 @@ static struct attribute *zram_disk_attrs[] = { &dev_attr_disksize.attr, &dev_attr_initstate.attr, &dev_attr_reset.attr, + &dev_attr_compressor.attr, &dev_attr_num_reads.attr, &dev_attr_num_writes.attr, &dev_attr_failed_reads.attr, @@ -790,6 +819,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.rc1.183.g614c158 ^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2014-02-07 2:01 UTC | newest] Thread overview: 5+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2014-01-30 19:28 [PATCHv2 0/2] add compression backend abstraction Sergey Senozhatsky 2014-01-30 19:28 ` [PATCHv2 1/2] zram: introduce compressing " Sergey Senozhatsky 2014-02-06 5:16 ` Minchan Kim 2014-02-07 2:02 ` Minchan Kim 2014-01-30 19:28 ` [PATCHv2 2/2] zram: use zram_comp compressing backends 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).