diff options
author | Andreas Gruenbacher <agruenba@redhat.com> | 2018-06-07 12:56:46 +0200 |
---|---|---|
committer | Bob Peterson <rpeterso@redhat.com> | 2018-06-21 14:39:44 +0200 |
commit | 9e1a9ecd13b9bb421c88135b178577caf4d54f6a (patch) | |
tree | be518fb982b7f8f47c3ed3fa864772d4dd974985 /fs/gfs2/log.c | |
parent | gfs2: eliminate rs_inum and reduce the size of gfs2 inodes (diff) | |
download | linux-9e1a9ecd13b9bb421c88135b178577caf4d54f6a.tar.xz linux-9e1a9ecd13b9bb421c88135b178577caf4d54f6a.zip |
gfs2: Don't withdraw under a spin lock
In two places, the gfs2_io_error_bh macro is called while holding the
sd_ail_lock spin lock. This isn't allowed because gfs2_io_error_bh
withdraws the filesystem, which can sleep because it issues a uevent.
To fix that, add a gfs2_io_error_bh_wd macro that does withdraw the
filesystem and change gfs2_io_error_bh to not withdraw the filesystem.
In those places where the new gfs2_io_error_bh is used, withdraw the
filesystem after releasing sd_ail_lock.
Signed-off-by: Andreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: Bob Peterson <rpeterso@redhat.com>
Reviewed-by: Andrew Price <anprice@redhat.com>
Diffstat (limited to 'fs/gfs2/log.c')
-rw-r--r-- | fs/gfs2/log.c | 26 |
1 files changed, 19 insertions, 7 deletions
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index 0248835625f1..a767fad02386 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -92,7 +92,8 @@ static void gfs2_remove_from_ail(struct gfs2_bufdata *bd) static int gfs2_ail1_start_one(struct gfs2_sbd *sdp, struct writeback_control *wbc, - struct gfs2_trans *tr) + struct gfs2_trans *tr, + bool *withdraw) __releases(&sdp->sd_ail_lock) __acquires(&sdp->sd_ail_lock) { @@ -107,8 +108,10 @@ __acquires(&sdp->sd_ail_lock) gfs2_assert(sdp, bd->bd_tr == tr); if (!buffer_busy(bh)) { - if (!buffer_uptodate(bh)) + if (!buffer_uptodate(bh)) { gfs2_io_error_bh(sdp, bh); + *withdraw = true; + } list_move(&bd->bd_ail_st_list, &tr->tr_ail2_list); continue; } @@ -148,6 +151,7 @@ void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc) struct list_head *head = &sdp->sd_ail1_list; struct gfs2_trans *tr; struct blk_plug plug; + bool withdraw = false; trace_gfs2_ail_flush(sdp, wbc, 1); blk_start_plug(&plug); @@ -156,11 +160,13 @@ restart: list_for_each_entry_reverse(tr, head, tr_list) { if (wbc->nr_to_write <= 0) break; - if (gfs2_ail1_start_one(sdp, wbc, tr)) + if (gfs2_ail1_start_one(sdp, wbc, tr, &withdraw)) goto restart; } spin_unlock(&sdp->sd_ail_lock); blk_finish_plug(&plug); + if (withdraw) + gfs2_lm_withdraw(sdp, NULL); trace_gfs2_ail_flush(sdp, wbc, 0); } @@ -188,7 +194,8 @@ static void gfs2_ail1_start(struct gfs2_sbd *sdp) * */ -static void gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_trans *tr) +static void gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_trans *tr, + bool *withdraw) { struct gfs2_bufdata *bd, *s; struct buffer_head *bh; @@ -199,11 +206,12 @@ static void gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_trans *tr) gfs2_assert(sdp, bd->bd_tr == tr); if (buffer_busy(bh)) continue; - if (!buffer_uptodate(bh)) + if (!buffer_uptodate(bh)) { gfs2_io_error_bh(sdp, bh); + *withdraw = true; + } list_move(&bd->bd_ail_st_list, &tr->tr_ail2_list); } - } /** @@ -218,10 +226,11 @@ static int gfs2_ail1_empty(struct gfs2_sbd *sdp) struct gfs2_trans *tr, *s; int oldest_tr = 1; int ret; + bool withdraw = false; spin_lock(&sdp->sd_ail_lock); list_for_each_entry_safe_reverse(tr, s, &sdp->sd_ail1_list, tr_list) { - gfs2_ail1_empty_one(sdp, tr); + gfs2_ail1_empty_one(sdp, tr, &withdraw); if (list_empty(&tr->tr_ail1_list) && oldest_tr) list_move(&tr->tr_list, &sdp->sd_ail2_list); else @@ -230,6 +239,9 @@ static int gfs2_ail1_empty(struct gfs2_sbd *sdp) ret = list_empty(&sdp->sd_ail1_list); spin_unlock(&sdp->sd_ail_lock); + if (withdraw) + gfs2_lm_withdraw(sdp, "fatal: I/O error(s)\n"); + return ret; } |