mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH RFC 0/3] zram: Allow zcomps to manage their own streams
@ 2026-02-04 13:48 Jihan LIN via B4 Relay
  2026-02-04 13:48 ` [PATCH RFC 1/3] zram: Rename zcomp_strm_{init, free}() Jihan LIN via B4 Relay
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Jihan LIN via B4 Relay @ 2026-02-04 13:48 UTC (permalink / raw)
  To: Minchan Kim, Sergey Senozhatsky, Jens Axboe
  Cc: linux-kernel, linux-block, Jihan LIN

Hi all,

This RFC series introduces a new interface to allow zram compression
backends to manage their own streams, in addition to the existing
per-CPU stream model.

Current zram manages compression contexts via preemptive per-CPU
streams, which strictly limits concurrency to the number of online CPUs. 
In contrast, hardware accelerators specialized for page compression
generally process PAGE_SIZE payloads (e.g. 4K) using standard
algorithms. These devices expose the limitations of this model due to
the following features:
 - These devices utilize a hardware queue to batch requests. A typical
   queue depth (e.g., 256) far exceeds the number of available CPUs.
 - These devices are asymmetric. Submission is generally fast and
   asynchronous, but completion implies latency.
 - Some devices only support compression requests, leaving decompression
   to be handled by software.

This exposes the limitations of the current zcomp architecture, which
assumes a model where streams are inherently tied to CPU execution
contexts. This design is not flexible enough to integrate with such
backends, as it forces a one-size-fits-all model that limits the ability
to offload compression operations from CPU.

This series proposes a hybrid approach. While maintaining full backward
compatibility with existing backends, this series introduces a new set
of operations, op->{get, put}_streams(), for backends that wish to
manage their own streams. This allows the backend to handle contentions
internally and dynamically select an execution path for the acquired
streams. A new flag is also introduced to indicate this capability at
runtime. zram_write_page() now prefers streams managed by the backend if
 a bio is considered asynchronous.

Some design decisions as follows.
1. The proposed get_stream() does not take gfp_t flags to keep the
   interface minimal. By design, backends are fully responsible for
   allocation safety.
2. The default per-cpu streams now also imply synchronous path for the
   backends.
3. The recompression path currently relies on the default per-cpu
   streams. This is a trade-off, since recompression is primarily for
   memory saving, and hardware accelerators typically prioritize
   throughput over compression ratio.
4. zstrm->lock is restricted to the default per-cpu streams. Backends
   must implement internal locking if required. While currently exposed
   in struct zstrm, this mutex is an implementation detail of the
   default path. Future work may involve making the default stream
   locking mechanism opaque to the backends, ensuring they interact only
   with the necessary stream interfaces.

Although I do not have access to an Intel IAA accelerator and other
accelerators may not be generally available, this series seems to give
a good start for supporting batched asynchronous operations in zram.
The next step would be to introduce an interface that allows
non-blocking compression submission and validate its real-world
performance once such hardware accelerators become available.

Signed-off-by: Jihan LIN <linjh22s@gmail.com>
---
Jihan LIN (3):
      zram: Rename zcomp_strm_{init, free}()
      zram: Introduce zcomp-managed streams
      zram: Use zcomp-managed streams for async write requests

 drivers/block/zram/zcomp.c    | 37 ++++++++++++++++++++++++++++++-------
 drivers/block/zram/zcomp.h    | 23 +++++++++++++++++++++--
 drivers/block/zram/zram_drv.c | 28 ++++++++++++++++++++++------
 3 files changed, 73 insertions(+), 15 deletions(-)
---
base-commit: 24d479d26b25bce5faea3ddd9fa8f3a6c3129ea7
change-id: 20260202-b4_zcomp_stream-7e9f7884e128

Best regards,
-- 
Jihan LIN <linjh22s@gmail.com>



^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH RFC 1/3] zram: Rename zcomp_strm_{init, free}()
  2026-02-04 13:48 [PATCH RFC 0/3] zram: Allow zcomps to manage their own streams Jihan LIN via B4 Relay
@ 2026-02-04 13:48 ` Jihan LIN via B4 Relay
  2026-02-04 13:48 ` [PATCH RFC 2/3] zram: Introduce zcomp-managed streams Jihan LIN via B4 Relay
  2026-02-04 13:48 ` [PATCH RFC 3/3] zram: Use zcomp-managed streams for async write requests Jihan LIN via B4 Relay
  2 siblings, 0 replies; 4+ messages in thread
