Re: [PATCH v3 10/11] zram: use rw_semaphore for decompression streams

From: Sergey Senozhatsky

Date: Wed Oct 14 2026 - 23:20:14 EST


On (26/10/15 10:41), Sergey Senozhatsky wrote:
> 23.3% is a slightly high bounce reads number, basically almost 1/4 of
> reads are exclusive. I wonder if we can do better. Technically, the
> number of bounce reads depends on the data. Data that are not compressed
> good enough can results in much higher numbers. E.g. if most of the
> objects compress to say 2500 bytes then every second object in the size
> class becomes a spanning object.
>
> I have a quick and dirty patch that turns ->local_copy into a fallback
> slowpath and uses vm_map_ram() for fast path.
>
> A synthetic fio test in VM (reader vs reader inversion test).
>
> BASE (local_copy + excl_lock for spanning objects):
> Run #0: 1,434k IOPS, avg 3.74 us, stdev 443.11 us, p99.99 23.94 us, max 741,374 us (741 ms)
> Run #1: 1,465k IOPS, avg 3.36 us, stdev 99.88 us, p99.99 26.50 us, max 187,415 us (187 ms)
> Run #2: 1,454k IOPS, avg 3.51 us, stdev 171.87 us, p99.99 24.45 us, max 257,715 us (258 ms)
> PATCHED (vm_map_ram() for spanning objects):
> Run #0: 1,471k IOPS, avg 3.33 us, stdev 14.69 us, p99.99 28.29 us, max 2,844 us (2.8 ms)
> Run #1: 1,463k IOPS, avg 3.32 us, stdev 15.93 us, p99.99 24.96 us, max 5,182 us (5.2 ms)
> Run #2: 1,458k IOPS, avg 3.34 us, stdev 14.70 us, p99.99 24.96 us, max 5,619 us (5.6 ms)

Or perhaps we can avoid vm_map/unmap. A slightly different approach.
kmalloc on fast path + memcpy (zsmalloc objects are strictly less than
PAGE_SIZE bytes)

Run #0: 1,475k IOPS, avg 3.26 us, stdev 14.24 us, p99.99 24.45 us, max 5,617 us (5.62 ms)
Run #1: 1,472k IOPS, avg 3.27 us, stdev 15.35 us, p99.99 23.94 us, max 5,619 us (5.62 ms)
Run #2: 1,473k IOPS, avg 3.29 us, stdev 15.37 us, p99.99 24.45 us, max 7,190 us (7.19 ms)

---

diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c
index 39d1f9384939..1838057475f4 100644
--- a/drivers/block/zram/zcomp.c
+++ b/drivers/block/zram/zcomp.c
@@ -181,14 +181,13 @@ void zcomp_cstrm_put(struct zcomp_cstrm *zstrm)
mutex_unlock(&zstrm->lock);
}

-bool zcomp_shared_decomp(struct zcomp *comp, struct scatterlist *sg)
+bool zcomp_shared_decomp(struct zcomp *comp)
{
/*
* Shared decompression is only safe when the backend has no
- * mutable per-stream decompression context and the object is
- * contained within a single page (no ->local_copy bounce needed).
+ * mutable per-stream decompression context.
*/
- return !comp->ops->create_dctx && sg_is_last(sg);
+ return !comp->ops->create_dctx;
}

struct zcomp_dstrm *zcomp_dstrm_get(struct zcomp *comp,
@@ -257,22 +256,37 @@ int zcomp_decompress(struct zcomp *comp, struct zcomp_dstrm *zstrm,

might_sleep();
lockdep_assert_held(&zstrm->lock);
- if (!zcomp_shared_decomp(comp, sg))
+ if (!zcomp_shared_decomp(comp))
lockdep_assert_held(&zstrm->excl_lock);

if (sg_is_last(sg)) {
/* the object is contained within one page, read it in-place */
src = kmap_local_page(sg_page(sg));
req.src = src + sg->offset;
+ ret = comp->ops->decompress(comp->params, &zstrm->ctx, &req);
+ kunmap_local(src);
+ return ret;
+ }
+
+ /* the object spans two pages, linearize it */
+ if (zcomp_shared_decomp(comp)) {
+ src = kmalloc(src_len, GFP_NOWAIT | __GFP_NOWARN);
+ if (!src) {
+ mutex_lock(&zstrm->excl_lock);
+ src = zstrm->local_copy;
+ }
} else {
- /* the object spans two pages, linearize it into local copy */
- sg_copy_to_buffer(sg, 2, zstrm->local_copy, src_len);
- req.src = zstrm->local_copy;
+ src = zstrm->local_copy;
}

+ sg_copy_to_buffer(sg, 2, src, src_len);
+ req.src = src;
ret = comp->ops->decompress(comp->params, &zstrm->ctx, &req);
- if (src)
- kunmap_local(src);
+
+ if (src != zstrm->local_copy)
+ kfree(src);
+ else if (zcomp_shared_decomp(comp))
+ mutex_unlock(&zstrm->excl_lock);
return ret;
}

diff --git a/drivers/block/zram/zcomp.h b/drivers/block/zram/zcomp.h
index 924c4185abe9..6764818d2a9f 100644
--- a/drivers/block/zram/zcomp.h
+++ b/drivers/block/zram/zcomp.h
@@ -115,7 +115,7 @@ void zcomp_destroy(struct zcomp *comp);
struct zcomp_cstrm *zcomp_cstrm_get(struct zcomp *comp);
void zcomp_cstrm_put(struct zcomp_cstrm *zstrm);

-bool zcomp_shared_decomp(struct zcomp *comp, struct scatterlist *sg);
+bool zcomp_shared_decomp(struct zcomp *comp);
struct zcomp_dstrm *zcomp_dstrm_get(struct zcomp *comp,
enum zcomp_dstrm_mode mode);
void zcomp_dstrm_put(struct zcomp_dstrm *zstrm, enum zcomp_dstrm_mode mode);
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index ad244624d4a5..e608769bfad5 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -2128,7 +2128,7 @@ static int read_compressed_page(struct zram *zram, struct page *page,
prio = get_slot_comp_priority(zram, index);

zs_obj_read_sg_begin(zram->mem_pool, handle, sg, size);
- if (zcomp_shared_decomp(zram->comps[prio], sg))
+ if (zcomp_shared_decomp(zram->comps[prio]))
mode = ZCOMP_DSTRM_SHARED;

zstrm = zcomp_dstrm_get(zram->comps[prio], mode);