Re: [PATCH v3 10/11] zram: use rw_semaphore for decompression streams
From: Sergey Senozhatsky
Date: Thu Oct 15 2026 - 01:19:11 EST
On (26/10/15 12:19), Sergey Senozhatsky wrote:
> 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)
Just a better looking code (but same idea).
---
drivers/block/zram/zcomp.c | 47 +++++++++++++++++++++++++----------
drivers/block/zram/zcomp.h | 2 +-
drivers/block/zram/zram_drv.c | 2 +-
3 files changed, 36 insertions(+), 15 deletions(-)
diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c
index 39d1f9384939..81f21bad787e 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,
@@ -244,6 +243,15 @@ int zcomp_compress(struct zcomp *comp, struct zcomp_cstrm *zstrm,
return ret;
}
+static int zcomp_decompress_buf(struct zcomp *comp, struct zcomp_dstrm *zstrm,
+ struct scatterlist *sg, void *buf,
+ struct zcomp_req *req)
+{
+ sg_copy_to_buffer(sg, 2, buf, req->src_len);
+ req->src = buf;
+ return comp->ops->decompress(comp->params, &zstrm->ctx, req);
+}
+
int zcomp_decompress(struct zcomp *comp, struct zcomp_dstrm *zstrm,
struct scatterlist *sg, unsigned int src_len, void *dst)
{
@@ -252,27 +260,40 @@ int zcomp_decompress(struct zcomp *comp, struct zcomp_dstrm *zstrm,
.src_len = src_len,
.dst_len = PAGE_SIZE,
};
- void *src = NULL;
+ void *src;
int ret;
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;
- } 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;
+ ret = comp->ops->decompress(comp->params, &zstrm->ctx, &req);
+ kunmap_local(src);
+ return ret;
}
- ret = comp->ops->decompress(comp->params, &zstrm->ctx, &req);
- if (src)
- kunmap_local(src);
+ /* exclusive mode already holds ->excl_lock */
+ if (!zcomp_shared_decomp(comp))
+ return zcomp_decompress_buf(comp, zstrm, sg, zstrm->local_copy,
+ &req);
+
+ /* shared mode: try a per-request bounce buffer first */
+ src = kmalloc(src_len, GFP_NOWAIT | __GFP_NOWARN);
+ if (src) {
+ ret = zcomp_decompress_buf(comp, zstrm, sg, src, &req);
+ kfree(src);
+ return ret;
+ }
+
+ /* fall back to ->local_copy serialized by ->excl_lock */
+ mutex_lock(&zstrm->excl_lock);
+ ret = zcomp_decompress_buf(comp, zstrm, sg, zstrm->local_copy, &req);
+ 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);
--
2.56.0.385.gd3acb90ef8-goog