* [PATCH v2] zram: remove global tb_lock with fine grain lock
@ 2014-05-15 8:00 Weijie Yang
2014-05-15 21:38 ` Andrew Morton
2014-05-20 22:10 ` Andrew Morton
0 siblings, 2 replies; 8+ messages in thread
From: Weijie Yang @ 2014-05-15 8:00 UTC (permalink / raw)
To: 'Minchan Kim'
Cc: 'Andrew Morton', 'Nitin Gupta',
'Sergey Senozhatsky', 'Bob Liu',
'Dan Streetman', 'Weijie Yang',
'Heesub Shin', 'Davidlohr Bueso',
'Joonsoo Kim', 'linux-kernel', 'Linux-MM'
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.
Changes since v1: https://lkml.org/lkml/2014/5/5/1
- replace CAS method with bit_spinlock method
- rename zram_test_flag() to zram_test_zero()
- add some comments
- change the patch subject
Signed-off-by: Weijie Yang <weijie.yang@samsung.com>
---
drivers/block/zram/zram_drv.c | 84 +++++++++++++++++++++++------------------
drivers/block/zram/zram_drv.h | 22 ++++++++---
2 files changed, 63 insertions(+), 43 deletions(-)
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 9849b52..81f2b3e 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -179,23 +179,32 @@ static ssize_t comp_algorithm_store(struct device *dev,
return len;
}
-/* flag operations needs meta->tb_lock */
-static int zram_test_flag(struct zram_meta *meta, u32 index,
- enum zram_pageflags flag)
+static int zram_test_zero(struct zram_meta *meta, u32 index)
{
- return meta->table[index].flags & BIT(flag);
+ return meta->table[index].value & BIT(ZRAM_ZERO);
}
-static void zram_set_flag(struct zram_meta *meta, u32 index,
- enum zram_pageflags flag)
+static void zram_set_zero(struct zram_meta *meta, u32 index)
{
- meta->table[index].flags |= BIT(flag);
+ meta->table[index].value |= BIT(ZRAM_ZERO);
}
-static void zram_clear_flag(struct zram_meta *meta, u32 index,
- enum zram_pageflags flag)
+static void zram_clear_zero(struct zram_meta *meta, u32 index)
{
- meta->table[index].flags &= ~BIT(flag);
+ meta->table[index].value &= ~BIT(ZRAM_ZERO);
+}
+
+static int zram_get_obj_size(struct zram_meta *meta, u32 index)
+{
+ return meta->table[index].value & (BIT(ZRAM_FLAG_SHIFT) - 1);
+}
+
+static void zram_set_obj_size(struct zram_meta *meta,
+ u32 index, int size)
+{
+ meta->table[index].value = (unsigned long)size |
+ ((meta->table[index].value >> ZRAM_FLAG_SHIFT)
+ << ZRAM_FLAG_SHIFT );
}
static inline int is_partial_io(struct bio_vec *bvec)
@@ -255,7 +264,6 @@ static struct zram_meta *zram_meta_alloc(u64 disksize)
goto free_table;
}
- rwlock_init(&meta->tb_lock);
return meta;
free_table:
@@ -304,19 +312,19 @@ static void handle_zero_page(struct bio_vec *bvec)
flush_dcache_page(page);
}
-/* NOTE: caller should hold meta->tb_lock with write-side */
static void zram_free_page(struct zram *zram, size_t index)
{
struct zram_meta *meta = zram->meta;
unsigned long handle = meta->table[index].handle;
+ int size;
if (unlikely(!handle)) {
/*
* No memory is allocated for zero filled pages.
* Simply clear zero page flag.
*/
- if (zram_test_flag(meta, index, ZRAM_ZERO)) {
- zram_clear_flag(meta, index, ZRAM_ZERO);
+ if (zram_test_zero(meta, index)) {
+ zram_clear_zero(meta, index);
atomic64_dec(&zram->stats.zero_pages);
}
return;
@@ -324,27 +332,28 @@ static void zram_free_page(struct zram *zram, size_t index)
zs_free(meta->mem_pool, handle);
- atomic64_sub(meta->table[index].size, &zram->stats.compr_data_size);
+ size = zram_get_obj_size(meta, index);
+ atomic64_sub(size, &zram->stats.compr_data_size);
atomic64_dec(&zram->stats.pages_stored);
meta->table[index].handle = 0;
- meta->table[index].size = 0;
+ zram_set_obj_size(meta, index, 0);
}
static int zram_decompress_page(struct zram *zram, char *mem, u32 index)
{
- int ret = 0;
unsigned char *cmem;
struct zram_meta *meta = zram->meta;
unsigned long handle;
- u16 size;
+ int size;
+ int ret = 0;
- read_lock(&meta->tb_lock);
+ bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value);
handle = meta->table[index].handle;
- size = meta->table[index].size;
+ size = zram_get_obj_size(meta, index);
- if (!handle || zram_test_flag(meta, index, ZRAM_ZERO)) {
- read_unlock(&meta->tb_lock);
+ if (!handle || zram_test_zero(meta, index)) {
+ bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
clear_page(mem);
return 0;
}
@@ -355,7 +364,7 @@ static int zram_decompress_page(struct zram *zram, char *mem, u32 index)
else
ret = zcomp_decompress(zram->comp, cmem, size, mem);
zs_unmap_object(meta->mem_pool, handle);
- read_unlock(&meta->tb_lock);
+ bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
/* Should NEVER happen. Return bio error if it does. */
if (unlikely(ret)) {
@@ -376,14 +385,14 @@ static int zram_bvec_read(struct zram *zram, struct bio_vec *bvec,
struct zram_meta *meta = zram->meta;
page = bvec->bv_page;
- read_lock(&meta->tb_lock);
+ bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value);
if (unlikely(!meta->table[index].handle) ||
- zram_test_flag(meta, index, ZRAM_ZERO)) {
- read_unlock(&meta->tb_lock);
+ zram_test_zero(meta, index)) {
+ bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
handle_zero_page(bvec);
return 0;
}
- read_unlock(&meta->tb_lock);
+ bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
if (is_partial_io(bvec))
/* Use a temporary buffer to decompress the page */
@@ -461,10 +470,10 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
if (page_zero_filled(uncmem)) {
kunmap_atomic(user_mem);
/* Free memory associated with this sector now. */
- write_lock(&zram->meta->tb_lock);
+ bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value);
zram_free_page(zram, index);
- zram_set_flag(meta, index, ZRAM_ZERO);
- write_unlock(&zram->meta->tb_lock);
+ zram_set_zero(meta, index);
+ bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
atomic64_inc(&zram->stats.zero_pages);
ret = 0;
@@ -514,12 +523,12 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec, u32 index,
* Free memory associated with this sector
* before overwriting unused sectors.
*/
- write_lock(&zram->meta->tb_lock);
+ bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value);
zram_free_page(zram, index);
meta->table[index].handle = handle;
- meta->table[index].size = clen;
- write_unlock(&zram->meta->tb_lock);
+ zram_set_obj_size(meta, index, clen);
+ bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
/* Update stats */
atomic64_add(clen, &zram->stats.compr_data_size);
@@ -560,6 +569,7 @@ static void zram_bio_discard(struct zram *zram, u32 index,
int offset, struct bio *bio)
{
size_t n = bio->bi_iter.bi_size;
+ struct zram_meta *meta = zram->meta;
/*
* zram manages data in physical block size units. Because logical block
@@ -584,9 +594,9 @@ static void zram_bio_discard(struct zram *zram, u32 index,
* Discard request can be large so the lock hold times could be
* lengthy. So take the lock once per page.
*/
- write_lock(&zram->meta->tb_lock);
+ bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value);
zram_free_page(zram, index);
- write_unlock(&zram->meta->tb_lock);
+ bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
index++;
n -= PAGE_SIZE;
}
@@ -804,9 +814,9 @@ static void zram_slot_free_notify(struct block_device *bdev,
zram = bdev->bd_disk->private_data;
meta = zram->meta;
- write_lock(&meta->tb_lock);
+ bit_spin_lock(ZRAM_ACCESS, &meta->table[index].value);
zram_free_page(zram, index);
- write_unlock(&meta->tb_lock);
+ bit_spin_unlock(ZRAM_ACCESS, &meta->table[index].value);
atomic64_inc(&zram->stats.notify_free);
}
diff --git a/drivers/block/zram/zram_drv.h b/drivers/block/zram/zram_drv.h
index 7f21c14..caf620e 100644
--- a/drivers/block/zram/zram_drv.h
+++ b/drivers/block/zram/zram_drv.h
@@ -51,10 +51,22 @@ static const size_t max_zpage_size = PAGE_SIZE / 4 * 3;
#define ZRAM_SECTOR_PER_LOGICAL_BLOCK \
(1 << (ZRAM_LOGICAL_BLOCK_SHIFT - SECTOR_SHIFT))
-/* Flags for zram pages (table[page_no].flags) */
+/*
+ * The lower ZRAM_FLAG_SHIFT bits of table.value is for
+ * object size (excluding header), the higher bits is for
+ * zram_pageflags. By this means, it won't increase any
+ * memory overhead on both 32-bit and 64-bit system.
+ * zram is mostly used on small embedded system, so we
+ * don't want to increase memory footprint. That is why
+ * we pack size and flag into table.value.
+ */
+#define ZRAM_FLAG_SHIFT 24
+
+/* Flags for zram pages (table[page_no].value) */
enum zram_pageflags {
/* Page consists entirely of zeros */
- ZRAM_ZERO,
+ ZRAM_ZERO = ZRAM_FLAG_SHIFT + 1,
+ ZRAM_ACCESS, /* page in now accessed */
__NR_ZRAM_PAGEFLAGS,
};
@@ -64,9 +76,8 @@ enum zram_pageflags {
/* Allocated for each disk page */
struct table {
unsigned long handle;
- u16 size; /* object size (excluding header) */
- u8 flags;
-} __aligned(4);
+ unsigned long value;
+};
struct zram_stats {
atomic64_t compr_data_size; /* compressed size of pages stored */
@@ -81,7 +92,6 @@ struct zram_stats {
};
struct zram_meta {
- rwlock_t tb_lock; /* protect table */
struct table *table;
struct zs_pool *mem_pool;
};
--
1.7.10.4
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply related [flat|nested] 8+ messages in thread
* Re: [PATCH v2] zram: remove global tb_lock with fine grain lock
2014-05-15 8:00 [PATCH v2] zram: remove global tb_lock with fine grain lock Weijie Yang
@ 2014-05-15 21:38 ` Andrew Morton
2014-05-16 6:51 ` Minchan Kim
2014-05-20 22:10 ` Andrew Morton
1 sibling, 1 reply; 8+ messages in thread
From: Andrew Morton @ 2014-05-15 21:38 UTC (permalink / raw)
To: Weijie Yang
Cc: 'Minchan Kim', 'Nitin Gupta',
'Sergey Senozhatsky', 'Bob Liu',
'Dan Streetman', 'Weijie Yang',
'Heesub Shin', 'Davidlohr Bueso',
'Joonsoo Kim', 'linux-kernel', 'Linux-MM'
On Thu, 15 May 2014 16:00:47 +0800 Weijie Yang <weijie.yang@samsung.com> wrote:
> 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
Did you investigate seqlocks?
> 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.
bit_spinlocks are not a particularly good or complete mechanism - they
don't have lockdep support and iirc they are somewhat slow.
So we need a pretty good reason to use them. How much memory saving
are we expecting here?
> 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.
So do we need any locking at all?
>
> ....
>
> static void zram_free_page(struct zram *zram, size_t index)
> {
> struct zram_meta *meta = zram->meta;
> unsigned long handle = meta->table[index].handle;
> + int size;
>
> if (unlikely(!handle)) {
> /*
> * No memory is allocated for zero filled pages.
> * Simply clear zero page flag.
> */
> - if (zram_test_flag(meta, index, ZRAM_ZERO)) {
> - zram_clear_flag(meta, index, ZRAM_ZERO);
> + if (zram_test_zero(meta, index)) {
> + zram_clear_zero(meta, index);
> atomic64_dec(&zram->stats.zero_pages);
Having these atomic ops in the alloc/free hotpaths must be costing us?
> }
> return;
>
> ....
>
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] zram: remove global tb_lock with fine grain lock
2014-05-15 21:38 ` Andrew Morton
@ 2014-05-16 6:51 ` Minchan Kim
2014-05-17 1:34 ` Weijie Yang
0 siblings, 1 reply; 8+ messages in thread
From: Minchan Kim @ 2014-05-16 6:51 UTC (permalink / raw)
To: Andrew Morton
Cc: Weijie Yang, 'Nitin Gupta', 'Sergey Senozhatsky',
'Bob Liu', 'Dan Streetman', 'Weijie Yang',
'Heesub Shin', 'Davidlohr Bueso',
'Joonsoo Kim', 'linux-kernel', 'Linux-MM'
Hello Andrew,
On Thu, May 15, 2014 at 02:38:56PM -0700, Andrew Morton wrote:
> On Thu, 15 May 2014 16:00:47 +0800 Weijie Yang <weijie.yang@samsung.com> wrote:
>
> > 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
>
> Did you investigate seqlocks?
>
> > 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.
>
> bit_spinlocks are not a particularly good or complete mechanism - they
> don't have lockdep support and iirc they are somewhat slow.
>
> So we need a pretty good reason to use them. How much memory saving
> are we expecting here?
Actually, the reason would be same with page->flags bit spinlock.
Given that normally people set up swap size two times bigger than
memory, zram table's bloating will be bigger than struct page's one.
>
> > 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.
>
> So do we need any locking at all?
Yes, insane user might want to read/write block device directly while
another user uses it with some FS on the block device so at least, zram
should make sure consistency.
>
> >
> > ....
> >
> > static void zram_free_page(struct zram *zram, size_t index)
> > {
> > struct zram_meta *meta = zram->meta;
> > unsigned long handle = meta->table[index].handle;
> > + int size;
> >
> > if (unlikely(!handle)) {
> > /*
> > * No memory is allocated for zero filled pages.
> > * Simply clear zero page flag.
> > */
> > - if (zram_test_flag(meta, index, ZRAM_ZERO)) {
> > - zram_clear_flag(meta, index, ZRAM_ZERO);
> > + if (zram_test_zero(meta, index)) {
> > + zram_clear_zero(meta, index);
> > atomic64_dec(&zram->stats.zero_pages);
>
> Having these atomic ops in the alloc/free hotpaths must be costing us?
Yeb, maybe but I think it's not a scope of this patch. If it was really
trouble, maybe we could change accouting with percpu.
Thanks.
>
> > }
> > return;
> >
> > ....
> >
>
> --
> To unsubscribe, send a message with 'unsubscribe linux-mm' in
> the body to majordomo@kvack.org. For more info on Linux MM,
> see: http://www.linux-mm.org/ .
> Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
--
Kind regards,
Minchan Kim
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] zram: remove global tb_lock with fine grain lock
2014-05-16 6:51 ` Minchan Kim
@ 2014-05-17 1:34 ` Weijie Yang
0 siblings, 0 replies; 8+ messages in thread
From: Weijie Yang @ 2014-05-17 1:34 UTC (permalink / raw)
To: Minchan Kim, Andrew Morton
Cc: Weijie Yang, Nitin Gupta, Sergey Senozhatsky, Bob Liu,
Dan Streetman, Heesub Shin, Davidlohr Bueso, Joonsoo Kim,
linux-kernel, Linux-MM
On Fri, May 16, 2014 at 2:51 PM, Minchan Kim <minchan@kernel.org> wrote:
> Hello Andrew,
>
> On Thu, May 15, 2014 at 02:38:56PM -0700, Andrew Morton wrote:
>> On Thu, 15 May 2014 16:00:47 +0800 Weijie Yang <weijie.yang@samsung.com> wrote:
>>
>> > 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
>>
>> Did you investigate seqlocks?
>>
Yes, I did. However, I think it is hard the use seqlocks here, no
matter use it as
a meta global lock or a table[index] lock. The main reason is the
writer will free
the handle rather than just change some values.
>> > 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.
>>
>> bit_spinlocks are not a particularly good or complete mechanism - they
>> don't have lockdep support and iirc they are somewhat slow.
>>
>> So we need a pretty good reason to use them. How much memory saving
>> are we expecting here?
>
> Actually, the reason would be same with page->flags bit spinlock.
> Given that normally people set up swap size two times bigger than
> memory, zram table's bloating will be bigger than struct page's one.
>
This data is just for reference: for 1GB zram, CAS will increase about
1MB memory
on 32-bit system, the other locks will increase more especially when we config
DEBUG_SPINLOCK these configs.
Consider the zsmalloc compress ratio (about 1: 4.7 on test), we can save more
memory, it is good news for embedded 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.
>>
>> So do we need any locking at all?
>
> Yes, insane user might want to read/write block device directly while
> another user uses it with some FS on the block device so at least, zram
> should make sure consistency.
>
I agree with Minchan, zram is a general block device, we should consider
the completeness of its logic.
But your question really inspire me, maybe we can modify the frontswap/zswap
system by removing any inner lock, because it is for special purpose and the
upper swap system already have its lock logic.
>>
>> >
>> > ....
>> >
>> > static void zram_free_page(struct zram *zram, size_t index)
>> > {
>> > struct zram_meta *meta = zram->meta;
>> > unsigned long handle = meta->table[index].handle;
>> > + int size;
>> >
>> > if (unlikely(!handle)) {
>> > /*
>> > * No memory is allocated for zero filled pages.
>> > * Simply clear zero page flag.
>> > */
>> > - if (zram_test_flag(meta, index, ZRAM_ZERO)) {
>> > - zram_clear_flag(meta, index, ZRAM_ZERO);
>> > + if (zram_test_zero(meta, index)) {
>> > + zram_clear_zero(meta, index);
>> > atomic64_dec(&zram->stats.zero_pages);
>>
>> Having these atomic ops in the alloc/free hotpaths must be costing us?
>
> Yeb, maybe but I think it's not a scope of this patch. If it was really
> trouble, maybe we could change accouting with percpu.
>
> Thanks.
>
>>
>> > }
>> > return;
>> >
>> > ....
>> >
>>
>> --
>> To unsubscribe, send a message with 'unsubscribe linux-mm' in
>> the body to majordomo@kvack.org. For more info on Linux MM,
>> see: http://www.linux-mm.org/ .
>> Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
>
> --
> Kind regards,
> Minchan Kim
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] zram: remove global tb_lock with fine grain lock
2014-05-15 8:00 [PATCH v2] zram: remove global tb_lock with fine grain lock Weijie Yang
2014-05-15 21:38 ` Andrew Morton
@ 2014-05-20 22:10 ` Andrew Morton
2014-05-21 7:51 ` Minchan Kim
1 sibling, 1 reply; 8+ messages in thread
From: Andrew Morton @ 2014-05-20 22:10 UTC (permalink / raw)
To: Weijie Yang
Cc: 'Minchan Kim', 'Nitin Gupta',
'Sergey Senozhatsky', 'Bob Liu',
'Dan Streetman', 'Weijie Yang',
'Heesub Shin', 'Davidlohr Bueso',
'Joonsoo Kim', 'linux-kernel', 'Linux-MM'
On Thu, 15 May 2014 16:00:47 +0800 Weijie Yang <weijie.yang@samsung.com> wrote:
> 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:
>
> ...
>
> --- a/drivers/block/zram/zram_drv.c
> +++ b/drivers/block/zram/zram_drv.c
> @@ -179,23 +179,32 @@ static ssize_t comp_algorithm_store(struct device *dev,
> return len;
> }
>
> -/* flag operations needs meta->tb_lock */
> -static int zram_test_flag(struct zram_meta *meta, u32 index,
> - enum zram_pageflags flag)
> +static int zram_test_zero(struct zram_meta *meta, u32 index)
> {
> - return meta->table[index].flags & BIT(flag);
> + return meta->table[index].value & BIT(ZRAM_ZERO);
> }
>
> -static void zram_set_flag(struct zram_meta *meta, u32 index,
> - enum zram_pageflags flag)
> +static void zram_set_zero(struct zram_meta *meta, u32 index)
> {
> - meta->table[index].flags |= BIT(flag);
> + meta->table[index].value |= BIT(ZRAM_ZERO);
> }
>
> -static void zram_clear_flag(struct zram_meta *meta, u32 index,
> - enum zram_pageflags flag)
> +static void zram_clear_zero(struct zram_meta *meta, u32 index)
> {
> - meta->table[index].flags &= ~BIT(flag);
> + meta->table[index].value &= ~BIT(ZRAM_ZERO);
> +}
> +
> +static int zram_get_obj_size(struct zram_meta *meta, u32 index)
> +{
> + return meta->table[index].value & (BIT(ZRAM_FLAG_SHIFT) - 1);
> +}
> +
> +static void zram_set_obj_size(struct zram_meta *meta,
> + u32 index, int size)
> +{
> + meta->table[index].value = (unsigned long)size |
> + ((meta->table[index].value >> ZRAM_FLAG_SHIFT)
> + << ZRAM_FLAG_SHIFT );
> }
Let's sort out the types here? It makes no sense for `size' to be
signed. And I don't think we need *any* 64-bit quantities here
(discussed below).
So I think we can make `size' a u32 and remove that typecast.
Also, please use checkpatch ;)
> static inline int is_partial_io(struct bio_vec *bvec)
> @@ -255,7 +264,6 @@ static struct zram_meta *zram_meta_alloc(u64 disksize)
> goto free_table;
> }
>
> - rwlock_init(&meta->tb_lock);
> return meta;
>
> free_table:
> @@ -304,19 +312,19 @@ static void handle_zero_page(struct bio_vec *bvec)
> flush_dcache_page(page);
> }
>
> -/* NOTE: caller should hold meta->tb_lock with write-side */
Can we please update this important comment rather than simply deleting
it?
> static void zram_free_page(struct zram *zram, size_t index)
> {
> struct zram_meta *meta = zram->meta;
> unsigned long handle = meta->table[index].handle;
> + int size;
>
> if (unlikely(!handle)) {
> /*
> * No memory is allocated for zero filled pages.
> * Simply clear zero page flag.
> */
> - if (zram_test_flag(meta, index, ZRAM_ZERO)) {
> - zram_clear_flag(meta, index, ZRAM_ZERO);
> + if (zram_test_zero(meta, index)) {
> + zram_clear_zero(meta, index);
> atomic64_dec(&zram->stats.zero_pages);
> }
> return;
>
> ...
>
> @@ -64,9 +76,8 @@ enum zram_pageflags {
> /* Allocated for each disk page */
> struct table {
> unsigned long handle;
> - u16 size; /* object size (excluding header) */
> - u8 flags;
> -} __aligned(4);
> + unsigned long value;
> +};
Does `value' need to be 64 bit on 64-bit machines? I think u32 will be
sufficient? The struct will still be 16 bytes but if we then play
around adding __packed to this structure we should be able to shrink it
to 12 bytes, save large amounts of memory?
And does `handle' need to be 64-bit on 64-bit?
Problem is, if we make optimisations such as this we will smash head-on
into the bit_spin_lock() requirement that it operate on a ulong*.
Which is due to the bitops requiring a ulong*. How irritating.
um, something like
union table { /* Should be called table_entry */
unsigned long ul;
struct {
u32 size_and_flags;
u32 handle;
} s;
};
That's a 64-bit structure containing 32-bit handle and 8-bit flags and
24-bit size.
I'm tempted to use bitfields here but that could get messy as we handle
endianness.
static void zram_table_lock(union table *table)
{
#ifdef __LITTLE_ENDIAN
bit_spin_lock(ZRAM_ACCESS, &t->ul);
#else
#ifdef CONFIG_64BIT
bit_spin_lock(ZRAM_ACCESS ^ (3 << 3), &t->ul);
#else
bit_spin_lock(ZRAM_ACCESS ^ (7 << 3), &t->ul);
#endif
#endif
}
Or something like that ;) And I don't know if it's correct to use
32-bit handle on 64-bit.
But you get the idea. It's worth spending time over this because the
space savings will be quite large.
> struct zram_stats {
> atomic64_t compr_data_size; /* compressed size of pages stored */
>
> ...
>
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] zram: remove global tb_lock with fine grain lock
2014-05-20 22:10 ` Andrew Morton
@ 2014-05-21 7:51 ` Minchan Kim
2014-05-26 7:55 ` Weijie Yang
0 siblings, 1 reply; 8+ messages in thread
From: Minchan Kim @ 2014-05-21 7:51 UTC (permalink / raw)
To: Andrew Morton
Cc: Weijie Yang, 'Nitin Gupta', 'Sergey Senozhatsky',
'Bob Liu', 'Dan Streetman', 'Weijie Yang',
'Heesub Shin', 'Davidlohr Bueso',
'Joonsoo Kim', 'linux-kernel', 'Linux-MM'
Hello Andrew,
On Tue, May 20, 2014 at 03:10:51PM -0700, Andrew Morton wrote:
> On Thu, 15 May 2014 16:00:47 +0800 Weijie Yang <weijie.yang@samsung.com> wrote:
>
> > 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:
> >
> > ...
> >
> > --- a/drivers/block/zram/zram_drv.c
> > +++ b/drivers/block/zram/zram_drv.c
> > @@ -179,23 +179,32 @@ static ssize_t comp_algorithm_store(struct device *dev,
> > return len;
> > }
> >
> > -/* flag operations needs meta->tb_lock */
> > -static int zram_test_flag(struct zram_meta *meta, u32 index,
> > - enum zram_pageflags flag)
> > +static int zram_test_zero(struct zram_meta *meta, u32 index)
> > {
> > - return meta->table[index].flags & BIT(flag);
> > + return meta->table[index].value & BIT(ZRAM_ZERO);
> > }
> >
> > -static void zram_set_flag(struct zram_meta *meta, u32 index,
> > - enum zram_pageflags flag)
> > +static void zram_set_zero(struct zram_meta *meta, u32 index)
> > {
> > - meta->table[index].flags |= BIT(flag);
> > + meta->table[index].value |= BIT(ZRAM_ZERO);
> > }
> >
> > -static void zram_clear_flag(struct zram_meta *meta, u32 index,
> > - enum zram_pageflags flag)
> > +static void zram_clear_zero(struct zram_meta *meta, u32 index)
> > {
> > - meta->table[index].flags &= ~BIT(flag);
> > + meta->table[index].value &= ~BIT(ZRAM_ZERO);
> > +}
> > +
> > +static int zram_get_obj_size(struct zram_meta *meta, u32 index)
> > +{
> > + return meta->table[index].value & (BIT(ZRAM_FLAG_SHIFT) - 1);
> > +}
> > +
> > +static void zram_set_obj_size(struct zram_meta *meta,
> > + u32 index, int size)
> > +{
> > + meta->table[index].value = (unsigned long)size |
> > + ((meta->table[index].value >> ZRAM_FLAG_SHIFT)
> > + << ZRAM_FLAG_SHIFT );
> > }
>
> Let's sort out the types here? It makes no sense for `size' to be
> signed. And I don't think we need *any* 64-bit quantities here
> (discussed below).
>
> So I think we can make `size' a u32 and remove that typecast.
>
> Also, please use checkpatch ;)
>
> > static inline int is_partial_io(struct bio_vec *bvec)
> > @@ -255,7 +264,6 @@ static struct zram_meta *zram_meta_alloc(u64 disksize)
> > goto free_table;
> > }
> >
> > - rwlock_init(&meta->tb_lock);
> > return meta;
> >
> > free_table:
> > @@ -304,19 +312,19 @@ static void handle_zero_page(struct bio_vec *bvec)
> > flush_dcache_page(page);
> > }
> >
> > -/* NOTE: caller should hold meta->tb_lock with write-side */
>
> Can we please update this important comment rather than simply deleting
> it?
>
> > static void zram_free_page(struct zram *zram, size_t index)
> > {
> > struct zram_meta *meta = zram->meta;
> > unsigned long handle = meta->table[index].handle;
> > + int size;
> >
> > if (unlikely(!handle)) {
> > /*
> > * No memory is allocated for zero filled pages.
> > * Simply clear zero page flag.
> > */
> > - if (zram_test_flag(meta, index, ZRAM_ZERO)) {
> > - zram_clear_flag(meta, index, ZRAM_ZERO);
> > + if (zram_test_zero(meta, index)) {
> > + zram_clear_zero(meta, index);
> > atomic64_dec(&zram->stats.zero_pages);
> > }
> > return;
> >
> > ...
> >
> > @@ -64,9 +76,8 @@ enum zram_pageflags {
> > /* Allocated for each disk page */
> > struct table {
> > unsigned long handle;
> > - u16 size; /* object size (excluding header) */
> > - u8 flags;
> > -} __aligned(4);
> > + unsigned long value;
> > +};
>
> Does `value' need to be 64 bit on 64-bit machines? I think u32 will be
> sufficient? The struct will still be 16 bytes but if we then play
> around adding __packed to this structure we should be able to shrink it
> to 12 bytes, save large amounts of memory?
>
> And does `handle' need to be 64-bit on 64-bit?
To me, it's a buggy. We should not have used (unsigned long) as zsmalloc's
handle from the beginning. Sometime it might be bigger than sizeof(unsigned long)
because zsmalloc's handle consists of (pfn, obj idx) so pfn itself is already
unsigned long but more practically, if we consider MAX_PHYSMEM_BITS of arch
and zsmalloc's min size class we have some room for obj_idx which is offset
from each pages(I think that's why it isn't a problem for CONFIG_X86_32 PAE)
but MAX_PHYSMEM_BITS is really arch dependent thing and zsmalloc's class size
could be changed in future so we can't make sure in (exisiting/upcoming)
all architecture, (MAX_PHYSMEM_BITS + bit for obj_idx) is less than
unsigned long. So we should use zs_handle rather than unsigned log and
zs_handle's size shouldn't expose to user. :(
So, I'm fine with Weijie's patch other than naming Andrew pointed out.
I like size_and_flags. :)
>
>
> Problem is, if we make optimisations such as this we will smash head-on
> into the bit_spin_lock() requirement that it operate on a ulong*.
> Which is due to the bitops requiring a ulong*. How irritating.
>
>
> um, something like
>
> union table { /* Should be called table_entry */
> unsigned long ul;
> struct {
> u32 size_and_flags;
> u32 handle;
> } s;
> };
>
> That's a 64-bit structure containing 32-bit handle and 8-bit flags and
> 24-bit size.
>
> I'm tempted to use bitfields here but that could get messy as we handle
> endianness.
>
> static void zram_table_lock(union table *table)
> {
> #ifdef __LITTLE_ENDIAN
> bit_spin_lock(ZRAM_ACCESS, &t->ul);
> #else
> #ifdef CONFIG_64BIT
> bit_spin_lock(ZRAM_ACCESS ^ (3 << 3), &t->ul);
> #else
> bit_spin_lock(ZRAM_ACCESS ^ (7 << 3), &t->ul);
> #endif
> #endif
> }
>
> Or something like that ;) And I don't know if it's correct to use
> 32-bit handle on 64-bit.
>
> But you get the idea. It's worth spending time over this because the
> space savings will be quite large.
>
> > struct zram_stats {
> > atomic64_t compr_data_size; /* compressed size of pages stored */
> >
> > ...
> >
>
> --
> To unsubscribe, send a message with 'unsubscribe linux-mm' in
> the body to majordomo@kvack.org. For more info on Linux MM,
> see: http://www.linux-mm.org/ .
> Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
--
Kind regards,
Minchan Kim
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] zram: remove global tb_lock with fine grain lock
2014-05-21 7:51 ` Minchan Kim
@ 2014-05-26 7:55 ` Weijie Yang
2014-05-30 1:09 ` Minchan Kim
0 siblings, 1 reply; 8+ messages in thread
From: Weijie Yang @ 2014-05-26 7:55 UTC (permalink / raw)
To: Minchan Kim, Andrew Morton
Cc: Weijie Yang, Nitin Gupta, Sergey Senozhatsky, Bob Liu,
Dan Streetman, Heesub Shin, Davidlohr Bueso, Joonsoo Kim,
linux-kernel, Linux-MM
Hello,
Sorry for my late reply, because of a biz trip.
On Wed, May 21, 2014 at 3:51 PM, Minchan Kim <minchan@kernel.org> wrote:
> Hello Andrew,
>
> On Tue, May 20, 2014 at 03:10:51PM -0700, Andrew Morton wrote:
>> On Thu, 15 May 2014 16:00:47 +0800 Weijie Yang <weijie.yang@samsung.com> wrote:
>>
>> > 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:
>> >
>> > ...
>> >
>> > --- a/drivers/block/zram/zram_drv.c
>> > +++ b/drivers/block/zram/zram_drv.c
>> > @@ -179,23 +179,32 @@ static ssize_t comp_algorithm_store(struct device *dev,
>> > return len;
>> > }
>> >
>> > -/* flag operations needs meta->tb_lock */
>> > -static int zram_test_flag(struct zram_meta *meta, u32 index,
>> > - enum zram_pageflags flag)
>> > +static int zram_test_zero(struct zram_meta *meta, u32 index)
>> > {
>> > - return meta->table[index].flags & BIT(flag);
>> > + return meta->table[index].value & BIT(ZRAM_ZERO);
>> > }
>> >
>> > -static void zram_set_flag(struct zram_meta *meta, u32 index,
>> > - enum zram_pageflags flag)
>> > +static void zram_set_zero(struct zram_meta *meta, u32 index)
>> > {
>> > - meta->table[index].flags |= BIT(flag);
>> > + meta->table[index].value |= BIT(ZRAM_ZERO);
>> > }
>> >
>> > -static void zram_clear_flag(struct zram_meta *meta, u32 index,
>> > - enum zram_pageflags flag)
>> > +static void zram_clear_zero(struct zram_meta *meta, u32 index)
>> > {
>> > - meta->table[index].flags &= ~BIT(flag);
>> > + meta->table[index].value &= ~BIT(ZRAM_ZERO);
>> > +}
>> > +
>> > +static int zram_get_obj_size(struct zram_meta *meta, u32 index)
>> > +{
>> > + return meta->table[index].value & (BIT(ZRAM_FLAG_SHIFT) - 1);
>> > +}
>> > +
>> > +static void zram_set_obj_size(struct zram_meta *meta,
>> > + u32 index, int size)
>> > +{
>> > + meta->table[index].value = (unsigned long)size |
>> > + ((meta->table[index].value >> ZRAM_FLAG_SHIFT)
>> > + << ZRAM_FLAG_SHIFT );
>> > }
>>
>> Let's sort out the types here? It makes no sense for `size' to be
>> signed. And I don't think we need *any* 64-bit quantities here
>> (discussed below).
>>
>> So I think we can make `size' a u32 and remove that typecast.
>>
>> Also, please use checkpatch ;)
>>
I will remove the typecast and do checkpatch in the next patch version.
>> > static inline int is_partial_io(struct bio_vec *bvec)
>> > @@ -255,7 +264,6 @@ static struct zram_meta *zram_meta_alloc(u64 disksize)
>> > goto free_table;
>> > }
>> >
>> > - rwlock_init(&meta->tb_lock);
>> > return meta;
>> >
>> > free_table:
>> > @@ -304,19 +312,19 @@ static void handle_zero_page(struct bio_vec *bvec)
>> > flush_dcache_page(page);
>> > }
>> >
>> > -/* NOTE: caller should hold meta->tb_lock with write-side */
>>
>> Can we please update this important comment rather than simply deleting
>> it?
>>
Of couse, I will update it.
>> > static void zram_free_page(struct zram *zram, size_t index)
>> > {
>> > struct zram_meta *meta = zram->meta;
>> > unsigned long handle = meta->table[index].handle;
>> > + int size;
>> >
>> > if (unlikely(!handle)) {
>> > /*
>> > * No memory is allocated for zero filled pages.
>> > * Simply clear zero page flag.
>> > */
>> > - if (zram_test_flag(meta, index, ZRAM_ZERO)) {
>> > - zram_clear_flag(meta, index, ZRAM_ZERO);
>> > + if (zram_test_zero(meta, index)) {
>> > + zram_clear_zero(meta, index);
>> > atomic64_dec(&zram->stats.zero_pages);
>> > }
>> > return;
>> >
>> > ...
>> >
>> > @@ -64,9 +76,8 @@ enum zram_pageflags {
>> > /* Allocated for each disk page */
>> > struct table {
>> > unsigned long handle;
>> > - u16 size; /* object size (excluding header) */
>> > - u8 flags;
>> > -} __aligned(4);
>> > + unsigned long value;
>> > +};
>>
>> Does `value' need to be 64 bit on 64-bit machines? I think u32 will be
>> sufficient? The struct will still be 16 bytes but if we then play
>> around adding __packed to this structure we should be able to shrink it
>> to 12 bytes, save large amounts of memory?
>>
I agree that u32 is sufficient to value(size and flags), the reason I choice
unsigned long is as you said bit_spin_lock() requires a ulong *.
>> And does `handle' need to be 64-bit on 64-bit?
>
> To me, it's a buggy. We should not have used (unsigned long) as zsmalloc's
> handle from the beginning. Sometime it might be bigger than sizeof(unsigned long)
> because zsmalloc's handle consists of (pfn, obj idx) so pfn itself is already
> unsigned long but more practically, if we consider MAX_PHYSMEM_BITS of arch
> and zsmalloc's min size class we have some room for obj_idx which is offset
> from each pages(I think that's why it isn't a problem for CONFIG_X86_32 PAE)
> but MAX_PHYSMEM_BITS is really arch dependent thing and zsmalloc's class size
> could be changed in future so we can't make sure in (exisiting/upcoming)
> all architecture, (MAX_PHYSMEM_BITS + bit for obj_idx) is less than
> unsigned long. So we should use zs_handle rather than unsigned log and
> zs_handle's size shouldn't expose to user. :(
>
> So, I'm fine with Weijie's patch other than naming Andrew pointed out.
> I like size_and_flags. :)
>
Andrew proposed a pack idea to save more memory, when I go through it,
I think I am not convinced to use it, because:
1. it doesn't help on 32-bit system, while most embedded system are 32-bit.
2. it make code messy and unreadable.
3. it will help on 64-bit system only if "handle" can be 32-bit, but I
am not sure it.
Minchan said it's better to hide "handle" size to user, if it becomes
true, it will
be more messy for the upper pack code.
So, I like to insist this v2 patch design on the table entry.
>>
>>
>> Problem is, if we make optimisations such as this we will smash head-on
>> into the bit_spin_lock() requirement that it operate on a ulong*.
>> Which is due to the bitops requiring a ulong*. How irritating.
>>
>>
>> um, something like
>>
>> union table { /* Should be called table_entry */
>> unsigned long ul;
>> struct {
>> u32 size_and_flags;
>> u32 handle;
>> } s;
>> };
>>
>> That's a 64-bit structure containing 32-bit handle and 8-bit flags and
>> 24-bit size.
>>
>> I'm tempted to use bitfields here but that could get messy as we handle
>> endianness.
>>
>> static void zram_table_lock(union table *table)
>> {
>> #ifdef __LITTLE_ENDIAN
>> bit_spin_lock(ZRAM_ACCESS, &t->ul);
>> #else
>> #ifdef CONFIG_64BIT
>> bit_spin_lock(ZRAM_ACCESS ^ (3 << 3), &t->ul);
>> #else
>> bit_spin_lock(ZRAM_ACCESS ^ (7 << 3), &t->ul);
>> #endif
>> #endif
>> }
>>
>> Or something like that ;) And I don't know if it's correct to use
>> 32-bit handle on 64-bit.
>>
>> But you get the idea. It's worth spending time over this because the
>> space savings will be quite large.
>>
>> > struct zram_stats {
>> > atomic64_t compr_data_size; /* compressed size of pages stored */
>> >
>> > ...
>> >
>>
>> --
>> To unsubscribe, send a message with 'unsubscribe linux-mm' in
>> the body to majordomo@kvack.org. For more info on Linux MM,
>> see: http://www.linux-mm.org/ .
>> Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
>
> --
> Kind regards,
> Minchan Kim
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 8+ messages in thread
* Re: [PATCH v2] zram: remove global tb_lock with fine grain lock
2014-05-26 7:55 ` Weijie Yang
@ 2014-05-30 1:09 ` Minchan Kim
0 siblings, 0 replies; 8+ messages in thread
From: Minchan Kim @ 2014-05-30 1:09 UTC (permalink / raw)
To: Weijie Yang
Cc: Andrew Morton, Weijie Yang, Nitin Gupta, Sergey Senozhatsky,
Bob Liu, Dan Streetman, Heesub Shin, Davidlohr Bueso, Joonsoo Kim,
linux-kernel, Linux-MM
On Mon, May 26, 2014 at 03:55:27PM +0800, Weijie Yang wrote:
> Hello,
>
> Sorry for my late reply, because of a biz trip.
>
> On Wed, May 21, 2014 at 3:51 PM, Minchan Kim <minchan@kernel.org> wrote:
> > Hello Andrew,
> >
> > On Tue, May 20, 2014 at 03:10:51PM -0700, Andrew Morton wrote:
> >> On Thu, 15 May 2014 16:00:47 +0800 Weijie Yang <weijie.yang@samsung.com> wrote:
> >>
> >> > 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:
> >> >
> >> > ...
> >> >
> >> > --- a/drivers/block/zram/zram_drv.c
> >> > +++ b/drivers/block/zram/zram_drv.c
> >> > @@ -179,23 +179,32 @@ static ssize_t comp_algorithm_store(struct device *dev,
> >> > return len;
> >> > }
> >> >
> >> > -/* flag operations needs meta->tb_lock */
> >> > -static int zram_test_flag(struct zram_meta *meta, u32 index,
> >> > - enum zram_pageflags flag)
> >> > +static int zram_test_zero(struct zram_meta *meta, u32 index)
> >> > {
> >> > - return meta->table[index].flags & BIT(flag);
> >> > + return meta->table[index].value & BIT(ZRAM_ZERO);
> >> > }
> >> >
> >> > -static void zram_set_flag(struct zram_meta *meta, u32 index,
> >> > - enum zram_pageflags flag)
> >> > +static void zram_set_zero(struct zram_meta *meta, u32 index)
> >> > {
> >> > - meta->table[index].flags |= BIT(flag);
> >> > + meta->table[index].value |= BIT(ZRAM_ZERO);
> >> > }
> >> >
> >> > -static void zram_clear_flag(struct zram_meta *meta, u32 index,
> >> > - enum zram_pageflags flag)
> >> > +static void zram_clear_zero(struct zram_meta *meta, u32 index)
> >> > {
> >> > - meta->table[index].flags &= ~BIT(flag);
> >> > + meta->table[index].value &= ~BIT(ZRAM_ZERO);
> >> > +}
> >> > +
> >> > +static int zram_get_obj_size(struct zram_meta *meta, u32 index)
> >> > +{
> >> > + return meta->table[index].value & (BIT(ZRAM_FLAG_SHIFT) - 1);
> >> > +}
> >> > +
> >> > +static void zram_set_obj_size(struct zram_meta *meta,
> >> > + u32 index, int size)
> >> > +{
> >> > + meta->table[index].value = (unsigned long)size |
> >> > + ((meta->table[index].value >> ZRAM_FLAG_SHIFT)
> >> > + << ZRAM_FLAG_SHIFT );
> >> > }
> >>
> >> Let's sort out the types here? It makes no sense for `size' to be
> >> signed. And I don't think we need *any* 64-bit quantities here
> >> (discussed below).
> >>
> >> So I think we can make `size' a u32 and remove that typecast.
> >>
> >> Also, please use checkpatch ;)
> >>
>
> I will remove the typecast and do checkpatch in the next patch version.
>
> >> > static inline int is_partial_io(struct bio_vec *bvec)
> >> > @@ -255,7 +264,6 @@ static struct zram_meta *zram_meta_alloc(u64 disksize)
> >> > goto free_table;
> >> > }
> >> >
> >> > - rwlock_init(&meta->tb_lock);
> >> > return meta;
> >> >
> >> > free_table:
> >> > @@ -304,19 +312,19 @@ static void handle_zero_page(struct bio_vec *bvec)
> >> > flush_dcache_page(page);
> >> > }
> >> >
> >> > -/* NOTE: caller should hold meta->tb_lock with write-side */
> >>
> >> Can we please update this important comment rather than simply deleting
> >> it?
> >>
>
> Of couse, I will update it.
>
> >> > static void zram_free_page(struct zram *zram, size_t index)
> >> > {
> >> > struct zram_meta *meta = zram->meta;
> >> > unsigned long handle = meta->table[index].handle;
> >> > + int size;
> >> >
> >> > if (unlikely(!handle)) {
> >> > /*
> >> > * No memory is allocated for zero filled pages.
> >> > * Simply clear zero page flag.
> >> > */
> >> > - if (zram_test_flag(meta, index, ZRAM_ZERO)) {
> >> > - zram_clear_flag(meta, index, ZRAM_ZERO);
> >> > + if (zram_test_zero(meta, index)) {
> >> > + zram_clear_zero(meta, index);
> >> > atomic64_dec(&zram->stats.zero_pages);
> >> > }
> >> > return;
> >> >
> >> > ...
> >> >
> >> > @@ -64,9 +76,8 @@ enum zram_pageflags {
> >> > /* Allocated for each disk page */
> >> > struct table {
> >> > unsigned long handle;
> >> > - u16 size; /* object size (excluding header) */
> >> > - u8 flags;
> >> > -} __aligned(4);
> >> > + unsigned long value;
> >> > +};
> >>
> >> Does `value' need to be 64 bit on 64-bit machines? I think u32 will be
> >> sufficient? The struct will still be 16 bytes but if we then play
> >> around adding __packed to this structure we should be able to shrink it
> >> to 12 bytes, save large amounts of memory?
> >>
>
> I agree that u32 is sufficient to value(size and flags), the reason I choice
> unsigned long is as you said bit_spin_lock() requires a ulong *.
>
> >> And does `handle' need to be 64-bit on 64-bit?
> >
> > To me, it's a buggy. We should not have used (unsigned long) as zsmalloc's
> > handle from the beginning. Sometime it might be bigger than sizeof(unsigned long)
> > because zsmalloc's handle consists of (pfn, obj idx) so pfn itself is already
> > unsigned long but more practically, if we consider MAX_PHYSMEM_BITS of arch
> > and zsmalloc's min size class we have some room for obj_idx which is offset
> > from each pages(I think that's why it isn't a problem for CONFIG_X86_32 PAE)
> > but MAX_PHYSMEM_BITS is really arch dependent thing and zsmalloc's class size
> > could be changed in future so we can't make sure in (exisiting/upcoming)
> > all architecture, (MAX_PHYSMEM_BITS + bit for obj_idx) is less than
> > unsigned long. So we should use zs_handle rather than unsigned log and
> > zs_handle's size shouldn't expose to user. :(
> >
> > So, I'm fine with Weijie's patch other than naming Andrew pointed out.
> > I like size_and_flags. :)
> >
>
> Andrew proposed a pack idea to save more memory, when I go through it,
> I think I am not convinced to use it, because:
> 1. it doesn't help on 32-bit system, while most embedded system are 32-bit.
> 2. it make code messy and unreadable.
> 3. it will help on 64-bit system only if "handle" can be 32-bit, but I
> am not sure it.
>
> Minchan said it's better to hide "handle" size to user, if it becomes
> true, it will
> be more messy for the upper pack code.
>
> So, I like to insist this v2 patch design on the table entry.
Hello Weijie,
Could you resend?
--
Kind regards,
Minchan Kim
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in
the body to majordomo@kvack.org. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"dont@kvack.org"> email@kvack.org </a>
^ permalink raw reply [flat|nested] 8+ messages in thread
end of thread, other threads:[~2014-05-30 1:08 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-05-15 8:00 [PATCH v2] zram: remove global tb_lock with fine grain lock Weijie Yang
2014-05-15 21:38 ` Andrew Morton
2014-05-16 6:51 ` Minchan Kim
2014-05-17 1:34 ` Weijie Yang
2014-05-20 22:10 ` Andrew Morton
2014-05-21 7:51 ` Minchan Kim
2014-05-26 7:55 ` Weijie Yang
2014-05-30 1:09 ` Minchan Kim
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).