diff options
Diffstat (limited to 'fs/f2fs/segment.c')
| -rw-r--r-- | fs/f2fs/segment.c | 122 | 
1 files changed, 83 insertions, 39 deletions
diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 923cb76fdc46..42607a679923 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -178,17 +178,47 @@ void register_inmem_page(struct inode *inode, struct page *page)  {  	struct f2fs_inode_info *fi = F2FS_I(inode);  	struct inmem_pages *new; +	int err; + +	SetPagePrivate(page);  	new = f2fs_kmem_cache_alloc(inmem_entry_slab, GFP_NOFS);  	/* add atomic page indices to the list */  	new->page = page;  	INIT_LIST_HEAD(&new->list); - +retry:  	/* increase reference count with clean state */  	mutex_lock(&fi->inmem_lock); +	err = radix_tree_insert(&fi->inmem_root, page->index, new); +	if (err == -EEXIST) { +		mutex_unlock(&fi->inmem_lock); +		kmem_cache_free(inmem_entry_slab, new); +		return; +	} else if (err) { +		mutex_unlock(&fi->inmem_lock); +		goto retry; +	}  	get_page(page);  	list_add_tail(&new->list, &fi->inmem_pages); +	inc_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES); +	mutex_unlock(&fi->inmem_lock); +} + +void invalidate_inmem_page(struct inode *inode, struct page *page) +{ +	struct f2fs_inode_info *fi = F2FS_I(inode); +	struct inmem_pages *cur; + +	mutex_lock(&fi->inmem_lock); +	cur = radix_tree_lookup(&fi->inmem_root, page->index); +	if (cur) { +		radix_tree_delete(&fi->inmem_root, cur->page->index); +		f2fs_put_page(cur->page, 0); +		list_del(&cur->list); +		kmem_cache_free(inmem_entry_slab, cur); +		dec_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES); +	}  	mutex_unlock(&fi->inmem_lock);  } @@ -203,7 +233,16 @@ void commit_inmem_pages(struct inode *inode, bool abort)  		.rw = WRITE_SYNC,  	}; -	f2fs_balance_fs(sbi); +	/* +	 * The abort is true only when f2fs_evict_inode is called. +	 * Basically, the f2fs_evict_inode doesn't produce any data writes, so +	 * that we don't need to call f2fs_balance_fs. +	 * Otherwise, f2fs_gc in f2fs_balance_fs can wait forever until this +	 * inode becomes free by iget_locked in f2fs_iget. +	 */ +	if (!abort) +		f2fs_balance_fs(sbi); +  	f2fs_lock_op(sbi);  	mutex_lock(&fi->inmem_lock); @@ -216,9 +255,11 @@ void commit_inmem_pages(struct inode *inode, bool abort)  			do_write_data_page(cur->page, &fio);  			submit_bio = true;  		} +		radix_tree_delete(&fi->inmem_root, cur->page->index);  		f2fs_put_page(cur->page, 1);  		list_del(&cur->list);  		kmem_cache_free(inmem_entry_slab, cur); +		dec_page_count(F2FS_I_SB(inode), F2FS_INMEM_PAGES);  	}  	if (submit_bio)  		f2fs_submit_merged_bio(sbi, DATA, WRITE); @@ -248,7 +289,8 @@ void f2fs_balance_fs_bg(struct f2fs_sb_info *sbi)  {  	/* check the # of cached NAT entries and prefree segments */  	if (try_to_free_nats(sbi, NAT_ENTRY_PER_BLOCK) || -				excess_prefree_segs(sbi)) +			excess_prefree_segs(sbi) || +			available_free_memory(sbi, INO_ENTRIES))  		f2fs_sync_fs(sbi->sb, true);  } @@ -441,10 +483,33 @@ void discard_next_dnode(struct f2fs_sb_info *sbi, block_t blkaddr)  	}  } -static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc) +static void __add_discard_entry(struct f2fs_sb_info *sbi, +		struct cp_control *cpc, unsigned int start, unsigned int end)  {  	struct list_head *head = &SM_I(sbi)->discard_list; -	struct discard_entry *new; +	struct discard_entry *new, *last; + +	if (!list_empty(head)) { +		last = list_last_entry(head, struct discard_entry, list); +		if (START_BLOCK(sbi, cpc->trim_start) + start == +						last->blkaddr + last->len) { +			last->len += end - start; +			goto done; +		} +	} + +	new = f2fs_kmem_cache_alloc(discard_entry_slab, GFP_NOFS); +	INIT_LIST_HEAD(&new->list); +	new->blkaddr = START_BLOCK(sbi, cpc->trim_start) + start; +	new->len = end - start; +	list_add_tail(&new->list, head); +done: +	SM_I(sbi)->nr_discards += end - start; +	cpc->trimmed += end - start; +} + +static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc) +{  	int entries = SIT_VBLOCK_MAP_SIZE / sizeof(unsigned long);  	int max_blocks = sbi->blocks_per_seg;  	struct seg_entry *se = get_seg_entry(sbi, cpc->trim_start); @@ -473,13 +538,7 @@ static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc)  		}  		mutex_unlock(&dirty_i->seglist_lock); -		new = f2fs_kmem_cache_alloc(discard_entry_slab, GFP_NOFS); -		INIT_LIST_HEAD(&new->list); -		new->blkaddr = START_BLOCK(sbi, cpc->trim_start); -		new->len = sbi->blocks_per_seg; -		list_add_tail(&new->list, head); -		SM_I(sbi)->nr_discards += sbi->blocks_per_seg; -		cpc->trimmed += sbi->blocks_per_seg; +		__add_discard_entry(sbi, cpc, 0, sbi->blocks_per_seg);  		return;  	} @@ -489,7 +548,7 @@ static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc)  	/* SIT_VBLOCK_MAP_SIZE should be multiple of sizeof(unsigned long) */  	for (i = 0; i < entries; i++) -		dmap[i] = (cur_map[i] ^ ckpt_map[i]) & ckpt_map[i]; +		dmap[i] = ~(cur_map[i] | ckpt_map[i]);  	while (force || SM_I(sbi)->nr_discards <= SM_I(sbi)->max_discards) {  		start = __find_rev_next_bit(dmap, max_blocks, end + 1); @@ -501,14 +560,7 @@ static void add_discard_addrs(struct f2fs_sb_info *sbi, struct cp_control *cpc)  		if (end - start < cpc->trim_minlen)  			continue; -		new = f2fs_kmem_cache_alloc(discard_entry_slab, GFP_NOFS); -		INIT_LIST_HEAD(&new->list); -		new->blkaddr = START_BLOCK(sbi, cpc->trim_start) + start; -		new->len = end - start; -		cpc->trimmed += end - start; - -		list_add_tail(&new->list, head); -		SM_I(sbi)->nr_discards += end - start; +		__add_discard_entry(sbi, cpc, start, end);  	}  } @@ -620,10 +672,10 @@ static void update_sit_entry(struct f2fs_sb_info *sbi, block_t blkaddr, int del)  	/* Update valid block bitmap */  	if (del > 0) { -		if (f2fs_set_bit(offset, se->cur_valid_map)) +		if (f2fs_test_and_set_bit(offset, se->cur_valid_map))  			f2fs_bug_on(sbi, 1);  	} else { -		if (!f2fs_clear_bit(offset, se->cur_valid_map)) +		if (!f2fs_test_and_clear_bit(offset, se->cur_valid_map))  			f2fs_bug_on(sbi, 1);  	}  	if (!f2fs_test_bit(offset, se->ckpt_valid_map)) @@ -1004,6 +1056,7 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)  						range->len < sbi->blocksize)  		return -EINVAL; +	cpc.trimmed = 0;  	if (end <= MAIN_BLKADDR(sbi))  		goto out; @@ -1015,10 +1068,11 @@ int f2fs_trim_fs(struct f2fs_sb_info *sbi, struct fstrim_range *range)  	cpc.trim_start = start_segno;  	cpc.trim_end = end_segno;  	cpc.trim_minlen = range->minlen >> sbi->log_blocksize; -	cpc.trimmed = 0;  	/* do checkpoint to issue discard commands safely */ +	mutex_lock(&sbi->gc_mutex);  	write_checkpoint(sbi, &cpc); +	mutex_unlock(&sbi->gc_mutex);  out:  	range->len = cpc.trimmed << sbi->log_blocksize;  	return 0; @@ -1050,8 +1104,8 @@ static int __get_segment_type_4(struct page *page, enum page_type p_type)  		else  			return CURSEG_COLD_DATA;  	} else { -		if (IS_DNODE(page) && !is_cold_node(page)) -			return CURSEG_HOT_NODE; +		if (IS_DNODE(page) && is_cold_node(page)) +			return CURSEG_WARM_NODE;  		else  			return CURSEG_COLD_NODE;  	} @@ -1524,17 +1578,7 @@ int lookup_journal_in_cursum(struct f2fs_summary_block *sum, int type,  static struct page *get_current_sit_page(struct f2fs_sb_info *sbi,  					unsigned int segno)  { -	struct sit_info *sit_i = SIT_I(sbi); -	unsigned int offset = SIT_BLOCK_OFFSET(segno); -	block_t blk_addr = sit_i->sit_base_addr + offset; - -	check_seg_range(sbi, segno); - -	/* calculate sit block address */ -	if (f2fs_test_bit(offset, sit_i->sit_bitmap)) -		blk_addr += sit_i->sit_blocks; - -	return get_meta_page(sbi, blk_addr); +	return get_meta_page(sbi, current_sit_addr(sbi, segno));  }  static struct page *get_next_sit_page(struct f2fs_sb_info *sbi, @@ -1687,7 +1731,7 @@ void flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc)  	 * #2, flush sit entries to sit page.  	 */  	list_for_each_entry_safe(ses, tmp, head, set_list) { -		struct page *page; +		struct page *page = NULL;  		struct f2fs_sit_block *raw_sit = NULL;  		unsigned int start_segno = ses->start_segno;  		unsigned int end = min(start_segno + SIT_ENTRY_PER_BLOCK, @@ -2200,7 +2244,7 @@ int __init create_segment_manager_caches(void)  		goto fail;  	sit_entry_set_slab = f2fs_kmem_cache_create("sit_entry_set", -			sizeof(struct nat_entry_set)); +			sizeof(struct sit_entry_set));  	if (!sit_entry_set_slab)  		goto destory_discard_entry;  |