diff options
Diffstat (limited to 'fs/f2fs/node.c')
| -rw-r--r-- | fs/f2fs/node.c | 86 | 
1 files changed, 64 insertions, 22 deletions
diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 7dd63b794bfb..27d1a74dd6f3 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -159,7 +159,7 @@ static void __set_nat_cache_dirty(struct f2fs_nm_info *nm_i,  	head = radix_tree_lookup(&nm_i->nat_set_root, set);  	if (!head) { -		head = f2fs_kmem_cache_alloc(nat_entry_set_slab, GFP_ATOMIC); +		head = f2fs_kmem_cache_alloc(nat_entry_set_slab, GFP_NOFS);  		INIT_LIST_HEAD(&head->entry_list);  		INIT_LIST_HEAD(&head->set_list); @@ -246,7 +246,7 @@ static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid)  {  	struct nat_entry *new; -	new = f2fs_kmem_cache_alloc(nat_entry_slab, GFP_ATOMIC); +	new = f2fs_kmem_cache_alloc(nat_entry_slab, GFP_NOFS);  	f2fs_radix_tree_insert(&nm_i->nat_root, nid, new);  	memset(new, 0, sizeof(struct nat_entry));  	nat_set_nid(new, nid); @@ -306,6 +306,10 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,  	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)); + +		/* in order to reuse the nid */ +		if (nm_i->next_scan_nid > ni->nid) +			nm_i->next_scan_nid = ni->nid;  	}  	/* change address */ @@ -328,11 +332,11 @@ static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,  int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink)  {  	struct f2fs_nm_info *nm_i = NM_I(sbi); +	int nr = nr_shrink; -	if (available_free_memory(sbi, NAT_ENTRIES)) +	if (!down_write_trylock(&nm_i->nat_tree_lock))  		return 0; -	down_write(&nm_i->nat_tree_lock);  	while (nr_shrink && !list_empty(&nm_i->nat_entries)) {  		struct nat_entry *ne;  		ne = list_first_entry(&nm_i->nat_entries, @@ -341,7 +345,7 @@ int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink)  		nr_shrink--;  	}  	up_write(&nm_i->nat_tree_lock); -	return nr_shrink; +	return nr - nr_shrink;  }  /* @@ -898,17 +902,20 @@ int truncate_xattr_node(struct inode *inode, struct page *page)   * Caller should grab and release a rwsem by calling f2fs_lock_op() and   * f2fs_unlock_op().   */ -void remove_inode_page(struct inode *inode) +int remove_inode_page(struct inode *inode)  {  	struct dnode_of_data dn; +	int err;  	set_new_dnode(&dn, inode, NULL, NULL, inode->i_ino); -	if (get_dnode_of_data(&dn, 0, LOOKUP_NODE)) -		return; +	err = get_dnode_of_data(&dn, 0, LOOKUP_NODE); +	if (err) +		return err; -	if (truncate_xattr_node(inode, dn.inode_page)) { +	err = truncate_xattr_node(inode, dn.inode_page); +	if (err) {  		f2fs_put_dnode(&dn); -		return; +		return err;  	}  	/* remove potential inline_data blocks */ @@ -922,6 +929,7 @@ void remove_inode_page(struct inode *inode)  	/* will put inode & node pages */  	truncate_node(&dn); +	return 0;  }  struct page *new_inode_page(struct inode *inode) @@ -991,8 +999,7 @@ fail:  /*   * Caller should do after getting the following values.   * 0: f2fs_put_page(page, 0) - * LOCKED_PAGE: f2fs_put_page(page, 1) - * error: nothing + * LOCKED_PAGE or error: f2fs_put_page(page, 1)   */  static int read_node_page(struct page *page, int rw)  { @@ -1010,7 +1017,6 @@ static int read_node_page(struct page *page, int rw)  	if (unlikely(ni.blk_addr == NULL_ADDR)) {  		ClearPageUptodate(page); -		f2fs_put_page(page, 1);  		return -ENOENT;  	} @@ -1041,10 +1047,7 @@ void ra_node_page(struct f2fs_sb_info *sbi, nid_t nid)  		return;  	err = read_node_page(apage, READA); -	if (err == 0) -		f2fs_put_page(apage, 0); -	else if (err == LOCKED_PAGE) -		f2fs_put_page(apage, 1); +	f2fs_put_page(apage, err ? 1 : 0);  }  struct page *get_node_page(struct f2fs_sb_info *sbi, pgoff_t nid) @@ -1057,10 +1060,12 @@ repeat:  		return ERR_PTR(-ENOMEM);  	err = read_node_page(page, READ_SYNC); -	if (err < 0) +	if (err < 0) { +		f2fs_put_page(page, 1);  		return ERR_PTR(err); -	else if (err != LOCKED_PAGE) +	} else if (err != LOCKED_PAGE) {  		lock_page(page); +	}  	if (unlikely(!PageUptodate(page) || nid != nid_of_node(page))) {  		ClearPageUptodate(page); @@ -1096,10 +1101,12 @@ repeat:  		return ERR_PTR(-ENOMEM);  	err = read_node_page(page, READ_SYNC); -	if (err < 0) +	if (err < 0) { +		f2fs_put_page(page, 1);  		return ERR_PTR(err); -	else if (err == LOCKED_PAGE) +	} else if (err == LOCKED_PAGE) {  		goto page_hit; +	}  	blk_start_plug(&plug); @@ -1533,7 +1540,7 @@ static void build_free_nids(struct f2fs_sb_info *sbi)  		if (unlikely(nid >= nm_i->max_nid))  			nid = 0; -		if (i++ == FREE_NID_PAGES) +		if (++i >= FREE_NID_PAGES)  			break;  	} @@ -1570,6 +1577,8 @@ retry:  	/* We should not use stale free nids created by build_free_nids */  	if (nm_i->fcnt && !on_build_free_nids(nm_i)) { +		struct node_info ni; +  		f2fs_bug_on(sbi, list_empty(&nm_i->free_nid_list));  		list_for_each_entry(i, &nm_i->free_nid_list, list)  			if (i->state == NID_NEW) @@ -1580,6 +1589,13 @@ retry:  		i->state = NID_ALLOC;  		nm_i->fcnt--;  		spin_unlock(&nm_i->free_nid_list_lock); + +		/* check nid is allocated already */ +		get_node_info(sbi, *nid, &ni); +		if (ni.blk_addr != NULL_ADDR) { +			alloc_nid_done(sbi, *nid); +			goto retry; +		}  		return true;  	}  	spin_unlock(&nm_i->free_nid_list_lock); @@ -1636,6 +1652,32 @@ void alloc_nid_failed(struct f2fs_sb_info *sbi, nid_t nid)  		kmem_cache_free(free_nid_slab, i);  } +int try_to_free_nids(struct f2fs_sb_info *sbi, int nr_shrink) +{ +	struct f2fs_nm_info *nm_i = NM_I(sbi); +	struct free_nid *i, *next; +	int nr = nr_shrink; + +	if (!mutex_trylock(&nm_i->build_lock)) +		return 0; + +	spin_lock(&nm_i->free_nid_list_lock); +	list_for_each_entry_safe(i, next, &nm_i->free_nid_list, list) { +		if (nr_shrink <= 0 || nm_i->fcnt <= NAT_ENTRY_PER_BLOCK) +			break; +		if (i->state == NID_ALLOC) +			continue; +		__del_from_free_nid_list(nm_i, i); +		kmem_cache_free(free_nid_slab, i); +		nm_i->fcnt--; +		nr_shrink--; +	} +	spin_unlock(&nm_i->free_nid_list_lock); +	mutex_unlock(&nm_i->build_lock); + +	return nr - nr_shrink; +} +  void recover_inline_xattr(struct inode *inode, struct page *page)  {  	void *src_addr, *dst_addr;  |