From: Jihan LIN via B4 Relay @ 2026-02-04 13:48 UTC (permalink / raw)
  To: Minchan Kim, Sergey Senozhatsky, Jens Axboe
  Cc: linux-kernel, linux-block, Jihan LIN

From: Jihan LIN <linjh22s@gmail.com>

Currently, zcomp uses a preemptive per-CPU stream model where streams
are allocated for each online CPUs and guarded by mutexes. The existing
names zcomp_strm_{init, free}() are too generic to explicitly indicate
they handle per-CPU streams.

Rename them to zcomp_strm_{init, free}_percpu(). This helps distinguish
them from future streams that may not be per-CPU based. No functional
changes are intended.

Signed-off-by: Jihan LIN <linjh22s@gmail.com>
---
 drivers/block/zram/zcomp.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c
index b1bd1daa0060a5b437d947d504d9ee6b4a9ac152..1614340e81dd2bebb29373411c9d180446f78f4c 100644
--- a/drivers/block/zram/zcomp.c
+++ b/drivers/block/zram/zcomp.c
@@ -43,7 +43,7 @@ static const struct zcomp_ops *backends[] = {
 	NULL
 };
 
-static void zcomp_strm_free(struct zcomp *comp, struct zcomp_strm *zstrm)
+static void zcomp_strm_free_percpu(struct zcomp *comp, struct zcomp_strm *zstrm)
 {
 	comp->ops->destroy_ctx(&zstrm->ctx);
 	vfree(zstrm->local_copy);
@@ -51,7 +51,7 @@ static void zcomp_strm_free(struct zcomp *comp, struct zcomp_strm *zstrm)
 	zstrm->buffer = NULL;
 }
 
-static int zcomp_strm_init(struct zcomp *comp, struct zcomp_strm *zstrm)
+static int zcomp_strm_init_percpu(struct zcomp *comp, struct zcomp_strm *zstrm)
 {
 	int ret;
 
@@ -66,7 +66,7 @@ static int zcomp_strm_init(struct zcomp *comp, struct zcomp_strm *zstrm)
 	 */
 	zstrm->buffer = vzalloc(2 * PAGE_SIZE);
 	if (!zstrm->buffer || !zstrm->local_copy) {
-		zcomp_strm_free(comp, zstrm);
+		zcomp_strm_free_percpu(comp, zstrm);
 		return -ENOMEM;
 	}
 	return 0;
@@ -172,7 +172,7 @@ int zcomp_cpu_up_prepare(unsigned int cpu, struct hlist_node *node)
 	struct zcomp_strm *zstrm = per_cpu_ptr(comp->stream, cpu);
 	int ret;
 
-	ret = zcomp_strm_init(comp, zstrm);
+	ret = zcomp_strm_init_percpu(comp, zstrm);
 	if (ret)
 		pr_err("Can't allocate a compression stream\n");
 	return ret;
@@ -184,7 +184,7 @@ int zcomp_cpu_dead(unsigned int cpu, struct hlist_node *node)
 	struct zcomp_strm *zstrm = per_cpu_ptr(comp->stream, cpu);
 
 	mutex_lock(&zstrm->lock);
-	zcomp_strm_free(comp, zstrm);
+	zcomp_strm_free_percpu(comp, zstrm);
 	mutex_unlock(&zstrm->lock);
 	return 0;
 }

-- 
2.51.0



