diff options
Diffstat (limited to 'fs/f2fs/node.c')
| -rw-r--r-- | fs/f2fs/node.c | 353 | 
1 files changed, 220 insertions, 133 deletions
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 4b697ccc9b0c..45378196e19a 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -25,6 +25,7 @@  static struct kmem_cache *nat_entry_slab;  static struct kmem_cache *free_nid_slab; +static struct kmem_cache *nat_entry_set_slab;  bool available_free_memory(struct f2fs_sb_info *sbi, int type)  { @@ -90,12 +91,8 @@ static struct page *get_next_nat_page(struct f2fs_sb_info *sbi, nid_t nid)  	/* get current nat block page with lock */  	src_page = get_meta_page(sbi, src_off); - -	/* Dirty src_page means that it is already the new target NAT page. */ -	if (PageDirty(src_page)) -		return src_page; -  	dst_page = grab_meta_page(sbi, dst_off); +	f2fs_bug_on(PageDirty(src_page));  	src_addr = page_address(src_page);  	dst_addr = page_address(dst_page); @@ -240,7 +237,7 @@ retry:  			nat_get_blkaddr(e) != NULL_ADDR &&  			new_blkaddr == NEW_ADDR); -	/* increament version no as node is removed */ +	/* increment version no as node is removed */  	if (nat_get_blkaddr(e) != NEW_ADDR && new_blkaddr == NULL_ADDR) {  		unsigned char version = nat_get_version(e);  		nat_set_version(e, inc_node_version(version)); @@ -277,7 +274,7 @@ int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink)  }  /* - * This function returns always success + * This function always returns success   */  void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni)  { @@ -653,7 +650,7 @@ static int truncate_partial_nodes(struct dnode_of_data *dn,  	/* get indirect nodes in the path */  	for (i = 0; i < idx + 1; i++) { -		/* refernece count'll be increased */ +		/* reference count'll be increased */  		pages[i] = get_node_page(sbi, nid[i]);  		if (IS_ERR(pages[i])) {  			err = PTR_ERR(pages[i]); @@ -826,26 +823,30 @@ int truncate_xattr_node(struct inode *inode, struct page *page)   */  void remove_inode_page(struct inode *inode)  { -	struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb); -	struct page *page; -	nid_t ino = inode->i_ino;  	struct dnode_of_data dn; -	page = get_node_page(sbi, ino); -	if (IS_ERR(page)) +	set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino); +	if (get_dnode_of_data(&dn, 0, LOOKUP_NODE))  		return; -	if (truncate_xattr_node(inode, page)) { -		f2fs_put_page(page, 1); +	if (truncate_xattr_node(inode, dn.inode_page)) { +		f2fs_put_dnode(&dn);  		return;  	} -	/* 0 is possible, after f2fs_new_inode() is failed */ + +	/* remove potential inline_data blocks */ +	if (S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || +				S_ISLNK(inode->i_mode)) +		truncate_data_blocks_range(&dn, 1); + +	/* 0 is possible, after f2fs_new_inode() has failed */  	f2fs_bug_on(inode->i_blocks != 0 && inode->i_blocks != 1); -	set_new_dnode(&dn, inode, page, page, ino); + +	/* will put inode & node pages */  	truncate_node(&dn);  } -struct page *new_inode_page(struct inode *inode, const struct qstr *name) +struct page *new_inode_page(struct inode *inode)  {  	struct dnode_of_data dn; @@ -1132,8 +1133,11 @@ continue_unlock:  				set_fsync_mark(page, 0);  				set_dentry_mark(page, 0);  			} -			NODE_MAPPING(sbi)->a_ops->writepage(page, wbc); -			wrote++; + +			if (NODE_MAPPING(sbi)->a_ops->writepage(page, wbc)) +				unlock_page(page); +			else +				wrote++;  			if (--wbc->nr_to_write == 0)  				break; @@ -1215,6 +1219,8 @@ static int f2fs_write_node_page(struct page *page,  	if (unlikely(sbi->por_doing))  		goto redirty_out; +	if (unlikely(f2fs_cp_error(sbi))) +		goto redirty_out;  	f2fs_wait_on_page_writeback(page, NODE); @@ -1234,12 +1240,12 @@ static int f2fs_write_node_page(struct page *page,  	if (wbc->for_reclaim)  		goto redirty_out; -	mutex_lock(&sbi->node_write); +	down_read(&sbi->node_write);  	set_page_writeback(page);  	write_node_page(sbi, page, &fio, nid, ni.blk_addr, &new_addr);  	set_node_addr(sbi, &ni, new_addr, is_fsync_dnode(page));  	dec_page_count(sbi, F2FS_DIRTY_NODES); -	mutex_unlock(&sbi->node_write); +	up_read(&sbi->node_write);  	unlock_page(page);  	return 0; @@ -1543,16 +1549,7 @@ void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid)  		kmem_cache_free(free_nid_slab, i);  } -void recover_node_page(struct f2fs_sb_info *sbi, struct page *page, -		struct f2fs_summary *sum, struct node_info *ni, -		block_t new_blkaddr) -{ -	rewrite_node_page(sbi, page, sum, ni->blk_addr, new_blkaddr); -	set_node_addr(sbi, ni, new_blkaddr, false); -	clear_node_page_dirty(page); -} - -static void recover_inline_xattr(struct inode *inode, struct page *page) +void recover_inline_xattr(struct inode *inode, struct page *page)  {  	struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);  	void *src_addr, *dst_addr; @@ -1560,42 +1557,33 @@ static void recover_inline_xattr(struct inode *inode, struct page *page)  	struct page *ipage;  	struct f2fs_inode *ri; -	if (!f2fs_has_inline_xattr(inode)) -		return; - -	if (!IS_INODE(page)) -		return; - -	ri = F2FS_INODE(page); -	if (!(ri->i_inline & F2FS_INLINE_XATTR)) -		return; -  	ipage = get_node_page(sbi, inode->i_ino);  	f2fs_bug_on(IS_ERR(ipage)); +	ri = F2FS_INODE(page); +	if (!(ri->i_inline & F2FS_INLINE_XATTR)) { +		clear_inode_flag(F2FS_I(inode), FI_INLINE_XATTR); +		goto update_inode; +	} +  	dst_addr = inline_xattr_addr(ipage);  	src_addr = inline_xattr_addr(page);  	inline_size = inline_xattr_size(inode);  	f2fs_wait_on_page_writeback(ipage, NODE);  	memcpy(dst_addr, src_addr, inline_size); - +update_inode:  	update_inode(inode, ipage);  	f2fs_put_page(ipage, 1);  } -bool recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr) +void recover_xattr_data(struct inode *inode, struct page *page, block_t blkaddr)  {  	struct f2fs_sb_info *sbi = F2FS_SB(inode->i_sb);  	nid_t prev_xnid = F2FS_I(inode)->i_xattr_nid;  	nid_t new_xnid = nid_of_node(page);  	struct node_info ni; -	recover_inline_xattr(inode, page); - -	if (!f2fs_has_xattr_block(ofs_of_node(page))) -		return false; -  	/* 1: invalidate the previous xattr nid */  	if (!prev_xnid)  		goto recover_xnid; @@ -1623,7 +1611,6 @@ recover_xnid:  	set_node_addr(sbi, &ni, blkaddr, false);  	update_inode_page(inode); -	return true;  }  int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page) @@ -1642,7 +1629,7 @@ int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page)  	if (!ipage)  		return -ENOMEM; -	/* Should not use this inode  from free nid list */ +	/* Should not use this inode from free nid list */  	remove_free_nid(NM_I(sbi), ino);  	SetPageUptodate(ipage); @@ -1656,6 +1643,7 @@ int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page)  	dst->i_blocks = cpu_to_le64(1);  	dst->i_links = cpu_to_le32(1);  	dst->i_xattr_nid = 0; +	dst->i_inline = src->i_inline & F2FS_INLINE_XATTR;  	new_ni = old_ni;  	new_ni.ino = ino; @@ -1664,13 +1652,14 @@ int recover_inode_page(struct f2fs_sb_info *sbi, struct page *page)  		WARN_ON(1);  	set_node_addr(sbi, &new_ni, NEW_ADDR, false);  	inc_valid_inode_count(sbi); +	set_page_dirty(ipage);  	f2fs_put_page(ipage, 1);  	return 0;  }  /*   * ra_sum_pages() merge contiguous pages into one bio and submit. - * these pre-readed pages are alloced in bd_inode's mapping tree. + * these pre-read pages are allocated in bd_inode's mapping tree.   */  static int ra_sum_pages(struct f2fs_sb_info *sbi, struct page **pages,  				int start, int nrpages) @@ -1714,7 +1703,7 @@ int restore_node_summary(struct f2fs_sb_info *sbi,  	for (i = 0; !err && i < last_offset; i += nrpages, addr += nrpages) {  		nrpages = min(last_offset - i, bio_blocks); -		/* read ahead node pages */ +		/* readahead node pages */  		nrpages = ra_sum_pages(sbi, pages, addr, nrpages);  		if (!nrpages)  			return -ENOMEM; @@ -1744,7 +1733,90 @@ skip:  	return err;  } -static bool flush_nats_in_journal(struct f2fs_sb_info *sbi) +static struct nat_entry_set *grab_nat_entry_set(void) +{ +	struct nat_entry_set *nes = +			f2fs_kmem_cache_alloc(nat_entry_set_slab, GFP_ATOMIC); + +	nes->entry_cnt = 0; +	INIT_LIST_HEAD(&nes->set_list); +	INIT_LIST_HEAD(&nes->entry_list); +	return nes; +} + +static void release_nat_entry_set(struct nat_entry_set *nes, +						struct f2fs_nm_info *nm_i) +{ +	f2fs_bug_on(!list_empty(&nes->entry_list)); + +	nm_i->dirty_nat_cnt -= nes->entry_cnt; +	list_del(&nes->set_list); +	kmem_cache_free(nat_entry_set_slab, nes); +} + +static void adjust_nat_entry_set(struct nat_entry_set *nes, +						struct list_head *head) +{ +	struct nat_entry_set *next = nes; + +	if (list_is_last(&nes->set_list, head)) +		return; + +	list_for_each_entry_continue(next, head, set_list) +		if (nes->entry_cnt <= next->entry_cnt) +			break; + +	list_move_tail(&nes->set_list, &next->set_list); +} + +static void add_nat_entry(struct nat_entry *ne, struct list_head *head) +{ +	struct nat_entry_set *nes; +	nid_t start_nid = START_NID(ne->ni.nid); + +	list_for_each_entry(nes, head, set_list) { +		if (nes->start_nid == start_nid) { +			list_move_tail(&ne->list, &nes->entry_list); +			nes->entry_cnt++; +			adjust_nat_entry_set(nes, head); +			return; +		} +	} + +	nes = grab_nat_entry_set(); + +	nes->start_nid = start_nid; +	list_move_tail(&ne->list, &nes->entry_list); +	nes->entry_cnt++; +	list_add(&nes->set_list, head); +} + +static void merge_nats_in_set(struct f2fs_sb_info *sbi) +{ +	struct f2fs_nm_info *nm_i = NM_I(sbi); +	struct list_head *dirty_list = &nm_i->dirty_nat_entries; +	struct list_head *set_list = &nm_i->nat_entry_set; +	struct nat_entry *ne, *tmp; + +	write_lock(&nm_i->nat_tree_lock); +	list_for_each_entry_safe(ne, tmp, dirty_list, list) { +		if (nat_get_blkaddr(ne) == NEW_ADDR) +			continue; +		add_nat_entry(ne, set_list); +		nm_i->dirty_nat_cnt++; +	} +	write_unlock(&nm_i->nat_tree_lock); +} + +static bool __has_cursum_space(struct f2fs_summary_block *sum, int size) +{ +	if (nats_in_cursum(sum) + size <= NAT_JOURNAL_ENTRIES) +		return true; +	else +		return false; +} + +static void remove_nats_in_journal(struct f2fs_sb_info *sbi)  {  	struct f2fs_nm_info *nm_i = NM_I(sbi);  	struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA); @@ -1752,12 +1824,6 @@ static bool flush_nats_in_journal(struct f2fs_sb_info *sbi)  	int i;  	mutex_lock(&curseg->curseg_mutex); - -	if (nats_in_cursum(sum) < NAT_JOURNAL_ENTRIES) { -		mutex_unlock(&curseg->curseg_mutex); -		return false; -	} -  	for (i = 0; i < nats_in_cursum(sum); i++) {  		struct nat_entry *ne;  		struct f2fs_nat_entry raw_ne; @@ -1767,23 +1833,21 @@ static bool flush_nats_in_journal(struct f2fs_sb_info *sbi)  retry:  		write_lock(&nm_i->nat_tree_lock);  		ne = __lookup_nat_cache(nm_i, nid); -		if (ne) { -			__set_nat_cache_dirty(nm_i, ne); -			write_unlock(&nm_i->nat_tree_lock); -			continue; -		} +		if (ne) +			goto found; +  		ne = grab_nat_entry(nm_i, nid);  		if (!ne) {  			write_unlock(&nm_i->nat_tree_lock);  			goto retry;  		}  		node_info_from_raw_nat(&ne->ni, &raw_ne); +found:  		__set_nat_cache_dirty(nm_i, ne);  		write_unlock(&nm_i->nat_tree_lock);  	}  	update_nats_in_cursum(sum, -i);  	mutex_unlock(&curseg->curseg_mutex); -	return true;  }  /* @@ -1794,80 +1858,91 @@ void flush_nat_entries(struct f2fs_sb_info *sbi)  	struct f2fs_nm_info *nm_i = NM_I(sbi);  	struct curseg_info *curseg = CURSEG_I(sbi, CURSEG_HOT_DATA);  	struct f2fs_summary_block *sum = curseg->sum_blk; -	struct nat_entry *ne, *cur; -	struct page *page = NULL; -	struct f2fs_nat_block *nat_blk = NULL; -	nid_t start_nid = 0, end_nid = 0; -	bool flushed; +	struct nat_entry_set *nes, *tmp; +	struct list_head *head = &nm_i->nat_entry_set; +	bool to_journal = true; -	flushed = flush_nats_in_journal(sbi); +	/* merge nat entries of dirty list to nat entry set temporarily */ +	merge_nats_in_set(sbi); -	if (!flushed) -		mutex_lock(&curseg->curseg_mutex); +	/* +	 * if there are no enough space in journal to store dirty nat +	 * entries, remove all entries from journal and merge them +	 * into nat entry set. +	 */ +	if (!__has_cursum_space(sum, nm_i->dirty_nat_cnt)) { +		remove_nats_in_journal(sbi); -	/* 1) flush dirty nat caches */ -	list_for_each_entry_safe(ne, cur, &nm_i->dirty_nat_entries, list) { -		nid_t nid; -		struct f2fs_nat_entry raw_ne; -		int offset = -1; - -		if (nat_get_blkaddr(ne) == NEW_ADDR) -			continue; - -		nid = nat_get_nid(ne); - -		if (flushed) -			goto to_nat_page; +		/* +		 * merge nat entries of dirty list to nat entry set temporarily +		 */ +		merge_nats_in_set(sbi); +	} -		/* if there is room for nat enries in curseg->sumpage */ -		offset = lookup_journal_in_cursum(sum, NAT_JOURNAL, nid, 1); -		if (offset >= 0) { -			raw_ne = nat_in_journal(sum, offset); -			goto flush_now; -		} -to_nat_page: -		if (!page || (start_nid > nid || nid > end_nid)) { -			if (page) { -				f2fs_put_page(page, 1); -				page = NULL; -			} -			start_nid = START_NID(nid); -			end_nid = start_nid + NAT_ENTRY_PER_BLOCK - 1; +	if (!nm_i->dirty_nat_cnt) +		return; -			/* -			 * get nat block with dirty flag, increased reference -			 * count, mapped and lock -			 */ +	/* +	 * there are two steps to flush nat entries: +	 * #1, flush nat entries to journal in current hot data summary block. +	 * #2, flush nat entries to nat page. +	 */ +	list_for_each_entry_safe(nes, tmp, head, set_list) { +		struct f2fs_nat_block *nat_blk; +		struct nat_entry *ne, *cur; +		struct page *page; +		nid_t start_nid = nes->start_nid; + +		if (to_journal && !__has_cursum_space(sum, nes->entry_cnt)) +			to_journal = false; + +		if (to_journal) { +			mutex_lock(&curseg->curseg_mutex); +		} else {  			page = get_next_nat_page(sbi, start_nid);  			nat_blk = page_address(page); +			f2fs_bug_on(!nat_blk);  		} -		f2fs_bug_on(!nat_blk); -		raw_ne = nat_blk->entries[nid - start_nid]; -flush_now: -		raw_nat_from_node_info(&raw_ne, &ne->ni); - -		if (offset < 0) { -			nat_blk->entries[nid - start_nid] = raw_ne; -		} else { -			nat_in_journal(sum, offset) = raw_ne; -			nid_in_journal(sum, offset) = cpu_to_le32(nid); -		} +		/* flush dirty nats in nat entry set */ +		list_for_each_entry_safe(ne, cur, &nes->entry_list, list) { +			struct f2fs_nat_entry *raw_ne; +			nid_t nid = nat_get_nid(ne); +			int offset; + +			if (to_journal) { +				offset = lookup_journal_in_cursum(sum, +							NAT_JOURNAL, nid, 1); +				f2fs_bug_on(offset < 0); +				raw_ne = &nat_in_journal(sum, offset); +				nid_in_journal(sum, offset) = cpu_to_le32(nid); +			} else { +				raw_ne = &nat_blk->entries[nid - start_nid]; +			} +			raw_nat_from_node_info(raw_ne, &ne->ni); -		if (nat_get_blkaddr(ne) == NULL_ADDR && +			if (nat_get_blkaddr(ne) == NULL_ADDR &&  				add_free_nid(sbi, nid, false) <= 0) { -			write_lock(&nm_i->nat_tree_lock); -			__del_from_nat_cache(nm_i, ne); -			write_unlock(&nm_i->nat_tree_lock); -		} else { -			write_lock(&nm_i->nat_tree_lock); -			__clear_nat_cache_dirty(nm_i, ne); -			write_unlock(&nm_i->nat_tree_lock); +				write_lock(&nm_i->nat_tree_lock); +				__del_from_nat_cache(nm_i, ne); +				write_unlock(&nm_i->nat_tree_lock); +			} else { +				write_lock(&nm_i->nat_tree_lock); +				__clear_nat_cache_dirty(nm_i, ne); +				write_unlock(&nm_i->nat_tree_lock); +			}  		} + +		if (to_journal) +			mutex_unlock(&curseg->curseg_mutex); +		else +			f2fs_put_page(page, 1); + +		release_nat_entry_set(nes, nm_i);  	} -	if (!flushed) -		mutex_unlock(&curseg->curseg_mutex); -	f2fs_put_page(page, 1); + +	f2fs_bug_on(!list_empty(head)); +	f2fs_bug_on(nm_i->dirty_nat_cnt);  }  static int init_node_manager(struct f2fs_sb_info *sbi) @@ -1886,7 +1961,7 @@ static int init_node_manager(struct f2fs_sb_info *sbi)  	nm_i->max_nid = NAT_ENTRY_PER_BLOCK * nat_blocks;  	/* not used nids: 0, node, meta, (and root counted as valid node) */ -	nm_i->available_nids = nm_i->max_nid - 3; +	nm_i->available_nids = nm_i->max_nid - F2FS_RESERVED_NODE_NUM;  	nm_i->fcnt = 0;  	nm_i->nat_cnt = 0;  	nm_i->ram_thresh = DEF_RAM_THRESHOLD; @@ -1896,6 +1971,7 @@ static int init_node_manager(struct f2fs_sb_info *sbi)  	INIT_RADIX_TREE(&nm_i->nat_root, GFP_ATOMIC);  	INIT_LIST_HEAD(&nm_i->nat_entries);  	INIT_LIST_HEAD(&nm_i->dirty_nat_entries); +	INIT_LIST_HEAD(&nm_i->nat_entry_set);  	mutex_init(&nm_i->build_lock);  	spin_lock_init(&nm_i->free_nid_list_lock); @@ -1976,19 +2052,30 @@ int __init create_node_manager_caches(void)  	nat_entry_slab = f2fs_kmem_cache_create("nat_entry",  			sizeof(struct nat_entry));  	if (!nat_entry_slab) -		return -ENOMEM; +		goto fail;  	free_nid_slab = f2fs_kmem_cache_create("free_nid",  			sizeof(struct free_nid)); -	if (!free_nid_slab) { -		kmem_cache_destroy(nat_entry_slab); -		return -ENOMEM; -	} +	if (!free_nid_slab) +		goto destory_nat_entry; + +	nat_entry_set_slab = f2fs_kmem_cache_create("nat_entry_set", +			sizeof(struct nat_entry_set)); +	if (!nat_entry_set_slab) +		goto destory_free_nid;  	return 0; + +destory_free_nid: +	kmem_cache_destroy(free_nid_slab); +destory_nat_entry: +	kmem_cache_destroy(nat_entry_slab); +fail: +	return -ENOMEM;  }  void destroy_node_manager_caches(void)  { +	kmem_cache_destroy(nat_entry_set_slab);  	kmem_cache_destroy(free_nid_slab);  	kmem_cache_destroy(nat_entry_slab);  }  |