[PATCH] loop: fix memcg css leak on read-only I/O path

Genjian <[email protected]> Mon, 10 Aug 2026 21:49:05 +0800
Newsgroups org.kernel.vger.linux-block,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
From: Genjian Zhang <[email protected]>

loop_queue_rq() takes a reference on the effective memcg css via
cgroup_get_e_css() and stores it in cmd->memcg_css. loop_handle_cmd()
is supposed to drop that reference after use, but the early exit for
writes to a read-only loop device jumps past css_put().

When testing on cgroup v2, stack a dm-linear device on a read-only
loop and issue writes to it from a child cgroup, then remove that
cgroup: nr_dying_subsys_memory in cgroup.stat increases and never goes
back down until reboot. The same test no longer shows the leak with
this patch.

Fix by moving css_put() onto the shared exit path so the read-only
failure case drops the reference as well.

Fixes: c74d40e8b5e2 ("loop: charge i/o to mem and blk cg")
Signed-off-by: Genjian Zhang <[email protected]>
---
 drivers/block/loop.c | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/drivers/block/loop.c b/drivers/block/loop.c
index 1faecef33009..66eaf912c013 100644
--- a/drivers/block/loop.c
+++ b/drivers/block/loop.c
@@ -1895,7 +1895,7 @@ static void loop_handle_cmd(struct loop_cmd *cmd)
 
 	if (write && (lo->lo_flags & LO_FLAGS_READ_ONLY)) {
 		ret = -EIO;
-		goto failed;
+		goto out;
 	}
 
 	/* We can block in this context, so ignore REQ_NOWAIT. */
@@ -1919,11 +1919,12 @@ static void loop_handle_cmd(struct loop_cmd *cmd)
 	if (cmd_blkcg_css)
 		kthread_associate_blkcg(NULL);
 
-	if (cmd_memcg_css) {
+	if (cmd_memcg_css)
 		set_active_memcg(old_memcg);
+out:
+	if (cmd_memcg_css)
 		css_put(cmd_memcg_css);
-	}
- failed:
+
 	/* complete non-aio request */
 	if (ret != -EIOCBQUEUED) {
 		if (ret == -EOPNOTSUPP)
-- 
2.25.1