diff options
Diffstat (limited to 'fs/gfs2/log.c')
| -rw-r--r-- | fs/gfs2/log.c | 206 | 
1 files changed, 118 insertions, 88 deletions
diff --git a/fs/gfs2/log.c b/fs/gfs2/log.c index 5b102c1887f..903115f2bb3 100644 --- a/fs/gfs2/log.c +++ b/fs/gfs2/log.c @@ -18,6 +18,7 @@  #include <linux/kthread.h>  #include <linux/freezer.h>  #include <linux/bio.h> +#include <linux/writeback.h>  #include "gfs2.h"  #include "incore.h" @@ -83,55 +84,97 @@ void gfs2_remove_from_ail(struct gfs2_bufdata *bd)  /**   * gfs2_ail1_start_one - Start I/O on a part of the AIL   * @sdp: the filesystem - * @tr: the part of the AIL + * @wbc: The writeback control structure + * @ai: The ail structure   *   */ -static void gfs2_ail1_start_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai) +static int gfs2_ail1_start_one(struct gfs2_sbd *sdp, +			       struct writeback_control *wbc, +			       struct gfs2_ail *ai)  __releases(&sdp->sd_ail_lock)  __acquires(&sdp->sd_ail_lock)  { +	struct gfs2_glock *gl = NULL; +	struct address_space *mapping;  	struct gfs2_bufdata *bd, *s;  	struct buffer_head *bh; -	int retry; -	do { -		retry = 0; +	list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list, bd_ail_st_list) { +		bh = bd->bd_bh; -		list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list, -						 bd_ail_st_list) { -			bh = bd->bd_bh; +		gfs2_assert(sdp, bd->bd_ail == ai); -			gfs2_assert(sdp, bd->bd_ail == ai); +		if (!buffer_busy(bh)) { +			if (!buffer_uptodate(bh)) +				gfs2_io_error_bh(sdp, bh); +			list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list); +			continue; +		} -			if (!buffer_busy(bh)) { -				if (!buffer_uptodate(bh)) -					gfs2_io_error_bh(sdp, bh); -				list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list); -				continue; -			} +		if (!buffer_dirty(bh)) +			continue; +		if (gl == bd->bd_gl) +			continue; +		gl = bd->bd_gl; +		list_move(&bd->bd_ail_st_list, &ai->ai_ail1_list); +		mapping = bh->b_page->mapping; +		if (!mapping) +			continue; +		spin_unlock(&sdp->sd_ail_lock); +		generic_writepages(mapping, wbc); +		spin_lock(&sdp->sd_ail_lock); +		if (wbc->nr_to_write <= 0) +			break; +		return 1; +	} -			if (!buffer_dirty(bh)) -				continue; +	return 0; +} -			list_move(&bd->bd_ail_st_list, &ai->ai_ail1_list); -			get_bh(bh); -			spin_unlock(&sdp->sd_ail_lock); -			lock_buffer(bh); -			if (test_clear_buffer_dirty(bh)) { -				bh->b_end_io = end_buffer_write_sync; -				submit_bh(WRITE_SYNC, bh); -			} else { -				unlock_buffer(bh); -				brelse(bh); -			} -			spin_lock(&sdp->sd_ail_lock); +/** + * gfs2_ail1_flush - start writeback of some ail1 entries  + * @sdp: The super block + * @wbc: The writeback control structure + * + * Writes back some ail1 entries, according to the limits in the + * writeback control structure + */ -			retry = 1; +void gfs2_ail1_flush(struct gfs2_sbd *sdp, struct writeback_control *wbc) +{ +	struct list_head *head = &sdp->sd_ail1_list; +	struct gfs2_ail *ai; + +	trace_gfs2_ail_flush(sdp, wbc, 1); +	spin_lock(&sdp->sd_ail_lock); +restart: +	list_for_each_entry_reverse(ai, head, ai_list) { +		if (wbc->nr_to_write <= 0)  			break; -		} -	} while (retry); +		if (gfs2_ail1_start_one(sdp, wbc, ai)) +			goto restart; +	} +	spin_unlock(&sdp->sd_ail_lock); +	trace_gfs2_ail_flush(sdp, wbc, 0); +} + +/** + * gfs2_ail1_start - start writeback of all ail1 entries + * @sdp: The superblock + */ + +static void gfs2_ail1_start(struct gfs2_sbd *sdp) +{ +	struct writeback_control wbc = { +		.sync_mode = WB_SYNC_NONE, +		.nr_to_write = LONG_MAX, +		.range_start = 0, +		.range_end = LLONG_MAX, +	}; + +	return gfs2_ail1_flush(sdp, &wbc);  }  /** @@ -141,7 +184,7 @@ __acquires(&sdp->sd_ail_lock)   *   */ -static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int flags) +static void gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai)  {  	struct gfs2_bufdata *bd, *s;  	struct buffer_head *bh; @@ -149,76 +192,63 @@ static int gfs2_ail1_empty_one(struct gfs2_sbd *sdp, struct gfs2_ail *ai, int fl  	list_for_each_entry_safe_reverse(bd, s, &ai->ai_ail1_list,  					 bd_ail_st_list) {  		bh = bd->bd_bh; -  		gfs2_assert(sdp, bd->bd_ail == ai); - -		if (buffer_busy(bh)) { -			if (flags & DIO_ALL) -				continue; -			else -				break; -		} - +		if (buffer_busy(bh)) +			continue;  		if (!buffer_uptodate(bh))  			gfs2_io_error_bh(sdp, bh); -  		list_move(&bd->bd_ail_st_list, &ai->ai_ail2_list);  	} -	return list_empty(&ai->ai_ail1_list);  } -static void gfs2_ail1_start(struct gfs2_sbd *sdp) -{ -	struct list_head *head; -	u64 sync_gen; -	struct gfs2_ail *ai; -	int done = 0; - -	spin_lock(&sdp->sd_ail_lock); -	head = &sdp->sd_ail1_list; -	if (list_empty(head)) { -		spin_unlock(&sdp->sd_ail_lock); -		return; -	} -	sync_gen = sdp->sd_ail_sync_gen++; - -	while(!done) { -		done = 1; -		list_for_each_entry_reverse(ai, head, ai_list) { -			if (ai->ai_sync_gen >= sync_gen) -				continue; -			ai->ai_sync_gen = sync_gen; -			gfs2_ail1_start_one(sdp, ai); /* This may drop ail lock */ -			done = 0; -			break; -		} -	} - -	spin_unlock(&sdp->sd_ail_lock); -} +/** + * gfs2_ail1_empty - Try to empty the ail1 lists + * @sdp: The superblock + * + * Tries to empty the ail1 lists, starting with the oldest first + */ -static int gfs2_ail1_empty(struct gfs2_sbd *sdp, int flags) +static int gfs2_ail1_empty(struct gfs2_sbd *sdp)  {  	struct gfs2_ail *ai, *s;  	int ret;  	spin_lock(&sdp->sd_ail_lock); -  	list_for_each_entry_safe_reverse(ai, s, &sdp->sd_ail1_list, ai_list) { -		if (gfs2_ail1_empty_one(sdp, ai, flags)) +		gfs2_ail1_empty_one(sdp, ai); +		if (list_empty(&ai->ai_ail1_list))  			list_move(&ai->ai_list, &sdp->sd_ail2_list); -		else if (!(flags & DIO_ALL)) +		else  			break;  	} -  	ret = list_empty(&sdp->sd_ail1_list); -  	spin_unlock(&sdp->sd_ail_lock);  	return ret;  } +static void gfs2_ail1_wait(struct gfs2_sbd *sdp) +{ +	struct gfs2_ail *ai; +	struct gfs2_bufdata *bd; +	struct buffer_head *bh; + +	spin_lock(&sdp->sd_ail_lock); +	list_for_each_entry_reverse(ai, &sdp->sd_ail1_list, ai_list) { +		list_for_each_entry(bd, &ai->ai_ail1_list, bd_ail_st_list) { +			bh = bd->bd_bh; +			if (!buffer_locked(bh)) +				continue; +			get_bh(bh); +			spin_unlock(&sdp->sd_ail_lock); +			wait_on_buffer(bh); +			brelse(bh); +			return; +		} +	} +	spin_unlock(&sdp->sd_ail_lock); +}  /**   * gfs2_ail2_empty_one - Check whether or not a trans in the AIL has been synced @@ -574,7 +604,7 @@ static void log_write_header(struct gfs2_sbd *sdp, u32 flags, int pull)  	set_buffer_uptodate(bh);  	clear_buffer_dirty(bh); -	gfs2_ail1_empty(sdp, 0); +	gfs2_ail1_empty(sdp);  	tail = current_tail(sdp);  	lh = (struct gfs2_log_header *)bh->b_data; @@ -869,9 +899,9 @@ void gfs2_meta_syncfs(struct gfs2_sbd *sdp)  	gfs2_log_flush(sdp, NULL);  	for (;;) {  		gfs2_ail1_start(sdp); -		if (gfs2_ail1_empty(sdp, DIO_ALL)) +		gfs2_ail1_wait(sdp); +		if (gfs2_ail1_empty(sdp))  			break; -		msleep(10);  	}  } @@ -905,20 +935,20 @@ int gfs2_logd(void *data)  		preflush = atomic_read(&sdp->sd_log_pinned);  		if (gfs2_jrnl_flush_reqd(sdp) || t == 0) { -			gfs2_ail1_empty(sdp, DIO_ALL); +			gfs2_ail1_empty(sdp);  			gfs2_log_flush(sdp, NULL); -			gfs2_ail1_empty(sdp, DIO_ALL);  		}  		if (gfs2_ail_flush_reqd(sdp)) {  			gfs2_ail1_start(sdp); -			io_schedule(); -			gfs2_ail1_empty(sdp, 0); +			gfs2_ail1_wait(sdp); +			gfs2_ail1_empty(sdp);  			gfs2_log_flush(sdp, NULL); -			gfs2_ail1_empty(sdp, DIO_ALL);  		} -		wake_up(&sdp->sd_log_waitq); +		if (!gfs2_ail_flush_reqd(sdp)) +			wake_up(&sdp->sd_log_waitq); +  		t = gfs2_tune_get(sdp, gt_logd_secs) * HZ;  		if (freezing(current))  			refrigerator();  |