^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH RFC 2/3] zram: Introduce zcomp-managed streams
  2026-02-04 13:48 [PATCH RFC 0/3] zram: Allow zcomps to manage their own streams Jihan LIN via B4 Relay
  2026-02-04 13:48 ` [PATCH RFC 1/3] zram: Rename zcomp_strm_{init, free}() Jihan LIN via B4 Relay
@ 2026-02-04 13:48 ` Jihan LIN via B4 Relay
  2026-02-04 13:48 ` [PATCH RFC 3/3] zram: Use zcomp-managed streams for async write requests Jihan LIN via B4 Relay
  2 siblings, 0 replies; 4+ messages in thread
From: Jihan LIN via B4 Relay @ 2026-02-04 13:48 UTC (permalink / raw)
  To: Minchan Kim, Sergey Senozhatsky, Jens Axboe
  Cc: linux-kernel, linux-block, Jihan LIN

From: Jihan LIN <linjh22s@gmail.com>

Currently, zcomp uses a per-CPU stream model. This design is restrictive
for hardware-accelerated or batched zcomp backends. These backends often
need to manage their own resources rather than relying on a generic
mutex-protected per-CPU stream for batched operations.

This patch introduces a hybrid model, allowing backends to optionally
manage their own streams while generic per-CPU streams still remain
allocated as a complementary mechanism.

Introduce zstrm_mgmt flag to struct zcomp_params. Backends set this flag
during zcomp_ops->setup_params() to advertise their capability to manage
streams.
Add zcomp_ops->{get, put}_stream() to allow zcomp backends to implement
their own stream strategies.
Modify zcomp_stream_get() to accept a new parameter indicating
zcomp-managed streams are preferred, and update zcomp_stream_put() to
route a zcomp-managed stream to the backend. If the backends advertise
their capability and the caller prefers managed streams, try to get the
stream from the backends; otherwise, fall back to the generic per-CPU
stream.

All existing call sites request the default per-CPU stream to preserve
the original behavior.

Signed-off-by: Jihan LIN <linjh22s@gmail.com>
---
 drivers/block/zram/zcomp.c    | 27 +++++++++++++++++++++++++--
 drivers/block/zram/zcomp.h    | 23 +++++++++++++++++++++--
 drivers/block/zram/zram_drv.c |  6 +++---
 3 files changed, 49 insertions(+), 7 deletions(-)

diff --git a/drivers/block/zram/zcomp.c b/drivers/block/zram/zcomp.c
index 1614340e81dd2bebb29373411c9d180446f78f4c..86ff6ecb0293d7b95ef4fa822122568cedf78f6e 100644
--- a/drivers/block/zram/zcomp.c
+++ b/drivers/block/zram/zcomp.c
@@ -69,6 +69,7 @@ static int zcomp_strm_init_percpu(struct zcomp *comp, struct zcomp_strm *zstrm)
 		zcomp_strm_free_percpu(comp, zstrm);
 		return -ENOMEM;
 	}
+	zstrm->zcomp_managed = false;
 	return 0;
 }
 
@@ -107,8 +108,18 @@ ssize_t zcomp_available_show(const char *comp, char *buf, ssize_t at)
 	return at;
 }
 
-struct zcomp_strm *zcomp_stream_get(struct zcomp *comp)
+struct zcomp_strm *zcomp_stream_get(struct zcomp *comp, enum zstrm_pref pref)
 {
+	if (comp->params->zstrm_mgmt && pref == ZSTRM_PREFER_MGMT) {
+		struct zcomp_strm *zcomp_strm =
+			comp->ops->get_stream(comp->params);
+
+		if (zcomp_strm) {
+			zcomp_strm->comp = comp;
+			return zcomp_strm;
+		}
+	}
+
 	for (;;) {
 		struct zcomp_strm *zstrm = raw_cpu_ptr(comp->stream);
 
@@ -131,7 +142,11 @@ struct zcomp_strm *zcomp_stream_get(struct zcomp *comp)
 
 void zcomp_stream_put(struct zcomp_strm *zstrm)
 {
-	mutex_unlock(&zstrm->lock);
+	if (zstrm->zcomp_managed) {
+		zstrm->comp->ops->put_stream(zstrm->comp->params, zstrm);
+	} else {
+		mutex_unlock(&zstrm->lock);
+	}
 }
 
 int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm,
@@ -197,11 +212,19 @@ static int zcomp_init(struct zcomp *comp, struct zcomp_params *params)
 	if (!comp->stream)
 		return -ENOMEM;
 
+	params->zstrm_mgmt = false;
 	comp->params = params;
 	ret = comp->ops->setup_params(comp->params);
 	if (ret)
 		goto cleanup;
 
+	if (params->zstrm_mgmt &&
+	    !(comp->ops->get_stream && comp->ops->put_stream)) {
+		params->zstrm_mgmt = false;
+		pr_warn("Missing managed stream ops in %s, managed stream disabled\n",
+			comp->ops->name);
+	}
+
 	for_each_possible_cpu(cpu)
 		mutex_init(&per_cpu_ptr(comp->stream, cpu)->lock);
 
diff --git a/drivers/block/zram/zcomp.h b/drivers/block/zram/zcomp.h
index eacfd3f7d61d9395694292713fb5da4f0023d6d7..cbe8842ea5352eed4e73e3d45fe6c12221ab9f64 100644
--- a/drivers/block/zram/zcomp.h
+++ b/drivers/block/zram/zcomp.h
@@ -24,6 +24,7 @@ struct zcomp_params {
 	union {
 		struct deflate_params deflate;
 	};
+	bool zstrm_mgmt;
 
 	void *drv_data;
 };
@@ -31,14 +32,18 @@ struct zcomp_params {
 /*
  * Run-time driver context - scratch buffers, etc. It is modified during
  * request execution (compression/decompression), cannot be shared, so
- * it's in per-CPU area.
+ * it's in per-CPU area or management by backend.
  */
 struct zcomp_ctx {
 	void *context;
 };
 
 struct zcomp_strm {
+	bool zcomp_managed;
+	/* lock used only for per-cpu streams */
 	struct mutex lock;
+	/* pointer to zcomp valid only for zcomp-managed streams */
+	struct zcomp *comp;
 	/* compression buffer */
 	void *buffer;
 	/* local copy of handle memory */
@@ -54,6 +59,11 @@ struct zcomp_req {
 	size_t dst_len;
 };
 
+enum zstrm_pref {
+	ZSTRM_DEFAULT, /* always use the generic per-CPU stream */
+	ZSTRM_PREFER_MGMT, /* try managed stream; fallback to per-CPU */
+};
+
 struct zcomp_ops {
 	int (*compress)(struct zcomp_params *params, struct zcomp_ctx *ctx,
 			struct zcomp_req *req);
@@ -66,6 +76,15 @@ struct zcomp_ops {
 	int (*setup_params)(struct zcomp_params *params);
 	void (*release_params)(struct zcomp_params *params);
 
+	/*
+	 * get_stream() needs to prepare zstrm->ctx, and backend must ensure
+	 * returned stream sets zcomp_managed and match the per-cpu stream
+	 * sizing: local_copy >= PAGE_SIZE, buffer >= 2 * PAGE_SIZE.
+	 */
+	struct zcomp_strm *(*get_stream)(struct zcomp_params *params);
+	void (*put_stream)(struct zcomp_params *params,
+			   struct zcomp_strm *zstrm);
+
 	const char *name;
 };
 
@@ -85,7 +104,7 @@ bool zcomp_available_algorithm(const char *comp);
 struct zcomp *zcomp_create(const char *alg, struct zcomp_params *params);
 void zcomp_destroy(struct zcomp *comp);
 
-struct zcomp_strm *zcomp_stream_get(struct zcomp *comp);
+struct zcomp_strm *zcomp_stream_get(struct zcomp *comp, enum zstrm_pref pref);
 void zcomp_stream_put(struct zcomp_strm *zstrm);
 
 int zcomp_compress(struct zcomp *comp, struct zcomp_strm *zstrm,
diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 5759823d631488904189168326fd133549c76141..2e5a1415e9034674e14e619f486052cd21098f50 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -1966,7 +1966,7 @@ static int read_compressed_page(struct zram *zram, struct page *page, u32 index)
 	size = zram_get_obj_size(zram, index);
 	prio = zram_get_priority(zram, index);
 
-	zstrm = zcomp_stream_get(zram->comps[prio]);
+	zstrm = zcomp_stream_get(zram->comps[prio], ZSTRM_DEFAULT);
 	src = zs_obj_read_begin(zram->mem_pool, handle, zstrm->local_copy);
 	dst = kmap_local_page(page);
 	ret = zcomp_decompress(zram->comps[prio], zstrm, src, size, dst);
@@ -2121,7 +2121,7 @@ static int zram_write_page(struct zram *zram, struct page *page, u32 index)
 	if (same_filled)
 		return write_same_filled_page(zram, element, index);
 
-	zstrm = zcomp_stream_get(zram->comps[ZRAM_PRIMARY_COMP]);
+	zstrm = zcomp_stream_get(zram->comps[ZRAM_PRIMARY_COMP], ZSTRM_DEFAULT);
 	mem = kmap_local_page(page);
 	ret = zcomp_compress(zram->comps[ZRAM_PRIMARY_COMP], zstrm,
 			     mem, &comp_len);
@@ -2303,7 +2303,7 @@ static int recompress_slot(struct zram *zram, u32 index, struct page *page,
 		if (!zram->comps[prio])
 			continue;
 
-		zstrm = zcomp_stream_get(zram->comps[prio]);
+		zstrm = zcomp_stream_get(zram->comps[prio], ZSTRM_DEFAULT);
 		src = kmap_local_page(page);
 		ret = zcomp_compress(zram->comps[prio], zstrm,
 				     src, &comp_len_new);

-- 
2.51.0



^ permalink raw reply	[flat|nested] 4+ messages in thread

* [PATCH RFC 3/3] zram: Use zcomp-managed streams for async write requests
  2026-02-04 13:48 [PATCH RFC 0/3] zram: Allow zcomps to manage their own streams Jihan LIN via B4 Relay
  2026-02-04 13:48 ` [PATCH RFC 1/3] zram: Rename zcomp_strm_{init, free}() Jihan LIN via B4 Relay
  2026-02-04 13:48 ` [PATCH RFC 2/3] zram: Introduce zcomp-managed streams Jihan LIN via B4 Relay
@ 2026-02-04 13:48 ` Jihan LIN via B4 Relay
  2 siblings, 0 replies; 4+ messages in thread
