[PATCH 14/18] gfs2: gfs2_log_flush withdraw fixes
Andreas Gruenbacher <[email protected]> Mon, 13 Apr 2026 16:52:07 +0200
| Newsgroups | dev.linux.lists.gfs2 |
|---|---|
| Message-ID | <[email protected]> |
When a withdraw occurs in gfs2_log_flush() and we are left with an unsubmitted bio, fail that bio. Otherwise, the bh's in that bio will remain locked and gfs2_evict_inode() -> truncate_inode_pages() -> gfs2_invalidate_folio() -> gfs2_discard() will hang trying to discard the locked bh's. In addition, when gfs2_log_flush() fails to submit a new transaction, unpin the buffers in the failing transaction like gfs2_remove_from_journal() does. If any of the bd's are on the ail2 list, leave them there and do_withdraw() -> gfs2_withdraw_glocks() -> inode_go_inval() -> truncate_inode_pages() -> gfs2_invalidate_folio() -> gfs2_discard() will remove them. They will be freed in gfs2_release_folio(). Signed-off-by: Andreas Gruenbacher <[email protected]> --- fs/gfs2/log.c | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index a96f9b9331e8..3a01d4e7667a 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -983,33 +983,38 @@ static void empty_ail1_list(struct gfs2_sbd *sdp) } } -static void gfs2_trans_drain_list(struct list_head *list) +static void gfs2_trans_drain_list(struct gfs2_sbd *sdp, struct list_head *list) { struct gfs2_bufdata *bd; while (!list_empty(list)) { bd = list_first_entry(list, struct gfs2_bufdata, bd_list); + struct buffer_head *bh = bd->bd_bh; + + WARN_ON_ONCE(!buffer_pinned(bh)); + clear_buffer_pinned(bh); + trace_gfs2_pin(bd, 0); + atomic_dec(&sdp->sd_log_pinned); list_del_init(&bd->bd_list); - if (!list_empty(&bd->bd_ail_st_list)) - gfs2_remove_from_ail(bd); - kmem_cache_free(gfs2_bufdata_cachep, bd); + brelse(bh); } } /** * gfs2_trans_drain - drain the buf and databuf queue for a failed transaction + * @sdp: the filesystem * @tr: the transaction to drain * * When this is called, we're taking an error exit for a log write that failed * but since we bypassed the after_commit functions, we need to remove the * items from the buf and databuf queue. */ -static void gfs2_trans_drain(struct gfs2_trans *tr) +static void gfs2_trans_drain(struct gfs2_sbd *sdp, struct gfs2_trans *tr) { if (!tr) return; - gfs2_trans_drain_list(&tr->tr_buf); - gfs2_trans_drain_list(&tr->tr_databuf); + gfs2_trans_drain_list(sdp, &tr->tr_buf); + gfs2_trans_drain_list(sdp, &tr->tr_databuf); } void gfs2_remove_from_journal(struct buffer_head *bh, int meta) @@ -1185,7 +1190,11 @@ static void __gfs2_log_flush(struct gfs2_sbd *sdp, struct gfs2_glock *gl, return; out_withdraw: - gfs2_trans_drain(tr); + if (sdp->sd_jdesc->jd_log_bio) { + bio_io_error(sdp->sd_jdesc->jd_log_bio); + sdp->sd_jdesc->jd_log_bio = NULL; + } + gfs2_trans_drain(sdp, tr); /** * If the tr_list is empty, we're withdrawing during a log * flush that targets a transaction, but the transaction was -- 2.53.0