From ad67b498de17b709f050d7ebf84749b1fc6a242d Mon Sep 17 00:00:00 2001 From: Tal Zussman Date: Wed, 2 Sep 2026 12:11:25 -0400 Subject: [PATCH] ceph: fix error handling when the OSD client is stopping in ceph_submit_write() 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 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 Reviewed-by: Alex Markuze Signed-off-by: Ilya Dryomov --- fs/ceph/addr.c | 39 +++++++++++++++++++++++---------------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/fs/ceph/addr.c b/fs/ceph/addr.c index e6d62f711db8..69a4be12b674 100644 --- a/fs/ceph/addr.c +++ b/fs/ceph/addr.c @@ -1502,28 +1502,32 @@ new_request: 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]; - - if (!folio) - continue; - - page = &folio->page; - redirty_page_for_writepage(wbc, page); - unlock_page(page); - } - for (i = 0; i < ceph_wbc->locked_pages; i++) { - page = ceph_fscrypt_pagecache_page(ceph_wbc->pages[i]); + page = ceph_wbc->pages[i]; + if (fscrypt_is_bounce_page(page)) { + page = fscrypt_pagecache_page(page); + fscrypt_free_bounce_page(ceph_wbc->pages[i]); + } - if (!page) - continue; + if (atomic_long_dec_return(&fsc->writeback_count) < + CONGESTION_OFF_THRESH(fsc->mount_options->congestion_kb)) + fsc->write_congested = false; ceph_undo_wrbuffer_claim(inode, page_folio(page)); redirty_page_for_writepage(wbc, page); unlock_page(page); + put_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; } @@ -1756,8 +1760,11 @@ process_folio_batch: } 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;