From: Jihan LIN via B4 Relay @ 2026-02-04 13:48 UTC (permalink / raw)
  To: Minchan Kim, Sergey Senozhatsky, Jens Axboe
  Cc: linux-kernel, linux-block, Jihan LIN

From: Jihan LIN <linjh22s@gmail.com>

Crrent per-CPU streams limit write concurrency to the number of online
CPUs. For zcomp backends that support zcomp-managed streams, we can
utilize them for async write requests to get better parallelism.

Modify zram_write_page() to accept a flag indicating the request is
asynchronous. If the bio request is considered as non-synchronous and
the backend supports zcomp-managed streams, attempt to acquire a
zcomp-managed stream.

Although zram_write_page() currently waits for compression to complete,
making the operation appear synchronous, using zcomp-managed streams
has the potential to improve parallelism for async write requests,
provided the backend utilizes efficient streams management or
specialized implementations.

zcomp_stream_get() handles the fallback to per-CPU streams.
zram_read_page() remains using the generic per-CPU streams, since reads
are always treated as synchronous in op_is_sync().

Support multiple pages within a single bio request is deferred to keep
simple and focused.

Signed-off-by: Jihan LIN <linjh22s@gmail.com>
---
 drivers/block/zram/zram_drv.c | 24 ++++++++++++++++++++----
 1 file changed, 20 insertions(+), 4 deletions(-)

