[PATCH net v4 4/4] net/rds: unpin MR pages with unpin_user_pages_dirty_lock()
Allison Henderson <[email protected]> Wed, 29 Jul 2026 21:16:29 -0700
| Newsgroups | org.kernel.vger.linux-rdma,org.kernel.vger.netdev |
|---|---|
| Message-ID | <[email protected]> |
The pages backing an RDS memory region are pinned in __rds_rdma_map()
with rds_pin_pages(), which uses pin_user_pages_fast(): each page's
refcount is biased by GUP_PIN_COUNTING_BIAS to account the pin. The
scatterlist is then handed to the IB transport, and the transport
releases the pages in __rds_ib_teardown_mr() with
set_page_dirty(page);
put_page(page);
put_page() drops a single reference instead of removing the pin bias,
so every MR teardown permanently strands the remaining references and
the pages are never freed - a userspace-triggerable memory leak of up
to RDS_MAX_MSG_SIZE per RDS_GET_MR/RDS_GET_MR_FOR_DEST call.
The conversion to the pin API updated the unpin sites in rdma.c but
missed this one on the transport side. Release the pages with
unpin_user_pages_dirty_lock(), which removes the pin bias and also
dirties the page under the folio lock, closing the truncation race
that a bare set_page_dirty() leaves open.
Dirtying under the folio lock can sleep, which is safe in every path
that reaches __rds_ib_teardown_mr(): the registration-reuse path
(rds_ib_map_frmr()) runs in syscall context, and the pool flush
(rds_ib_unreg_frmr()) runs under pool->flush_lock, a mutex, and
already sleeps in rds_ib_post_inv(). The WARN_ON that guarded the
old irq-context set_page_dirty() case is dropped along with it.
Fixes: 0d4597c8c5ab ("net/rds: Track user mapped pages through special API")
Assisted-by: Claude-Code:claude-fable-5
Signed-off-by: Allison Henderson <[email protected]>
---
net/rds/ib_rdma.c | 4 +---
1 file changed, 1 insertion(+), 3 deletions(-)
diff --git a/net/rds/ib_rdma.c b/net/rds/ib_rdma.c
index 9594ea245f7fe..db7e92e7bd29f 100644
--- a/net/rds/ib_rdma.c
+++ b/net/rds/ib_rdma.c
@@ -251,9 +251,7 @@ void __rds_ib_teardown_mr(struct rds_ib_mr *ibmr)
/* FIXME we need a way to tell a r/w MR
* from a r/o MR */
- WARN_ON(!page->mapping && irqs_disabled());
- set_page_dirty(page);
- put_page(page);
+ unpin_user_pages_dirty_lock(&page, 1, true);
}
kfree(ibmr->sg);
--
2.25.1