diff --git a/fs/ext4/balloc.c b/fs/ext4/balloc.c index 2b76c794..c2a25699 100644 --- a/fs/ext4/balloc.c +++ b/fs/ext4/balloc.c @@ -484,6 +484,93 @@ out: return NULL; } +struct ext4_reada_bitmap { + struct super_block *sb; + ext4_group_t bg_start; + struct work_struct wq; +}; + +static void ext4_read_block_bitmaps(struct super_block *sb, + ext4_group_t block_group, unsigned long nr) +{ + ext4_group_t bg; + int i = 0; + struct buffer_head **bh = NULL; + ext4_group_t ngroups = ext4_get_groups_count(sb); + ext4_group_t end_group; + int read_count = 0; + + atomic_inc(&EXT4_SB(sb)->s_bbitmap_reada_flighting); + if (nr > 1048576) + nr = 1048576; + + end_group = block_group + nr - 1; + if (end_group >= ngroups) { + end_group = ngroups - 1; + nr = end_group - block_group + 1; + } + + bh = kzalloc(sizeof(struct buffer_head *) * nr, GFP_NOFS); + if (!bh) { + ext4_warning(sb, "Failed to allocate memory for bitmaps readahead"); + atomic_dec(&EXT4_SB(sb)->s_bbitmap_reada_flighting); + return; + } + + for (bg = block_group; bg <= end_group; bg++) + if (!(bh[i++] = ext4_read_block_bitmap_nowait(sb, bg))) + ext4_warning(sb, "failed to reada group's bbitmap %llu\n", + (unsigned long long)bg); + i = 0; + for (bg = block_group; bg <= end_group; bg++) { + if (bh[i]) { + if (buffer_new(bh[i])) + read_count++; + ext4_wait_block_bitmap(sb, bg, bh[i]); + } + i++; + } + + printk(KERN_ERR "async thread read bbitmaps: %d, flighting IO: %d\n", + read_count, atomic_read(&EXT4_SB(sb)->s_bbitmap_reada_flighting)); + for (i = 0; i < nr; i++) + brelse(bh[i]); + kfree(bh); + atomic_dec(&EXT4_SB(sb)->s_bbitmap_reada_flighting); +} + +static void ext4_reada_bbitmap_work(struct work_struct *work) +{ + unsigned reada_blks; + struct ext4_reada_bitmap *erb = container_of(work, + struct ext4_reada_bitmap, wq); + + reada_blks = EXT4_SB(erb->sb)->s_bbitmap_readahead_blks; + if (reada_blks) + ext4_read_block_bitmaps(erb->sb, erb->bg_start, reada_blks); + kfree(erb); +} + +void ext4_read_block_bitmaps_async(struct super_block *sb, + ext4_group_t block_group) +{ + ext4_group_t ngroups = ext4_get_groups_count(sb); + + /* sumit readahead bitmaps request if possible */ + if (EXT4_SB(sb)->s_bbitmap_readahead_blks && + block_group < ngroups && + atomic_read(&EXT4_SB(sb)->s_bbitmap_reada_flighting) < + EXT4_SB(sb)->s_bbitmap_reada_req_limit) { + struct ext4_reada_bitmap *erb = kzalloc(sizeof(*erb), GFP_NOFS); + + erb->sb = sb; + erb->bg_start = block_group; + INIT_WORK(&erb->wq, ext4_reada_bbitmap_work); + queue_work(EXT4_SB(sb)->bbitmap_reada_wq, &erb->wq); + } +} + + /* Returns 0 on success, 1 on error */ int ext4_wait_block_bitmap(struct super_block *sb, ext4_group_t block_group, struct buffer_head *bh) @@ -517,6 +604,11 @@ ext4_read_block_bitmap(struct super_block *sb, ext4_group_t block_group) bh = ext4_read_block_bitmap_nowait(sb, block_group); if (!bh) return NULL; + + /* race doesn't matter */ + if (buffer_new(bh)) + ext4_read_block_bitmaps_async(sb, block_group + 1); + if (ext4_wait_block_bitmap(sb, block_group, bh)) { put_bh(bh); return NULL; diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index d4761dfa..e0e077fe 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1412,6 +1412,9 @@ struct ext4_sb_info { unsigned int s_mb_group_prealloc; unsigned int s_max_dir_size_kb; unsigned long s_warning_dir_size; + unsigned int s_bbitmap_readahead_blks; + unsigned int s_bbitmap_reada_req_limit; + atomic_t s_bbitmap_reada_flighting; /* where last allocation was done - for stream allocation */ unsigned long s_mb_last_group; unsigned long s_mb_last_start; @@ -1449,6 +1452,9 @@ struct ext4_sb_info { /* workqueue for reserved extent conversions (buffered io) */ struct workqueue_struct *rsv_conversion_wq; + /* workqueue for bbitmap readahead*/ + struct workqueue_struct *bbitmap_reada_wq; + /* timer for periodic error stats printing */ struct timer_list s_err_report; @@ -2236,6 +2242,8 @@ extern int ext4_wait_block_bitmap(struct super_block *sb, struct buffer_head *bh); extern struct buffer_head *ext4_read_block_bitmap(struct super_block *sb, ext4_group_t block_group); +void ext4_read_block_bitmaps_async(struct super_block *sb, + ext4_group_t block_group); extern unsigned ext4_free_clusters_after_init(struct super_block *sb, ext4_group_t block_group, struct ext4_group_desc *gdp); diff --git a/fs/ext4/mballoc.c b/fs/ext4/mballoc.c index d9b00e42..7db58912 100644 --- a/fs/ext4/mballoc.c +++ b/fs/ext4/mballoc.c @@ -883,6 +883,10 @@ static int ext4_mb_init_cache(struct page *page, char *incore) mb_debug(1, "read bitmap for group %u\n", group); } + /* race dosen't matter */ + if (buffer_new(bh[i - 1])) + ext4_read_block_bitmaps_async(sb, group + 1); + /* wait for I/O completion */ for (i = 0, group = first_group; i < groups_per_page; i++, group++) { if (bh[i] && ext4_wait_block_bitmap(sb, group, bh[i])) { diff --git a/fs/ext4/super.c b/fs/ext4/super.c index fb05762e..b235c165 100644 --- a/fs/ext4/super.c +++ b/fs/ext4/super.c @@ -823,6 +823,9 @@ static void ext4_put_super(struct super_block *sb) flush_workqueue(sbi->rsv_conversion_wq); destroy_workqueue(sbi->rsv_conversion_wq); + flush_workqueue(sbi->bbitmap_reada_wq); + destroy_workqueue(sbi->bbitmap_reada_wq); + if (sbi->s_journal) { aborted = is_journal_aborted(sbi->s_journal); err = jbd2_journal_destroy(sbi->s_journal); @@ -2802,6 +2805,8 @@ EXT4_RW_ATTR_SBI_UI(warning_ratelimit_interval_ms, s_warning_ratelimit_state.int EXT4_RW_ATTR_SBI_UI(warning_ratelimit_burst, s_warning_ratelimit_state.burst); EXT4_RW_ATTR_SBI_UI(msg_ratelimit_interval_ms, s_msg_ratelimit_state.interval); EXT4_RW_ATTR_SBI_UI(msg_ratelimit_burst, s_msg_ratelimit_state.burst); +EXT4_RW_ATTR_SBI_UI(bbitmap_readahead_blks, s_bbitmap_readahead_blks); +EXT4_RW_ATTR_SBI_UI(bbitmap_reada_req_limit, s_bbitmap_reada_req_limit); EXT4_RO_ATTR_ES_UI(errors_count, s_error_count); EXT4_RO_ATTR_ES_UI(first_error_time, s_first_error_time); EXT4_RO_ATTR_ES_UI(last_error_time, s_last_error_time); @@ -2832,6 +2837,8 @@ static struct attribute *ext4_attrs[] = { ATTR_LIST(warning_ratelimit_burst), ATTR_LIST(msg_ratelimit_interval_ms), ATTR_LIST(msg_ratelimit_burst), + ATTR_LIST(bbitmap_readahead_blks), + ATTR_LIST(bbitmap_reada_req_limit), ATTR_LIST(errors_count), ATTR_LIST(first_error_time), ATTR_LIST(last_error_time), @@ -4305,6 +4312,14 @@ no_journal: goto failed_mount4; } + sbi->s_bbitmap_reada_req_limit = 1; /* default is 1 */ + sbi->bbitmap_reada_wq = alloc_workqueue("ext4-bbitmap-reada", WQ_UNBOUND, 0); + if (!sbi->rsv_conversion_wq) { + printk(KERN_ERR "EXT4-fs: failed to create bbitmap reada workqueue\n"); + ret = -ENOMEM; + goto failed_mount4; + } + /* * The jbd2_journal_load will have done any necessary log recovery, * so we can safely mount the rest of the filesystem now. @@ -4500,6 +4515,8 @@ failed_mount4: ext4_msg(sb, KERN_ERR, "mount failed"); if (EXT4_SB(sb)->rsv_conversion_wq) destroy_workqueue(EXT4_SB(sb)->rsv_conversion_wq); + if (EXT4_SB(sb)->bbitmap_reada_wq) + destroy_workqueue(EXT4_SB(sb)->bbitmap_reada_wq); failed_mount_wq: if (sbi->s_journal) { jbd2_journal_destroy(sbi->s_journal);