diff --git a/drivers/block/zram/zram_drv.c b/drivers/block/zram/zram_drv.c
index 2e5a1415e9034674e14e619f486052cd21098f50..655d0e141c621ca38ca1059780c1a8a00258c868 100644
--- a/drivers/block/zram/zram_drv.c
+++ b/drivers/block/zram/zram_drv.c
@@ -1966,6 +1966,7 @@ static int read_compressed_page(struct zram *zram, struct page *page, u32 index)
 	size = zram_get_obj_size(zram, index);
 	prio = zram_get_priority(zram, index);
 
+	/* Reads are treated as synchronous, see op_is_sync(). */
 	zstrm = zcomp_stream_get(zram->comps[prio], ZSTRM_DEFAULT);
 	src = zs_obj_read_begin(zram->mem_pool, handle, zstrm->local_copy);
 	dst = kmap_local_page(page);
@@ -2105,7 +2106,8 @@ static int write_incompressible_page(struct zram *zram, struct page *page,
 	return 0;
 }
 
-static int zram_write_page(struct zram *zram, struct page *page, u32 index)
+static int zram_write_page(struct zram *zram, struct page *page, u32 index,
+			   bool is_async)
 {
 	int ret = 0;
 	unsigned long handle;
@@ -2121,7 +2123,19 @@ static int zram_write_page(struct zram *zram, struct page *page, u32 index)
 	if (same_filled)
 		return write_same_filled_page(zram, element, index);
 
-	zstrm = zcomp_stream_get(zram->comps[ZRAM_PRIMARY_COMP], ZSTRM_DEFAULT);
+	/*
+	 * Using zcomp-managed stream and waiting for compress done makes
+	 * this appear synchronous.
+	 *
+	 * At this time, zram_bio_write handles pages one by one.
+	 * However, zcomp-managed streams allow threads to submit jobs
+	 * to zcomp without the lock contention in per-cpu streams.
+	 * This might give us better parallelism than the generic per-cpu
+	 * streams could, assuming zcomp uses efficient streams management
+	 * or utilizes the specialized implementations.
+	 */
+	zstrm = zcomp_stream_get(zram->comps[ZRAM_PRIMARY_COMP],
+				 is_async ? ZSTRM_PREFER_MGMT : ZSTRM_DEFAULT);
 	mem = kmap_local_page(page);
 	ret = zcomp_compress(zram->comps[ZRAM_PRIMARY_COMP], zstrm,
 			     mem, &comp_len);
@@ -2183,7 +2197,8 @@ static int zram_bvec_write_partial(struct zram *zram, struct bio_vec *bvec,
 	ret = zram_read_page(zram, page, index, bio);
 	if (!ret) {
 		memcpy_from_bvec(page_address(page) + offset, bvec);
-		ret = zram_write_page(zram, page, index);
+		ret = zram_write_page(zram, page, index,
+				      !op_is_sync(bio->bi_opf));
 	}
 	__free_page(page);
 	return ret;
@@ -2194,7 +2209,8 @@ static int zram_bvec_write(struct zram *zram, struct bio_vec *bvec,
 {
 	if (is_partial_io(bvec))
 		return zram_bvec_write_partial(zram, bvec, index, offset, bio);
-	return zram_write_page(zram, bvec->bv_page, index);
+	return zram_write_page(zram, bvec->bv_page, index,
+			       !op_is_sync(bio->bi_opf));
 }
 
 #ifdef CONFIG_ZRAM_MULTI_COMP

-- 
2.51.0



^ permalink raw reply	[flat|nested] 4+ messages in thread

end of thread, other threads:[~2026-02-04 13:48 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-02-04 13:48 [PATCH RFC 0/3] zram: Allow zcomps to manage their own streams Jihan LIN via B4 Relay
2026-02-04 13:48 ` [PATCH RFC 1/3] zram: Rename zcomp_strm_{init, free}() Jihan LIN via B4 Relay
2026-02-04 13:48 ` [PATCH RFC 2/3] zram: Introduce zcomp-managed streams Jihan LIN via B4 Relay
2026-02-04 13:48 ` [PATCH RFC 3/3] zram: Use zcomp-managed streams for async write requests Jihan LIN via B4 Relay

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®