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

From: Genjian

Date: Mon Aug 10 2026 - 09:54:20 EST


From: Genjian Zhang <zhanggenjian@xxxxxxxxxx>

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 <zhanggenjian@xxxxxxxxxx>
---
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