Commit 9e1a9ecd authored by Andreas Gruenbacher's avatar Andreas Gruenbacher Committed by Bob Peterson

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: default avatarAndreas Gruenbacher <agruenba@redhat.com>
Signed-off-by: default avatarBob Peterson <rpeterso@redhat.com>
Reviewed-by: default avatarAndrew Price <anprice@redhat.com>
parent f85c10e2
...@@ -92,7 +92,8 @@ static void gfs2_remove_from_ail(struct gfs2_bufdata *bd) ...@@ -92,7 +92,8 @@ static void gfs2_remove_from_ail(struct gfs2_bufdata *bd)
static int gfs2_ail1_start_one(struct gfs2_sbd *sdp, static int gfs2_ail1_start_one(struct gfs2_sbd *sdp,
struct writeback_control *wbc, struct writeback_control *wbc,
struct gfs2_trans *tr) struct gfs2_trans *tr,
bool *withdraw)
__releases(&sdp->sd_ail_lock) __releases(&sdp->sd_ail_lock)
__acquires(&sdp->sd_ail_lock) __acquires(&sdp->sd_ail_lock)
{ {
...@@ -107,8 +108,10 @@ __acquires(&sdp->sd_ail_lock) ...@@ -107,8 +108,10 @@ __acquires(&sdp->sd_ail_lock)
gfs2_assert(sdp, bd->bd_tr == tr); gfs2_assert(sdp, bd->bd_tr == tr);
if (!buffer_busy(bh)) { if (!buffer_busy(bh)) {
if (!buffer_uptodate(bh)) if (!buffer_uptodate(bh)) {
gfs2_io_error_bh(sdp, bh); gfs2_io_error_bh(sdp, bh);
*withdraw = true;
}
list_move(&bd->bd_ail_st_list, &tr->tr_ail2_list); list_move(&bd->bd_ail_st_list, &tr->tr_ail2_list);
continue; continue;
} }
...@@ -148,6 +151,7 @@ void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc) ...@@ -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 list_head *head = &sdp->sd_ail1_list;
struct gfs2_trans *tr; struct gfs2_trans *tr;
struct blk_plug plug; struct blk_plug plug;
bool withdraw = false;
trace_gfs2_ail_flush(sdp, wbc, 1); trace_gfs2_ail_flush(sdp, wbc, 1);
blk_start_plug(&plug); blk_start_plug(&plug);
...@@ -156,11 +160,13 @@ void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc) ...@@ -156,11 +160,13 @@ void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc)
list_for_each_entry_reverse(tr, head, tr_list) { list_for_each_entry_reverse(tr, head, tr_list) {
if (wbc->nr_to_write <= 0) if (wbc->nr_to_write <= 0)
break; break;
if (gfs2_ail1_start_one(sdp, wbc, tr)) if (gfs2_ail1_start_one(sdp, wbc, tr, &withdraw))
goto restart; goto restart;
} }
spin_unlock(&sdp->sd_ail_lock); spin_unlock(&sdp->sd_ail_lock);
blk_finish_plug(&plug); blk_finish_plug(&plug);
if (withdraw)
gfs2_lm_withdraw(sdp, NULL);
trace_gfs2_ail_flush(sdp, wbc, 0); trace_gfs2_ail_flush(sdp, wbc, 0);
} }
...@@ -188,7 +194,8 @@ static void gfs2_ail1_start(struct gfs2_sbd *sdp) ...@@ -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 gfs2_bufdata *bd, *s;
struct buffer_head *bh; struct buffer_head *bh;
...@@ -199,11 +206,12 @@ static void gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_trans *tr) ...@@ -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); gfs2_assert(sdp, bd->bd_tr == tr);
if (buffer_busy(bh)) if (buffer_busy(bh))
continue; continue;
if (!buffer_uptodate(bh)) if (!buffer_uptodate(bh)) {
gfs2_io_error_bh(sdp, bh); gfs2_io_error_bh(sdp, bh);
*withdraw = true;
}
list_move(&bd->bd_ail_st_list, &tr->tr_ail2_list); list_move(&bd->bd_ail_st_list, &tr->tr_ail2_list);
} }
} }
/** /**
...@@ -218,10 +226,11 @@ static int gfs2_ail1_empty(struct gfs2_sbd *sdp) ...@@ -218,10 +226,11 @@ static int gfs2_ail1_empty(struct gfs2_sbd *sdp)
struct gfs2_trans *tr, *s; struct gfs2_trans *tr, *s;
int oldest_tr = 1; int oldest_tr = 1;
int ret; int ret;
bool withdraw = false;
spin_lock(&sdp->sd_ail_lock); spin_lock(&sdp->sd_ail_lock);
list_for_each_entry_safe_reverse(tr, s, &sdp->sd_ail1_list, tr_list) { 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) if (list_empty(&tr->tr_ail1_list) && oldest_tr)
list_move(&tr->tr_list, &sdp->sd_ail2_list); list_move(&tr->tr_list, &sdp->sd_ail2_list);
else else
...@@ -230,6 +239,9 @@ static int gfs2_ail1_empty(struct gfs2_sbd *sdp) ...@@ -230,6 +239,9 @@ static int gfs2_ail1_empty(struct gfs2_sbd *sdp)
ret = list_empty(&sdp->sd_ail1_list); ret = list_empty(&sdp->sd_ail1_list);
spin_unlock(&sdp->sd_ail_lock); spin_unlock(&sdp->sd_ail_lock);
if (withdraw)
gfs2_lm_withdraw(sdp, "fatal: I/O error(s)\n");
return ret; return ret;
} }
......
...@@ -49,7 +49,7 @@ void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh) ...@@ -49,7 +49,7 @@ void gfs2_pin(struct gfs2_sbd *sdp, struct buffer_head *bh)
if (test_set_buffer_pinned(bh)) if (test_set_buffer_pinned(bh))
gfs2_assert_withdraw(sdp, 0); gfs2_assert_withdraw(sdp, 0);
if (!buffer_uptodate(bh)) if (!buffer_uptodate(bh))
gfs2_io_error_bh(sdp, bh); gfs2_io_error_bh_wd(sdp, bh);
bd = bh->b_private; bd = bh->b_private;
/* If this buffer is in the AIL and it has already been written /* If this buffer is in the AIL and it has already been written
* to in-place disk block, remove it from the AIL. * to in-place disk block, remove it from the AIL.
......
...@@ -293,7 +293,7 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags, ...@@ -293,7 +293,7 @@ int gfs2_meta_read(struct gfs2_glock *gl, u64 blkno, int flags,
if (unlikely(!buffer_uptodate(bh))) { if (unlikely(!buffer_uptodate(bh))) {
struct gfs2_trans *tr = current->journal_info; struct gfs2_trans *tr = current->journal_info;
if (tr && test_bit(TR_TOUCHED, &tr->tr_flags)) if (tr && test_bit(TR_TOUCHED, &tr->tr_flags))
gfs2_io_error_bh(sdp, bh); gfs2_io_error_bh_wd(sdp, bh);
brelse(bh); brelse(bh);
*bhp = NULL; *bhp = NULL;
return -EIO; return -EIO;
...@@ -320,7 +320,7 @@ int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh) ...@@ -320,7 +320,7 @@ int gfs2_meta_wait(struct gfs2_sbd *sdp, struct buffer_head *bh)
if (!buffer_uptodate(bh)) { if (!buffer_uptodate(bh)) {
struct gfs2_trans *tr = current->journal_info; struct gfs2_trans *tr = current->journal_info;
if (tr && test_bit(TR_TOUCHED, &tr->tr_flags)) if (tr && test_bit(TR_TOUCHED, &tr->tr_flags))
gfs2_io_error_bh(sdp, bh); gfs2_io_error_bh_wd(sdp, bh);
return -EIO; return -EIO;
} }
if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags))) if (unlikely(test_bit(SDF_SHUTDOWN, &sdp->sd_flags)))
......
...@@ -46,14 +46,16 @@ int gfs2_lm_withdraw(struct gfs2_sbd *sdp, const char *fmt, ...) ...@@ -46,14 +46,16 @@ int gfs2_lm_withdraw(struct gfs2_sbd *sdp, const char *fmt, ...)
test_and_set_bit(SDF_SHUTDOWN, &sdp->sd_flags)) test_and_set_bit(SDF_SHUTDOWN, &sdp->sd_flags))
return 0; return 0;
va_start(args, fmt); if (fmt) {
va_start(args, fmt);
vaf.fmt = fmt; vaf.fmt = fmt;
vaf.va = &args; vaf.va = &args;
fs_err(sdp, "%pV", &vaf); fs_err(sdp, "%pV", &vaf);
va_end(args); va_end(args);
}
if (sdp->sd_args.ar_errors == GFS2_ERRORS_WITHDRAW) { if (sdp->sd_args.ar_errors == GFS2_ERRORS_WITHDRAW) {
fs_err(sdp, "about to withdraw this file system\n"); fs_err(sdp, "about to withdraw this file system\n");
...@@ -246,21 +248,21 @@ int gfs2_io_error_i(struct gfs2_sbd *sdp, const char *function, char *file, ...@@ -246,21 +248,21 @@ int gfs2_io_error_i(struct gfs2_sbd *sdp, const char *function, char *file,
} }
/** /**
* gfs2_io_error_bh_i - Flag a buffer I/O error and withdraw * gfs2_io_error_bh_i - Flag a buffer I/O error
* Returns: -1 if this call withdrew the machine, * @withdraw: withdraw the filesystem
* 0 if it was already withdrawn
*/ */
int gfs2_io_error_bh_i(struct gfs2_sbd *sdp, struct buffer_head *bh, void gfs2_io_error_bh_i(struct gfs2_sbd *sdp, struct buffer_head *bh,
const char *function, char *file, unsigned int line) const char *function, char *file, unsigned int line,
bool withdraw)
{ {
int rv; fs_err(sdp,
rv = gfs2_lm_withdraw(sdp, "fatal: I/O error\n"
"fatal: I/O error\n" " block = %llu\n"
" block = %llu\n" " function = %s, file = %s, line = %u\n",
" function = %s, file = %s, line = %u\n", (unsigned long long)bh->b_blocknr,
(unsigned long long)bh->b_blocknr, function, file, line);
function, file, line); if (withdraw)
return rv; gfs2_lm_withdraw(sdp, NULL);
} }
...@@ -136,11 +136,15 @@ int gfs2_io_error_i(struct gfs2_sbd *sdp, const char *function, ...@@ -136,11 +136,15 @@ int gfs2_io_error_i(struct gfs2_sbd *sdp, const char *function,
gfs2_io_error_i((sdp), __func__, __FILE__, __LINE__); gfs2_io_error_i((sdp), __func__, __FILE__, __LINE__);
int gfs2_io_error_bh_i(struct gfs2_sbd *sdp, struct buffer_head *bh, void gfs2_io_error_bh_i(struct gfs2_sbd *sdp, struct buffer_head *bh,
const char *function, char *file, unsigned int line); const char *function, char *file, unsigned int line,
bool withdraw);
#define gfs2_io_error_bh_wd(sdp, bh) \
gfs2_io_error_bh_i((sdp), (bh), __func__, __FILE__, __LINE__, true);
#define gfs2_io_error_bh(sdp, bh) \ #define gfs2_io_error_bh(sdp, bh) \
gfs2_io_error_bh_i((sdp), (bh), __func__, __FILE__, __LINE__); gfs2_io_error_bh_i((sdp), (bh), __func__, __FILE__, __LINE__, false);
extern struct kmem_cache *gfs2_glock_cachep; extern struct kmem_cache *gfs2_glock_cachep;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment