2009-09-22 12:56:53 +08:00
|
|
|
/*
|
2010-06-01 16:01:25 +08:00
|
|
|
* Compressed RAM block device
|
2009-09-22 12:56:53 +08:00
|
|
|
*
|
2010-01-28 23:51:35 +08:00
|
|
|
* Copyright (C) 2008, 2009, 2010 Nitin Gupta
|
2014-01-31 07:45:55 +08:00
|
|
|
* 2012, 2013 Minchan Kim
|
2009-09-22 12:56:53 +08:00
|
|
|
*
|
|
|
|
* This code is released using a dual license strategy: BSD/GPL
|
|
|
|
* You can choose the licence that better fits your requirements.
|
|
|
|
*
|
|
|
|
* Released under the terms of 3-clause BSD License
|
|
|
|
* Released under the terms of GNU General Public License Version 2.0
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2010-06-01 16:01:25 +08:00
|
|
|
#ifndef _ZRAM_DRV_H_
|
|
|
|
#define _ZRAM_DRV_H_
|
2009-09-22 12:56:53 +08:00
|
|
|
|
2010-01-28 23:43:37 +08:00
|
|
|
#include <linux/spinlock.h>
|
2014-01-31 07:45:50 +08:00
|
|
|
#include <linux/zsmalloc.h>
|
2009-09-22 12:56:53 +08:00
|
|
|
|
2014-04-08 06:38:12 +08:00
|
|
|
#include "zcomp.h"
|
|
|
|
|
2009-09-22 12:56:53 +08:00
|
|
|
/*-- Configurable parameters */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Pages that compress to size greater than this are stored
|
|
|
|
* uncompressed in memory.
|
|
|
|
*/
|
2011-09-10 07:01:00 +08:00
|
|
|
static const size_t max_zpage_size = PAGE_SIZE / 4 * 3;
|
2009-09-22 12:56:53 +08:00
|
|
|
|
|
|
|
/*
|
2010-05-13 16:54:21 +08:00
|
|
|
* NOTE: max_zpage_size must be less than or equal to:
|
2012-10-10 07:49:52 +08:00
|
|
|
* ZS_MAX_ALLOC_SIZE. Otherwise, zs_malloc() would
|
|
|
|
* always return failure.
|
2009-09-22 12:56:53 +08:00
|
|
|
*/
|
|
|
|
|
|
|
|
/*-- End of configurable params */
|
|
|
|
|
|
|
|
#define SECTOR_SHIFT 9
|
|
|
|
#define SECTORS_PER_PAGE_SHIFT (PAGE_SHIFT - SECTOR_SHIFT)
|
|
|
|
#define SECTORS_PER_PAGE (1 << SECTORS_PER_PAGE_SHIFT)
|
2011-06-10 21:28:48 +08:00
|
|
|
#define ZRAM_LOGICAL_BLOCK_SHIFT 12
|
|
|
|
#define ZRAM_LOGICAL_BLOCK_SIZE (1 << ZRAM_LOGICAL_BLOCK_SHIFT)
|
|
|
|
#define ZRAM_SECTOR_PER_LOGICAL_BLOCK \
|
|
|
|
(1 << (ZRAM_LOGICAL_BLOCK_SHIFT - SECTOR_SHIFT))
|
2009-09-22 12:56:53 +08:00
|
|
|
|
zram: replace global tb_lock with fine grain lock
Currently, we use a rwlock tb_lock to protect concurrent access to the
whole zram meta table. However, according to the actual access model,
there is only a small chance for upper user to access the same
table[index], so the current lock granularity is too big.
The idea of optimization is to change the lock granularity from whole
meta table to per table entry (table -> table[index]), so that we can
protect concurrent access to the same table[index], meanwhile allow the
maximum concurrency.
With this in mind, several kinds of locks which could be used as a
per-entry lock were tested and compared:
Test environment:
x86-64 Intel Core2 Q8400, system memory 4GB, Ubuntu 12.04,
kernel v3.15.0-rc3 as base, zram with 4 max_comp_streams LZO.
iozone test:
iozone -t 4 -R -r 16K -s 200M -I +Z
(1GB zram with ext4 filesystem, take the average of 10 tests, KB/s)
Test base CAS spinlock rwlock bit_spinlock
-------------------------------------------------------------------
Initial write 1381094 1425435 1422860 1423075 1421521
Rewrite 1529479 1641199 1668762 1672855 1654910
Read 8468009 11324979 11305569 11117273 10997202
Re-read 8467476 11260914 11248059 11145336 10906486
Reverse Read 6821393 8106334 8282174 8279195 8109186
Stride read 7191093 8994306 9153982 8961224 9004434
Random read 7156353 8957932 9167098 8980465 8940476
Mixed workload 4172747 5680814 5927825 5489578 5972253
Random write 1483044 1605588 1594329 1600453 1596010
Pwrite 1276644 1303108 1311612 1314228 1300960
Pread 4324337 4632869 4618386 4457870 4500166
To enhance the possibility of access the same table[index] concurrently,
set zram a small disksize(10MB) and let threads run with large loop
count.
fio test:
fio --bs=32k --randrepeat=1 --randseed=100 --refill_buffers
--scramble_buffers=1 --direct=1 --loops=3000 --numjobs=4
--filename=/dev/zram0 --name=seq-write --rw=write --stonewall
--name=seq-read --rw=read --stonewall --name=seq-readwrite
--rw=rw --stonewall --name=rand-readwrite --rw=randrw --stonewall
(10MB zram raw block device, take the average of 10 tests, KB/s)
Test base CAS spinlock rwlock bit_spinlock
-------------------------------------------------------------
seq-write 933789 999357 1003298 995961 1001958
seq-read 5634130 6577930 6380861 6243912 6230006
seq-rw 1405687 1638117 1640256 1633903 1634459
rand-rw 1386119 1614664 1617211 1609267 1612471
All the optimization methods show a higher performance than the base,
however, it is hard to say which method is the most appropriate.
On the other hand, zram is mostly used on small embedded system, so we
don't want to increase any memory footprint.
This patch pick the bit_spinlock method, pack object size and page_flag
into an unsigned long table.value, so as to not increase any memory
overhead on both 32-bit and 64-bit system.
On the third hand, even though different kinds of locks have different
performances, we can ignore this difference, because: if zram is used as
zram swapfile, the swap subsystem can prevent concurrent access to the
same swapslot; if zram is used as zram-blk for set up filesystem on it,
the upper filesystem and the page cache also prevent concurrent access
of the same block mostly. So we can ignore the different performances
among locks.
Acked-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
Reviewed-by: Davidlohr Bueso <davidlohr@hp.com>
Signed-off-by: Weijie Yang <weijie.yang@samsung.com>
Signed-off-by: Minchan Kim <minchan@kernel.org>
Cc: Jerome Marchand <jmarchan@redhat.com>
Cc: Nitin Gupta <ngupta@vflare.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2014-08-07 07:08:31 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The lower ZRAM_FLAG_SHIFT bits of table.value is for
|
|
|
|
* object size (excluding header), the higher bits is for
|
|
|
|
* zram_pageflags.
|
|
|
|
*
|
|
|
|
* zram is mainly used for memory efficiency so we want to keep memory
|
|
|
|
* footprint small so we can squeeze size and flags into a field.
|
|
|
|
* The lower ZRAM_FLAG_SHIFT bits is for object size (excluding header),
|
|
|
|
* the higher bits is for zram_pageflags.
|
|
|
|
*/
|
|
|
|
#define ZRAM_FLAG_SHIFT 24
|
|
|
|
|
|
|
|
/* Flags for zram pages (table[page_no].value) */
|
2010-06-01 16:01:25 +08:00
|
|
|
enum zram_pageflags {
|
2009-09-22 12:56:53 +08:00
|
|
|
/* Page consists entirely of zeros */
|
2014-12-13 08:57:04 +08:00
|
|
|
ZRAM_ZERO = ZRAM_FLAG_SHIFT,
|
|
|
|
ZRAM_ACCESS, /* page is now accessed */
|
2009-09-22 12:56:53 +08:00
|
|
|
|
2010-06-01 16:01:25 +08:00
|
|
|
__NR_ZRAM_PAGEFLAGS,
|
2009-09-22 12:56:53 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
/*-- Data structures */
|
|
|
|
|
2010-06-01 16:01:25 +08:00
|
|
|
/* Allocated for each disk page */
|
2014-08-07 07:08:25 +08:00
|
|
|
struct zram_table_entry {
|
2012-06-08 14:39:25 +08:00
|
|
|
unsigned long handle;
|
zram: replace global tb_lock with fine grain lock
Currently, we use a rwlock tb_lock to protect concurrent access to the
whole zram meta table. However, according to the actual access model,
there is only a small chance for upper user to access the same
table[index], so the current lock granularity is too big.
The idea of optimization is to change the lock granularity from whole
meta table to per table entry (table -> table[index]), so that we can
protect concurrent access to the same table[index], meanwhile allow the
maximum concurrency.
With this in mind, several kinds of locks which could be used as a
per-entry lock were tested and compared:
Test environment:
x86-64 Intel Core2 Q8400, system memory 4GB, Ubuntu 12.04,
kernel v3.15.0-rc3 as base, zram with 4 max_comp_streams LZO.
iozone test:
iozone -t 4 -R -r 16K -s 200M -I +Z
(1GB zram with ext4 filesystem, take the average of 10 tests, KB/s)
Test base CAS spinlock rwlock bit_spinlock
-------------------------------------------------------------------
Initial write 1381094 1425435 1422860 1423075 1421521
Rewrite 1529479 1641199 1668762 1672855 1654910
Read 8468009 11324979 11305569 11117273 10997202
Re-read 8467476 11260914 11248059 11145336 10906486
Reverse Read 6821393 8106334 8282174 8279195 8109186
Stride read 7191093 8994306 9153982 8961224 9004434
Random read 7156353 8957932 9167098 8980465 8940476
Mixed workload 4172747 5680814 5927825 5489578 5972253
Random write 1483044 1605588 1594329 1600453 1596010
Pwrite 1276644 1303108 1311612 1314228 1300960
Pread 4324337 4632869 4618386 4457870 4500166
To enhance the possibility of access the same table[index] concurrently,
set zram a small disksize(10MB) and let threads run with large loop
count.
fio test:
fio --bs=32k --randrepeat=1 --randseed=100 --refill_buffers
--scramble_buffers=1 --direct=1 --loops=3000 --numjobs=4
--filename=/dev/zram0 --name=seq-write --rw=write --stonewall
--name=seq-read --rw=read --stonewall --name=seq-readwrite
--rw=rw --stonewall --name=rand-readwrite --rw=randrw --stonewall
(10MB zram raw block device, take the average of 10 tests, KB/s)
Test base CAS spinlock rwlock bit_spinlock
-------------------------------------------------------------
seq-write 933789 999357 1003298 995961 1001958
seq-read 5634130 6577930 6380861 6243912 6230006
seq-rw 1405687 1638117 1640256 1633903 1634459
rand-rw 1386119 1614664 1617211 1609267 1612471
All the optimization methods show a higher performance than the base,
however, it is hard to say which method is the most appropriate.
On the other hand, zram is mostly used on small embedded system, so we
don't want to increase any memory footprint.
This patch pick the bit_spinlock method, pack object size and page_flag
into an unsigned long table.value, so as to not increase any memory
overhead on both 32-bit and 64-bit system.
On the third hand, even though different kinds of locks have different
performances, we can ignore this difference, because: if zram is used as
zram swapfile, the swap subsystem can prevent concurrent access to the
same swapslot; if zram is used as zram-blk for set up filesystem on it,
the upper filesystem and the page cache also prevent concurrent access
of the same block mostly. So we can ignore the different performances
among locks.
Acked-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
Reviewed-by: Davidlohr Bueso <davidlohr@hp.com>
Signed-off-by: Weijie Yang <weijie.yang@samsung.com>
Signed-off-by: Minchan Kim <minchan@kernel.org>
Cc: Jerome Marchand <jmarchan@redhat.com>
Cc: Nitin Gupta <ngupta@vflare.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2014-08-07 07:08:31 +08:00
|
|
|
unsigned long value;
|
|
|
|
};
|
2009-09-22 12:56:53 +08:00
|
|
|
|
2010-06-01 16:01:25 +08:00
|
|
|
struct zram_stats {
|
2014-04-08 06:38:03 +08:00
|
|
|
atomic64_t compr_data_size; /* compressed size of pages stored */
|
2013-06-07 00:07:31 +08:00
|
|
|
atomic64_t num_reads; /* failed + successful */
|
|
|
|
atomic64_t num_writes; /* --do-- */
|
2015-04-16 07:15:36 +08:00
|
|
|
atomic64_t num_migrated; /* no. of migrated object */
|
2014-08-30 06:18:37 +08:00
|
|
|
atomic64_t failed_reads; /* can happen when memory is too low */
|
2013-06-07 00:07:31 +08:00
|
|
|
atomic64_t failed_writes; /* can happen when memory is too low */
|
|
|
|
atomic64_t invalid_io; /* non-page-aligned I/O requests */
|
|
|
|
atomic64_t notify_free; /* no. of swap slot free notifications */
|
2014-04-08 06:38:03 +08:00
|
|
|
atomic64_t zero_pages; /* no. of zero filled pages */
|
|
|
|
atomic64_t pages_stored; /* no. of pages currently stored */
|
2014-10-10 06:29:55 +08:00
|
|
|
atomic_long_t max_used_pages; /* no. of maximum pages stored */
|
2009-09-22 12:56:53 +08:00
|
|
|
};
|
|
|
|
|
2013-02-06 07:48:53 +08:00
|
|
|
struct zram_meta {
|
2014-08-07 07:08:25 +08:00
|
|
|
struct zram_table_entry *table;
|
2013-02-06 07:48:53 +08:00
|
|
|
struct zs_pool *mem_pool;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct zram {
|
|
|
|
struct zram_meta *meta;
|
2015-02-13 07:00:45 +08:00
|
|
|
struct zcomp *comp;
|
2009-09-22 12:56:53 +08:00
|
|
|
struct gendisk *disk;
|
2015-02-13 07:00:45 +08:00
|
|
|
/* Prevent concurrent execution of device init */
|
2011-09-06 21:02:11 +08:00
|
|
|
struct rw_semaphore init_lock;
|
2009-09-22 12:56:53 +08:00
|
|
|
/*
|
2015-02-13 07:00:45 +08:00
|
|
|
* the number of pages zram can consume for storing compressed data
|
2009-09-22 12:56:53 +08:00
|
|
|
*/
|
2015-02-13 07:00:45 +08:00
|
|
|
unsigned long limit_pages;
|
zram: add multi stream functionality
Existing zram (zcomp) implementation has only one compression stream
(buffer and algorithm private part), so in order to prevent data
corruption only one write (compress operation) can use this compression
stream, forcing all concurrent write operations to wait for stream lock
to be released. This patch changes zcomp to keep a compression streams
list of user-defined size (via sysfs device attr). Each write operation
still exclusively holds compression stream, the difference is that we
can have N write operations (depending on size of streams list)
executing in parallel. See TEST section later in commit message for
performance data.
Introduce struct zcomp_strm_multi and a set of functions to manage
zcomp_strm stream access. zcomp_strm_multi has a list of idle
zcomp_strm structs, spinlock to protect idle list and wait queue, making
it possible to perform parallel compressions.
The following set of functions added:
- zcomp_strm_multi_find()/zcomp_strm_multi_release()
find and release a compression stream, implement required locking
- zcomp_strm_multi_create()/zcomp_strm_multi_destroy()
create and destroy zcomp_strm_multi
zcomp ->strm_find() and ->strm_release() callbacks are set during
initialisation to zcomp_strm_multi_find()/zcomp_strm_multi_release()
correspondingly.
Each time zcomp issues a zcomp_strm_multi_find() call, the following set
of operations performed:
- spin lock strm_lock
- if idle list is not empty, remove zcomp_strm from idle list, spin
unlock and return zcomp stream pointer to caller
- if idle list is empty, current adds itself to wait queue. it will be
awaken by zcomp_strm_multi_release() caller.
zcomp_strm_multi_release():
- spin lock strm_lock
- add zcomp stream to idle list
- spin unlock, wake up sleeper
Minchan Kim reported that spinlock-based locking scheme has demonstrated
a severe perfomance regression for single compression stream case,
comparing to mutex-based (see https://lkml.org/lkml/2014/2/18/16)
base spinlock mutex
==Initial write ==Initial write ==Initial write
records: 5 records: 5 records: 5
avg: 1642424.35 avg: 699610.40 avg: 1655583.71
std: 39890.95(2.43%) std: 232014.19(33.16%) std: 52293.96
max: 1690170.94 max: 1163473.45 max: 1697164.75
min: 1568669.52 min: 573429.88 min: 1553410.23
==Rewrite ==Rewrite ==Rewrite
records: 5 records: 5 records: 5
avg: 1611775.39 avg: 501406.64 avg: 1684419.11
std: 17144.58(1.06%) std: 15354.41(3.06%) std: 18367.42
max: 1641800.95 max: 531356.78 max: 1706445.84
min: 1593515.27 min: 488817.78 min: 1655335.73
When only one compression stream available, mutex with spin on owner
tends to perform much better than frequent wait_event()/wake_up(). This
is why single stream implemented as a special case with mutex locking.
Introduce and document zram device attribute max_comp_streams. This
attr shows and stores current zcomp's max number of zcomp streams
(max_strm). Extend zcomp's zcomp_create() with `max_strm' parameter.
`max_strm' limits the number of zcomp_strm structs in compression
backend's idle list (max_comp_streams).
max_comp_streams used during initialisation as follows:
-- passing to zcomp_create() max_strm equals to 1 will initialise zcomp
using single compression stream zcomp_strm_single (mutex-based locking).
-- passing to zcomp_create() max_strm greater than 1 will initialise zcomp
using multi compression stream zcomp_strm_multi (spinlock-based locking).
default max_comp_streams value is 1, meaning that zram with single stream
will be initialised.
Later patch will introduce configuration knob to change max_comp_streams
on already initialised and used zcomp.
TEST
iozone -t 3 -R -r 16K -s 60M -I +Z
test base 1 strm (mutex) 3 strm (spinlock)
-----------------------------------------------------------------------
Initial write 589286.78 583518.39 718011.05
Rewrite 604837.97 596776.38 1515125.72
Random write 584120.11 595714.58 1388850.25
Pwrite 535731.17 541117.38 739295.27
Fwrite 1418083.88 1478612.72 1484927.06
Usage example:
set max_comp_streams to 4
echo 4 > /sys/block/zram0/max_comp_streams
show current max_comp_streams (default value is 1).
cat /sys/block/zram0/max_comp_streams
Signed-off-by: Sergey Senozhatsky <sergey.senozhatsky@gmail.com>
Acked-by: Minchan Kim <minchan@kernel.org>
Cc: Jerome Marchand <jmarchan@redhat.com>
Cc: Nitin Gupta <ngupta@vflare.org>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2014-04-08 06:38:14 +08:00
|
|
|
int max_comp_streams;
|
2015-02-13 07:00:45 +08:00
|
|
|
|
2010-06-01 16:01:25 +08:00
|
|
|
struct zram_stats stats;
|
2015-02-13 07:00:45 +08:00
|
|
|
atomic_t refcount; /* refcount for zram_meta */
|
|
|
|
/* wait all IO under all of cpu are done */
|
|
|
|
wait_queue_head_t io_done;
|
2014-10-10 06:29:53 +08:00
|
|
|
/*
|
2015-02-13 07:00:45 +08:00
|
|
|
* This is the limit on amount of *uncompressed* worth of data
|
|
|
|
* we can store in a disk.
|
2014-10-10 06:29:53 +08:00
|
|
|
*/
|
2015-02-13 07:00:45 +08:00
|
|
|
u64 disksize; /* bytes */
|
2014-04-08 06:38:17 +08:00
|
|
|
char compressor[10];
|
2015-06-26 06:00:21 +08:00
|
|
|
/*
|
|
|
|
* zram is claimed so open request will be failed
|
|
|
|
*/
|
|
|
|
bool claim; /* Protected by bdev->bd_mutex */
|
2009-09-22 12:56:53 +08:00
|
|
|
};
|
2010-01-28 23:43:37 +08:00
|
|
|
#endif
|