* [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 an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.