[PATCH v3 01/12] ceph: fix error handling when the OSD client is stopping in ceph_submit_write()
From: Tal Zussman
Date: Mon Aug 10 2026 - 10:30:03 EST
When ceph_inc_osd_stopping_blocker() fails, ceph_submit_write()
redirties and unlocks the folios remaining in ceph_wbc->fbatch.
However, none of those folios are locked at this point: locked folios
were moved to ceph_wbc->pages[] and removed from the fbatch by
ceph_process_folio_batch(). Unlocking them triggers
VM_BUG_ON_FOLIO(!folio_test_locked(folio)) in folio_unlock(), or
unlocks a folio locked by another task.
This error path also returns -EIO without fully cleaning up the folios in
ceph_wbc->pages[]: their references are never dropped, fscrypt bounce
pages are never freed, the writeback congestion count is never
decremented, and the pages array is leaked. ceph_wbc->locked_pages is
also left non-zero, so ceph_writepages_start() loops back around and
hits BUG_ON(ceph_wbc.locked_pages).
Leave the fbatch folios alone, as ceph_writepages_start() drops their
references when releasing the batch, and unwind ceph_wbc->pages[] the
same way writepages_finish() would have. Abort writeback in
ceph_writepages_start() instead of continuing, matching the handling
of ceph_inc_osd_stopping_blocker() failure on entry, as the OSD
client is being torn down and further writeback cannot make progress.
An LLM was used to verify that the cleanup was comprehensive, and
suggested aborting the writeback instead of continuing.
Fixes: 1551ec61dc55 ("ceph: introduce ceph_submit_write() method")
Reported-by: Sashiko <sashiko-bot@xxxxxxxxxx>
Link: https://sashiko.dev/#/patchset/20260804-remove-wait-on-page-writeback-v2-0-81f0ab065284%40columbia.edu?part=5
Assisted-by: Claude:fable-5
Signed-off-by: Tal Zussman <tz2294@xxxxxxxxxxxx>
---
fs/ceph/addr.c | 34 +++++++++++++++++++---------------
1 file changed, 19 insertions(+), 15 deletions(-)
diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c
index e2da3ab9f808..8cd24f66c981 100644
--- a/fs/ceph/addr.c
+++ b/fs/ceph/addr.c
@@ -1472,26 +1472,27 @@ int ceph_submit_write(struct address_space *mapping,
BUG_ON(len < ceph_fscrypt_page_offset(page) + thp_size(page) - offset);
if (!ceph_inc_osd_stopping_blocker(fsc->mdsc)) {
- for (i = 0; i < folio_batch_count(&ceph_wbc->fbatch); i++) {
- struct folio *folio = ceph_wbc->fbatch.folios[i];
+ for (i = 0; i < ceph_wbc->locked_pages; i++) {
+ fscrypt_finalize_bounce_page(&ceph_wbc->pages[i]);
+ page = ceph_wbc->pages[i];
- if (!folio)
- continue;
+ if (atomic_long_dec_return(&fsc->writeback_count) <
+ CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb))
+ fsc->write_congested = false;
- page = &folio->page;
redirty_page_for_writepage(wbc, page);
unlock_page(page);
+ put_page(page);
}
- for (i = 0; i < ceph_wbc->locked_pages; i++) {
- page = ceph_fscrypt_pagecache_page(ceph_wbc->pages[i]);
-
- if (!page)
- continue;
-
- redirty_page_for_writepage(wbc, page);
- unlock_page(page);
+ if (ceph_wbc->from_pool) {
+ mempool_free(ceph_wbc->pages, ceph_wb_pagevec_pool);
+ ceph_wbc->from_pool = false;
+ } else {
+ kfree(ceph_wbc->pages);
}
+ ceph_wbc->pages = NULL;
+ ceph_wbc->locked_pages = 0;
ceph_osdc_put_request(req);
return -EIO;
@@ -1725,8 +1726,11 @@ static int ceph_writepages_start(struct address_space *mapping,
}
rc = ceph_submit_write(mapping, wbc, &ceph_wbc);
- if (rc)
- goto release_folios;
+ if (rc) {
+ /* the OSD client is being torn down, don't retry */
+ folio_batch_release(&ceph_wbc.fbatch);
+ goto dec_osd_stopping_blocker;
+ }
ceph_wbc.locked_pages = 0;
ceph_wbc.strip_unit_end = 0;
--
2.39.5