GFS2: don't overrun reserved revokes

When run during fsync, a gfs2_log_flush could happen between the
time when gfs2_ail_flush checked the number of blocks to revoke,
and when it actually started the transaction to do those revokes.
This occassionally caused it to need more revokes than it reserved,
causing gfs2 to crash.

Instead of just reserving enough revokes to handle the blocks that
currently need them, this patch makes gfs2_ail_flush reserve the
maximum number of revokes it can, without increasing the total number
of reserved log blocks. This patch also passes the number of reserved
revokes to __gfs2_ail_flush() so that it doesn't go over its limit
and cause a crash like we're seeing. Non-fsync calls to __gfs2_ail_flush
will still cause a BUG() necessary revokes are skipped.

Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Steven Whitehouse <swhiteho@redhat.com>
This commit is contained in:
Benjamin Marzinski 2013-07-26 17:09:33 -05:00 committed by Steven Whitehouse
parent d08fa65a81
commit 1bc333f4cf
1 changed files with 13 additions and 5 deletions

View File

@ -47,7 +47,8 @@ static void gfs2_ail_error(struct gfs2_glock *gl, const struct buffer_head *bh)
* None of the buffers should be dirty, locked, or pinned. * None of the buffers should be dirty, locked, or pinned.
*/ */
static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync) static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync,
unsigned int nr_revokes)
{ {
struct gfs2_sbd *sdp = gl->gl_sbd; struct gfs2_sbd *sdp = gl->gl_sbd;
struct list_head *head = &gl->gl_ail_list; struct list_head *head = &gl->gl_ail_list;
@ -57,7 +58,9 @@ static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync)
gfs2_log_lock(sdp); gfs2_log_lock(sdp);
spin_lock(&sdp->sd_ail_lock); spin_lock(&sdp->sd_ail_lock);
list_for_each_entry_safe(bd, tmp, head, bd_ail_gl_list) { list_for_each_entry_safe_reverse(bd, tmp, head, bd_ail_gl_list) {
if (nr_revokes == 0)
break;
bh = bd->bd_bh; bh = bd->bd_bh;
if (bh->b_state & b_state) { if (bh->b_state & b_state) {
if (fsync) if (fsync)
@ -65,6 +68,7 @@ static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync)
gfs2_ail_error(gl, bh); gfs2_ail_error(gl, bh);
} }
gfs2_trans_add_revoke(sdp, bd); gfs2_trans_add_revoke(sdp, bd);
nr_revokes--;
} }
GLOCK_BUG_ON(gl, !fsync && atomic_read(&gl->gl_ail_count)); GLOCK_BUG_ON(gl, !fsync && atomic_read(&gl->gl_ail_count));
spin_unlock(&sdp->sd_ail_lock); spin_unlock(&sdp->sd_ail_lock);
@ -91,7 +95,7 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl)
WARN_ON_ONCE(current->journal_info); WARN_ON_ONCE(current->journal_info);
current->journal_info = &tr; current->journal_info = &tr;
__gfs2_ail_flush(gl, 0); __gfs2_ail_flush(gl, 0, tr.tr_revokes);
gfs2_trans_end(sdp); gfs2_trans_end(sdp);
gfs2_log_flush(sdp, NULL); gfs2_log_flush(sdp, NULL);
@ -101,15 +105,19 @@ void gfs2_ail_flush(struct gfs2_glock *gl, bool fsync)
{ {
struct gfs2_sbd *sdp = gl->gl_sbd; struct gfs2_sbd *sdp = gl->gl_sbd;
unsigned int revokes = atomic_read(&gl->gl_ail_count); unsigned int revokes = atomic_read(&gl->gl_ail_count);
unsigned int max_revokes = (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_log_descriptor)) / sizeof(u64);
int ret; int ret;
if (!revokes) if (!revokes)
return; return;
ret = gfs2_trans_begin(sdp, 0, revokes); while (revokes > max_revokes)
max_revokes += (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header)) / sizeof(u64);
ret = gfs2_trans_begin(sdp, 0, max_revokes);
if (ret) if (ret)
return; return;
__gfs2_ail_flush(gl, fsync); __gfs2_ail_flush(gl, fsync, max_revokes);
gfs2_trans_end(sdp); gfs2_trans_end(sdp);
gfs2_log_flush(sdp, NULL); gfs2_log_flush(sdp, NULL);
} }