Loading Documentation/ABI/testing/sysfs-block-zram +10 −0 Original line number Diff line number Diff line Loading @@ -137,3 +137,13 @@ Description: The writeback_limit file is read-write and specifies the maximum amount of writeback ZRAM can do. The limit could be changed in run time. What: /sys/block/zram<id>/use_dedup Date: March 2017 Contact: Joonsoo Kim <iamjoonsoo.kim@lge.com> Description: The use_dedup file is read-write and specifies deduplication feature is used or not. If enabled, duplicated data is managed by reference count and will not be stored in memory twice. Benefit of this feature largely depends on the workload so keep attention when use. Documentation/blockdev/zram.txt +1 −1 Original line number Diff line number Diff line Loading @@ -173,7 +173,7 @@ compact WO trigger memory compaction debug_stat RO this file is used for zram debugging purposes backing_dev RW set up backend storage for zram to write out idle WO mark allocated slot as idle use_dedup RW show and set deduplication feature User space is advised to use the following files to read the device statistics. Loading drivers/block/zram/Kconfig +14 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,20 @@ config ZRAM See Documentation/blockdev/zram.txt for more information. config ZRAM_DEDUP bool "Deduplication support for ZRAM data" depends on ZRAM default n help Deduplicate ZRAM data to reduce amount of memory consumption. Advantage largely depends on the workload. In some cases, this option reduces memory usage to the half. However, if there is no duplicated data, the amount of memory consumption would be increased due to additional metadata usage. And, there is computation time trade-off. Please check the benefit before enabling this option. Experiment shows the positive effect when the zram is used as blockdev and is used to store build output. config ZRAM_WRITEBACK bool "Write back incompressible or idle page to backing device" depends on ZRAM Loading drivers/block/zram/Makefile +2 −1 Original line number Diff line number Diff line zram-y := zcomp.o zram_drv.o zram_dedup.o zram-y := zcomp.o zram_drv.o zram-$(CONFIG_ZRAM_DEDUP) += zram_dedup.o obj-$(CONFIG_ZRAM) += zram.o drivers/block/zram/zram_dedup.c +65 −14 Original line number Diff line number Diff line Loading @@ -41,6 +41,9 @@ void zram_dedup_insert(struct zram *zram, struct zram_entry *new, struct rb_node **rb_node, *parent = NULL; struct zram_entry *entry; if (!zram_dedup_enabled(zram)) return; new->checksum = checksum; hash = &zram->hash[checksum % zram->hash_size]; rb_root = &hash->rb_root; Loading Loading @@ -89,13 +92,14 @@ static unsigned long zram_dedup_put(struct zram *zram, { struct zram_hash *hash; u32 checksum; unsigned long val; checksum = entry->checksum; hash = &zram->hash[checksum % zram->hash_size]; spin_lock(&hash->lock); entry->refcount--; val = --entry->refcount; if (!entry->refcount) rb_erase(&entry->rb_node, &hash->rb_root); else Loading @@ -103,35 +107,70 @@ static unsigned long zram_dedup_put(struct zram *zram, spin_unlock(&hash->lock); return entry->refcount; return val; } static struct zram_entry *zram_dedup_get(struct zram *zram, unsigned char *mem, u32 checksum) static struct zram_entry *__zram_dedup_get(struct zram *zram, struct zram_hash *hash, unsigned char *mem, struct zram_entry *entry) { struct zram_hash *hash; struct zram_entry *entry; struct zram_entry *tmp, *prev = NULL; struct rb_node *rb_node; hash = &zram->hash[checksum % zram->hash_size]; /* find left-most entry with same checksum */ while ((rb_node = rb_prev(&entry->rb_node))) { tmp = rb_entry(rb_node, struct zram_entry, rb_node); if (tmp->checksum != entry->checksum) break; spin_lock(&hash->lock); rb_node = hash->rb_root.rb_node; while (rb_node) { entry = rb_entry(rb_node, struct zram_entry, rb_node); if (checksum == entry->checksum) { entry = tmp; } again: entry->refcount++; atomic64_add(entry->len, &zram->stats.dup_data_size); spin_unlock(&hash->lock); if (prev) zram_entry_free(zram, prev); if (zram_dedup_match(zram, entry, mem)) return entry; spin_lock(&hash->lock); tmp = NULL; rb_node = rb_next(&entry->rb_node); if (rb_node) tmp = rb_entry(rb_node, struct zram_entry, rb_node); if (tmp && (tmp->checksum == entry->checksum)) { prev = entry; entry = tmp; goto again; } spin_unlock(&hash->lock); zram_entry_free(zram, entry); return NULL; } static struct zram_entry *zram_dedup_get(struct zram *zram, unsigned char *mem, u32 checksum) { struct zram_hash *hash; struct zram_entry *entry; struct rb_node *rb_node; hash = &zram->hash[checksum % zram->hash_size]; spin_lock(&hash->lock); rb_node = hash->rb_root.rb_node; while (rb_node) { entry = rb_entry(rb_node, struct zram_entry, rb_node); if (checksum == entry->checksum) return __zram_dedup_get(zram, hash, mem, entry); if (checksum < entry->checksum) rb_node = rb_node->rb_left; else Loading @@ -148,6 +187,9 @@ struct zram_entry *zram_dedup_find(struct zram *zram, struct page *page, void *mem; struct zram_entry *entry; if (!zram_dedup_enabled(zram)) return NULL; mem = kmap_atomic(page); *checksum = zram_dedup_checksum(mem); Loading @@ -160,6 +202,9 @@ struct zram_entry *zram_dedup_find(struct zram *zram, struct page *page, void zram_dedup_init_entry(struct zram *zram, struct zram_entry *entry, unsigned long handle, unsigned int len) { if (!zram_dedup_enabled(zram)) return; entry->handle = handle; entry->refcount = 1; entry->len = len; Loading @@ -167,6 +212,9 @@ void zram_dedup_init_entry(struct zram *zram, struct zram_entry *entry, bool zram_dedup_put_entry(struct zram *zram, struct zram_entry *entry) { if (!zram_dedup_enabled(zram)) return true; if (zram_dedup_put(zram, entry)) return false; Loading @@ -178,6 +226,9 @@ int zram_dedup_init(struct zram *zram, size_t num_pages) int i; struct zram_hash *hash; if (!zram_dedup_enabled(zram)) return 0; zram->hash_size = num_pages >> ZRAM_HASH_SHIFT; zram->hash_size = min_t(size_t, ZRAM_HASH_SIZE_MAX, zram->hash_size); zram->hash_size = max_t(size_t, ZRAM_HASH_SIZE_MIN, zram->hash_size); Loading Loading
Documentation/ABI/testing/sysfs-block-zram +10 −0 Original line number Diff line number Diff line Loading @@ -137,3 +137,13 @@ Description: The writeback_limit file is read-write and specifies the maximum amount of writeback ZRAM can do. The limit could be changed in run time. What: /sys/block/zram<id>/use_dedup Date: March 2017 Contact: Joonsoo Kim <iamjoonsoo.kim@lge.com> Description: The use_dedup file is read-write and specifies deduplication feature is used or not. If enabled, duplicated data is managed by reference count and will not be stored in memory twice. Benefit of this feature largely depends on the workload so keep attention when use.
Documentation/blockdev/zram.txt +1 −1 Original line number Diff line number Diff line Loading @@ -173,7 +173,7 @@ compact WO trigger memory compaction debug_stat RO this file is used for zram debugging purposes backing_dev RW set up backend storage for zram to write out idle WO mark allocated slot as idle use_dedup RW show and set deduplication feature User space is advised to use the following files to read the device statistics. Loading
drivers/block/zram/Kconfig +14 −0 Original line number Diff line number Diff line Loading @@ -15,6 +15,20 @@ config ZRAM See Documentation/blockdev/zram.txt for more information. config ZRAM_DEDUP bool "Deduplication support for ZRAM data" depends on ZRAM default n help Deduplicate ZRAM data to reduce amount of memory consumption. Advantage largely depends on the workload. In some cases, this option reduces memory usage to the half. However, if there is no duplicated data, the amount of memory consumption would be increased due to additional metadata usage. And, there is computation time trade-off. Please check the benefit before enabling this option. Experiment shows the positive effect when the zram is used as blockdev and is used to store build output. config ZRAM_WRITEBACK bool "Write back incompressible or idle page to backing device" depends on ZRAM Loading
drivers/block/zram/Makefile +2 −1 Original line number Diff line number Diff line zram-y := zcomp.o zram_drv.o zram_dedup.o zram-y := zcomp.o zram_drv.o zram-$(CONFIG_ZRAM_DEDUP) += zram_dedup.o obj-$(CONFIG_ZRAM) += zram.o
drivers/block/zram/zram_dedup.c +65 −14 Original line number Diff line number Diff line Loading @@ -41,6 +41,9 @@ void zram_dedup_insert(struct zram *zram, struct zram_entry *new, struct rb_node **rb_node, *parent = NULL; struct zram_entry *entry; if (!zram_dedup_enabled(zram)) return; new->checksum = checksum; hash = &zram->hash[checksum % zram->hash_size]; rb_root = &hash->rb_root; Loading Loading @@ -89,13 +92,14 @@ static unsigned long zram_dedup_put(struct zram *zram, { struct zram_hash *hash; u32 checksum; unsigned long val; checksum = entry->checksum; hash = &zram->hash[checksum % zram->hash_size]; spin_lock(&hash->lock); entry->refcount--; val = --entry->refcount; if (!entry->refcount) rb_erase(&entry->rb_node, &hash->rb_root); else Loading @@ -103,35 +107,70 @@ static unsigned long zram_dedup_put(struct zram *zram, spin_unlock(&hash->lock); return entry->refcount; return val; } static struct zram_entry *zram_dedup_get(struct zram *zram, unsigned char *mem, u32 checksum) static struct zram_entry *__zram_dedup_get(struct zram *zram, struct zram_hash *hash, unsigned char *mem, struct zram_entry *entry) { struct zram_hash *hash; struct zram_entry *entry; struct zram_entry *tmp, *prev = NULL; struct rb_node *rb_node; hash = &zram->hash[checksum % zram->hash_size]; /* find left-most entry with same checksum */ while ((rb_node = rb_prev(&entry->rb_node))) { tmp = rb_entry(rb_node, struct zram_entry, rb_node); if (tmp->checksum != entry->checksum) break; spin_lock(&hash->lock); rb_node = hash->rb_root.rb_node; while (rb_node) { entry = rb_entry(rb_node, struct zram_entry, rb_node); if (checksum == entry->checksum) { entry = tmp; } again: entry->refcount++; atomic64_add(entry->len, &zram->stats.dup_data_size); spin_unlock(&hash->lock); if (prev) zram_entry_free(zram, prev); if (zram_dedup_match(zram, entry, mem)) return entry; spin_lock(&hash->lock); tmp = NULL; rb_node = rb_next(&entry->rb_node); if (rb_node) tmp = rb_entry(rb_node, struct zram_entry, rb_node); if (tmp && (tmp->checksum == entry->checksum)) { prev = entry; entry = tmp; goto again; } spin_unlock(&hash->lock); zram_entry_free(zram, entry); return NULL; } static struct zram_entry *zram_dedup_get(struct zram *zram, unsigned char *mem, u32 checksum) { struct zram_hash *hash; struct zram_entry *entry; struct rb_node *rb_node; hash = &zram->hash[checksum % zram->hash_size]; spin_lock(&hash->lock); rb_node = hash->rb_root.rb_node; while (rb_node) { entry = rb_entry(rb_node, struct zram_entry, rb_node); if (checksum == entry->checksum) return __zram_dedup_get(zram, hash, mem, entry); if (checksum < entry->checksum) rb_node = rb_node->rb_left; else Loading @@ -148,6 +187,9 @@ struct zram_entry *zram_dedup_find(struct zram *zram, struct page *page, void *mem; struct zram_entry *entry; if (!zram_dedup_enabled(zram)) return NULL; mem = kmap_atomic(page); *checksum = zram_dedup_checksum(mem); Loading @@ -160,6 +202,9 @@ struct zram_entry *zram_dedup_find(struct zram *zram, struct page *page, void zram_dedup_init_entry(struct zram *zram, struct zram_entry *entry, unsigned long handle, unsigned int len) { if (!zram_dedup_enabled(zram)) return; entry->handle = handle; entry->refcount = 1; entry->len = len; Loading @@ -167,6 +212,9 @@ void zram_dedup_init_entry(struct zram *zram, struct zram_entry *entry, bool zram_dedup_put_entry(struct zram *zram, struct zram_entry *entry) { if (!zram_dedup_enabled(zram)) return true; if (zram_dedup_put(zram, entry)) return false; Loading @@ -178,6 +226,9 @@ int zram_dedup_init(struct zram *zram, size_t num_pages) int i; struct zram_hash *hash; if (!zram_dedup_enabled(zram)) return 0; zram->hash_size = num_pages >> ZRAM_HASH_SHIFT; zram->hash_size = min_t(size_t, ZRAM_HASH_SIZE_MAX, zram->hash_size); zram->hash_size = max_t(size_t, ZRAM_HASH_SIZE_MIN, zram->hash_size); Loading