diff options
Diffstat (limited to 'fs/f2fs/segment.c')
| -rw-r--r-- | fs/f2fs/segment.c | 240 | 
1 files changed, 175 insertions, 65 deletions
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index fc886f008449..0738f48293cc 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -259,7 +259,7 @@ static int __commit_inmem_pages(struct inode *inode,  		.sbi = sbi,  		.type = DATA,  		.op = REQ_OP_WRITE, -		.op_flags = WRITE_SYNC | REQ_PRIO, +		.op_flags = REQ_SYNC | REQ_PRIO,  		.encrypted_page = NULL,  	};  	bool submit_bio = false; @@ -274,8 +274,10 @@ static int __commit_inmem_pages(struct inode *inode,  			set_page_dirty(page);  			f2fs_wait_on_page_writeback(page, DATA, true); -			if (clear_page_dirty_for_io(page)) +			if (clear_page_dirty_for_io(page)) {  				inode_dec_dirty_pages(inode); +				remove_dirty_inode(inode); +			}  			fio.page = page;  			err = do_write_data_page(&fio); @@ -287,7 +289,6 @@ static int __commit_inmem_pages(struct inode *inode,  			/* record old blkaddr for revoking */  			cur->old_addr = fio.old_blkaddr; -			clear_cold_data(page);  			submit_bio = true;  		}  		unlock_page(page); @@ -363,7 +364,7 @@ void f2fs_balance_fs(struct f2fs_sb_info *sbi, bool need)  	 */  	if (has_not_enough_free_secs(sbi, 0, 0)) {  		mutex_lock(&sbi->gc_mutex); -		f2fs_gc(sbi, false); +		f2fs_gc(sbi, false, false);  	}  } @@ -380,14 +381,17 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi)  	if (!available_free_memory(sbi, FREE_NIDS))  		try_to_free_nids(sbi, MAX_FREE_NIDS);  	else -		build_free_nids(sbi); +		build_free_nids(sbi, false); + +	if (!is_idle(sbi)) +		return;  	/* checkpoint is the only way to shrink partial cached entries */  	if (!available_free_memory(sbi, NAT_ENTRIES) ||  			!available_free_memory(sbi, INO_ENTRIES) ||  			excess_prefree_segs(sbi) ||  			excess_dirty_nats(sbi) || -			(is_idle(sbi) && f2fs_time_over(sbi, CP_TIME))) { +			f2fs_time_over(sbi, CP_TIME)) {  		if (test_opt(sbi, DATA_FLUSH)) {  			struct blk_plug plug; @@ -400,6 +404,33 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi)  	}  } +static int __submit_flush_wait(struct block_device *bdev) +{ +	struct bio *bio = f2fs_bio_alloc(0); +	int ret; + +	bio->bi_opf = REQ_OP_WRITE | REQ_PREFLUSH; +	bio->bi_bdev = bdev; +	ret = submit_bio_wait(bio); +	bio_put(bio); +	return ret; +} + +static int submit_flush_wait(struct f2fs_sb_info *sbi) +{ +	int ret = __submit_flush_wait(sbi->sb->s_bdev); +	int i; + +	if (sbi->s_ndevs && !ret) { +		for (i = 1; i < sbi->s_ndevs; i++) { +			ret = __submit_flush_wait(FDEV(i).bdev); +			if (ret) +				break; +		} +	} +	return ret; +} +  static int issue_flush_thread(void *data)  {  	struct f2fs_sb_info *sbi = data; @@ -410,25 +441,18 @@ repeat:  		return 0;  	if (!llist_empty(&fcc->issue_list)) { -		struct bio *bio;  		struct flush_cmd *cmd, *next;  		int ret; -		bio = f2fs_bio_alloc(0); -  		fcc->dispatch_list = llist_del_all(&fcc->issue_list);  		fcc->dispatch_list = llist_reverse_order(fcc->dispatch_list); -		bio->bi_bdev = sbi->sb->s_bdev; -		bio_set_op_attrs(bio, REQ_OP_WRITE, WRITE_FLUSH); -		ret = submit_bio_wait(bio); - +		ret = submit_flush_wait(sbi);  		llist_for_each_entry_safe(cmd, next,  					  fcc->dispatch_list, llnode) {  			cmd->ret = ret;  			complete(&cmd->wait);  		} -		bio_put(bio);  		fcc->dispatch_list = NULL;  	} @@ -449,15 +473,11 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi)  		return 0;  	if (!test_opt(sbi, FLUSH_MERGE) || !atomic_read(&fcc->submit_flush)) { -		struct bio *bio = f2fs_bio_alloc(0);  		int ret;  		atomic_inc(&fcc->submit_flush); -		bio->bi_bdev = sbi->sb->s_bdev; -		bio_set_op_attrs(bio, REQ_OP_WRITE, WRITE_FLUSH); -		ret = submit_bio_wait(bio); +		ret = submit_flush_wait(sbi);  		atomic_dec(&fcc->submit_flush); -		bio_put(bio);  		return ret;  	} @@ -469,8 +489,13 @@ int f2fs_issue_flush(struct f2fs_sb_info *sbi)  	if (!fcc->dispatch_list)  		wake_up(&fcc->flush_wait_queue); -	wait_for_completion(&cmd.wait); -	atomic_dec(&fcc->submit_flush); +	if (fcc->f2fs_issue_flush) { +		wait_for_completion(&cmd.wait); +		atomic_dec(&fcc->submit_flush); +	} else { +		llist_del_all(&fcc->issue_list); +		atomic_set(&fcc->submit_flush, 0); +	}  	return cmd.ret;  } @@ -481,6 +506,11 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi)  	struct flush_cmd_control *fcc;  	int err = 0; +	if (SM_I(sbi)->cmd_control_info) { +		fcc = SM_I(sbi)->cmd_control_info; +		goto init_thread; +	} +  	fcc = kzalloc(sizeof(struct flush_cmd_control), GFP_KERNEL);  	if (!fcc)  		return -ENOMEM; @@ -488,6 +518,7 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi)  	init_waitqueue_head(&fcc->flush_wait_queue);  	init_llist_head(&fcc->issue_list);  	SM_I(sbi)->cmd_control_info = fcc; +init_thread:  	fcc->f2fs_issue_flush = kthread_run(issue_flush_thread, sbi,  				"f2fs_flush-%u:%u", MAJOR(dev), MINOR(dev));  	if (IS_ERR(fcc->f2fs_issue_flush)) { @@ -500,14 +531,20 @@ int create_flush_cmd_control(struct f2fs_sb_info *sbi)  	return err;  } -void destroy_flush_cmd_control(struct f2fs_sb_info *sbi) +void destroy_flush_cmd_control(struct f2fs_sb_info *sbi, bool free)  {  	struct flush_cmd_control *fcc = SM_I(sbi)->cmd_control_info; -	if (fcc && fcc->f2fs_issue_flush) -		kthread_stop(fcc->f2fs_issue_flush); -	kfree(fcc); -	SM_I(sbi)->cmd_control_info = NULL; +	if (fcc && fcc->f2fs_issue_flush) { +		struct task_struct *flush_thread = fcc->f2fs_issue_flush; + +		fcc->f2fs_issue_flush = NULL; +		kthread_stop(flush_thread); +	} +	if (free) { +		kfree(fcc); +		SM_I(sbi)->cmd_control_info = NULL; +	}  }  static void __locate_dirty_segment(struct f2fs_sb_info *sbi, unsigned int segno, @@ -633,15 +670,23 @@ static void f2fs_submit_bio_wait_endio(struct bio *bio)  }  /* this function is copied from blkdev_issue_discard from block/blk-lib.c */ -int __f2fs_issue_discard_async(struct f2fs_sb_info *sbi, sector_t sector, -		sector_t nr_sects, gfp_t gfp_mask, unsigned long flags) +static int __f2fs_issue_discard_async(struct f2fs_sb_info *sbi, +		struct block_device *bdev, block_t blkstart, block_t blklen)  { -	struct block_device *bdev = sbi->sb->s_bdev;  	struct bio *bio = NULL;  	int err; -	err = __blkdev_issue_discard(bdev, sector, nr_sects, gfp_mask, flags, -			&bio); +	trace_f2fs_issue_discard(sbi->sb, blkstart, blklen); + +	if (sbi->s_ndevs) { +		int devi = f2fs_target_device_index(sbi, blkstart); + +		blkstart -= FDEV(devi).start_blk; +	} +	err = __blkdev_issue_discard(bdev, +				SECTOR_FROM_BLOCK(blkstart), +				SECTOR_FROM_BLOCK(blklen), +				GFP_NOFS, 0, &bio);  	if (!err && bio) {  		struct bio_entry *be = __add_bio_entry(sbi, bio); @@ -654,24 +699,101 @@ int __f2fs_issue_discard_async(struct f2fs_sb_info *sbi, sector_t sector,  	return err;  } +#ifdef CONFIG_BLK_DEV_ZONED +static int __f2fs_issue_discard_zone(struct f2fs_sb_info *sbi, +		struct block_device *bdev, block_t blkstart, block_t blklen) +{ +	sector_t nr_sects = SECTOR_FROM_BLOCK(blklen); +	sector_t sector; +	int devi = 0; + +	if (sbi->s_ndevs) { +		devi = f2fs_target_device_index(sbi, blkstart); +		blkstart -= FDEV(devi).start_blk; +	} +	sector = SECTOR_FROM_BLOCK(blkstart); + +	if (sector & (bdev_zone_size(bdev) - 1) || +				nr_sects != bdev_zone_size(bdev)) { +		f2fs_msg(sbi->sb, KERN_INFO, +			"(%d) %s: Unaligned discard attempted (block %x + %x)", +			devi, sbi->s_ndevs ? FDEV(devi).path: "", +			blkstart, blklen); +		return -EIO; +	} + +	/* +	 * We need to know the type of the zone: for conventional zones, +	 * use regular discard if the drive supports it. For sequential +	 * zones, reset the zone write pointer. +	 */ +	switch (get_blkz_type(sbi, bdev, blkstart)) { + +	case BLK_ZONE_TYPE_CONVENTIONAL: +		if (!blk_queue_discard(bdev_get_queue(bdev))) +			return 0; +		return __f2fs_issue_discard_async(sbi, bdev, blkstart, blklen); +	case BLK_ZONE_TYPE_SEQWRITE_REQ: +	case BLK_ZONE_TYPE_SEQWRITE_PREF: +		trace_f2fs_issue_reset_zone(sbi->sb, blkstart); +		return blkdev_reset_zones(bdev, sector, +					  nr_sects, GFP_NOFS); +	default: +		/* Unknown zone type: broken device ? */ +		return -EIO; +	} +} +#endif + +static int __issue_discard_async(struct f2fs_sb_info *sbi, +		struct block_device *bdev, block_t blkstart, block_t blklen) +{ +#ifdef CONFIG_BLK_DEV_ZONED +	if (f2fs_sb_mounted_blkzoned(sbi->sb) && +				bdev_zoned_model(bdev) != BLK_ZONED_NONE) +		return __f2fs_issue_discard_zone(sbi, bdev, blkstart, blklen); +#endif +	return __f2fs_issue_discard_async(sbi, bdev, blkstart, blklen); +} +  static int f2fs_issue_discard(struct f2fs_sb_info *sbi,  				block_t blkstart, block_t blklen)  { -	sector_t start = SECTOR_FROM_BLOCK(blkstart); -	sector_t len = SECTOR_FROM_BLOCK(blklen); +	sector_t start = blkstart, len = 0; +	struct block_device *bdev;  	struct seg_entry *se;  	unsigned int offset;  	block_t i; +	int err = 0; + +	bdev = f2fs_target_device(sbi, blkstart, NULL); + +	for (i = blkstart; i < blkstart + blklen; i++, len++) { +		if (i != start) { +			struct block_device *bdev2 = +				f2fs_target_device(sbi, i, NULL); + +			if (bdev2 != bdev) { +				err = __issue_discard_async(sbi, bdev, +						start, len); +				if (err) +					return err; +				bdev = bdev2; +				start = i; +				len = 0; +			} +		} -	for (i = blkstart; i < blkstart + blklen; i++) {  		se = get_seg_entry(sbi, GET_SEGNO(sbi, i));  		offset = GET_BLKOFF_FROM_SEG0(sbi, i);  		if (!f2fs_test_and_set_bit(offset, se->discard_map))  			sbi->discard_blks--;  	} -	trace_f2fs_issue_discard(sbi->sb, blkstart, blklen); -	return __f2fs_issue_discard_async(sbi, start, len, GFP_NOFS, 0); + +	if (len) +		err = __issue_discard_async(sbi, bdev, start, len); +	return err;  }  static void __add_discard_entry(struct f2fs_sb_info *sbi, @@ -1296,25 +1418,21 @@ static void allocate_segment_by_default(struct f2fs_sb_info *sbi,  	stat_inc_seg_type(sbi, curseg);  } -static void __allocate_new_segments(struct f2fs_sb_info *sbi, int type) -{ -	struct curseg_info *curseg = CURSEG_I(sbi, type); -	unsigned int old_segno; - -	old_segno = curseg->segno; -	SIT_I(sbi)->s_ops->allocate_segment(sbi, type, true); -	locate_dirty_segment(sbi, old_segno); -} -  void allocate_new_segments(struct f2fs_sb_info *sbi)  { +	struct curseg_info *curseg; +	unsigned int old_segno;  	int i;  	if (test_opt(sbi, LFS))  		return; -	for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) -		__allocate_new_segments(sbi, i); +	for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { +		curseg = CURSEG_I(sbi, i); +		old_segno = curseg->segno; +		SIT_I(sbi)->s_ops->allocate_segment(sbi, i, true); +		locate_dirty_segment(sbi, old_segno); +	}  }  static const struct segment_allocation default_salloc_ops = { @@ -1448,21 +1566,11 @@ void allocate_data_block(struct f2fs_sb_info *sbi, struct page *page,  		struct f2fs_summary *sum, int type)  {  	struct sit_info *sit_i = SIT_I(sbi); -	struct curseg_info *curseg; -	bool direct_io = (type == CURSEG_DIRECT_IO); - -	type = direct_io ? CURSEG_WARM_DATA : type; - -	curseg = CURSEG_I(sbi, type); +	struct curseg_info *curseg = CURSEG_I(sbi, type);  	mutex_lock(&curseg->curseg_mutex);  	mutex_lock(&sit_i->sentry_lock); -	/* direct_io'ed data is aligned to the segment for better performance */ -	if (direct_io && curseg->next_blkoff && -				!has_not_enough_free_secs(sbi, 0, 0)) -		__allocate_new_segments(sbi, type); -  	*new_blkaddr = NEXT_FREE_BLKADDR(sbi, curseg);  	/* @@ -1515,7 +1623,7 @@ void write_meta_page(struct f2fs_sb_info *sbi, struct page *page)  		.sbi = sbi,  		.type = META,  		.op = REQ_OP_WRITE, -		.op_flags = WRITE_SYNC | REQ_META | REQ_PRIO, +		.op_flags = REQ_SYNC | REQ_META | REQ_PRIO,  		.old_blkaddr = page->index,  		.new_blkaddr = page->index,  		.page = page, @@ -2166,7 +2274,6 @@ out:  static int build_sit_info(struct f2fs_sb_info *sbi)  {  	struct f2fs_super_block *raw_super = F2FS_RAW_SUPER(sbi); -	struct f2fs_checkpoint *ckpt = F2FS_CKPT(sbi);  	struct sit_info *sit_i;  	unsigned int sit_segs, start;  	char *src_bitmap, *dst_bitmap; @@ -2233,7 +2340,7 @@ static int build_sit_info(struct f2fs_sb_info *sbi)  	sit_i->sit_base_addr = le32_to_cpu(raw_super->sit_blkaddr);  	sit_i->sit_blocks = sit_segs << sbi->log_blocks_per_seg; -	sit_i->written_valid_blocks = le64_to_cpu(ckpt->valid_block_count); +	sit_i->written_valid_blocks = 0;  	sit_i->sit_bitmap = dst_bitmap;  	sit_i->bitmap_size = bitmap_size;  	sit_i->dirty_sentries = 0; @@ -2315,10 +2422,10 @@ static void build_sit_entries(struct f2fs_sb_info *sbi)  	int sit_blk_cnt = SIT_BLK_CNT(sbi);  	unsigned int i, start, end;  	unsigned int readed, start_blk = 0; -	int nrpages = MAX_BIO_BLOCKS(sbi) * 8;  	do { -		readed = ra_meta_pages(sbi, start_blk, nrpages, META_SIT, true); +		readed = ra_meta_pages(sbi, start_blk, BIO_MAX_PAGES, +							META_SIT, true);  		start = start_blk * sit_i->sents_per_block;  		end = (start_blk + readed) * sit_i->sents_per_block; @@ -2387,6 +2494,9 @@ static void init_free_segmap(struct f2fs_sb_info *sbi)  		struct seg_entry *sentry = get_seg_entry(sbi, start);  		if (!sentry->valid_blocks)  			__set_free(sbi, start); +		else +			SIT_I(sbi)->written_valid_blocks += +						sentry->valid_blocks;  	}  	/* set use the current segments */ @@ -2645,7 +2755,7 @@ void destroy_segment_manager(struct f2fs_sb_info *sbi)  	if (!sm_info)  		return; -	destroy_flush_cmd_control(sbi); +	destroy_flush_cmd_control(sbi, true);  	destroy_dirty_segmap(sbi);  	destroy_curseg(sbi);  	destroy_free_segmap(sbi);  |