Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

gfs2: gfs2_log_flush withdraw fixes

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 <agruenba@redhat.com>

+17 -8
+17 -8
fs/gfs2/log.c
··· 983 983 } 984 984 } 985 985 986 - static void gfs2_trans_drain_list(struct list_head *list) 986 + static void gfs2_trans_drain_list(struct gfs2_sbd *sdp, struct list_head *list) 987 987 { 988 988 struct gfs2_bufdata *bd; 989 989 990 990 while (!list_empty(list)) { 991 991 bd = list_first_entry(list, struct gfs2_bufdata, bd_list); 992 + struct buffer_head *bh = bd->bd_bh; 993 + 994 + WARN_ON_ONCE(!buffer_pinned(bh)); 995 + clear_buffer_pinned(bh); 996 + trace_gfs2_pin(bd, 0); 997 + atomic_dec(&sdp->sd_log_pinned); 992 998 list_del_init(&bd->bd_list); 993 - if (!list_empty(&bd->bd_ail_st_list)) 994 - gfs2_remove_from_ail(bd); 995 - kmem_cache_free(gfs2_bufdata_cachep, bd); 999 + brelse(bh); 996 1000 } 997 1001 } 998 1002 999 1003 /** 1000 1004 * gfs2_trans_drain - drain the buf and databuf queue for a failed transaction 1005 + * @sdp: the filesystem 1001 1006 * @tr: the transaction to drain 1002 1007 * 1003 1008 * When this is called, we're taking an error exit for a log write that failed 1004 1009 * but since we bypassed the after_commit functions, we need to remove the 1005 1010 * items from the buf and databuf queue. 1006 1011 */ 1007 - static void gfs2_trans_drain(struct gfs2_trans *tr) 1012 + static void gfs2_trans_drain(struct gfs2_sbd *sdp, struct gfs2_trans *tr) 1008 1013 { 1009 1014 if (!tr) 1010 1015 return; 1011 - gfs2_trans_drain_list(&tr->tr_buf); 1012 - gfs2_trans_drain_list(&tr->tr_databuf); 1016 + gfs2_trans_drain_list(sdp, &tr->tr_buf); 1017 + gfs2_trans_drain_list(sdp, &tr->tr_databuf); 1013 1018 } 1014 1019 1015 1020 void gfs2_remove_from_journal(struct buffer_head *bh, int meta) ··· 1190 1185 return; 1191 1186 1192 1187 out_withdraw: 1193 - gfs2_trans_drain(tr); 1188 + if (sdp->sd_jdesc->jd_log_bio) { 1189 + bio_io_error(sdp->sd_jdesc->jd_log_bio); 1190 + sdp->sd_jdesc->jd_log_bio = NULL; 1191 + } 1192 + gfs2_trans_drain(sdp, tr); 1194 1193 /** 1195 1194 * If the tr_list is empty, we're withdrawing during a log 1196 1195 * flush that targets a transaction, but the transaction was