2090 lines · 50.1 KB · C
RawDownload
/* * fs/f2fs/node.c * * Copyright (c) 2012 Samsung Electronics Co., Ltd. *             http://www.samsung.com/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */#include <linux/fs.h>#include <linux/f2fs_fs.h>#include <linux/mpage.h>#include <linux/backing-dev.h>#include <linux/blkdev.h>#include <linux/pagevec.h>#include <linux/swap.h> #include "f2fs.h"#include "node.h"#include "segment.h"#include "trace.h"#include <trace/events/f2fs.h> #define on_build_free_nids(nmi) mutex_is_locked(&nm_i->build_lock) 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){	struct f2fs_nm_info *nm_i = NM_I(sbi);	struct sysinfo val;	unsigned long avail_ram;	unsigned long mem_size = 0;	bool res = false; 	si_meminfo(&val); 	/* only uses low memory */	avail_ram = val.totalram - val.totalhigh; 	/*	 * give 25%, 25%, 50%, 50%, 50% memory for each components respectively	 */	if (type == FREE_NIDS) {		mem_size = (nm_i->fcnt * sizeof(struct free_nid)) >>							PAGE_CACHE_SHIFT;		res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2);	} else if (type == NAT_ENTRIES) {		mem_size = (nm_i->nat_cnt * sizeof(struct nat_entry)) >>							PAGE_CACHE_SHIFT;		res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 2);	} else if (type == DIRTY_DENTS) {		if (sbi->sb->s_bdi->wb.dirty_exceeded)			return false;		mem_size = get_pages(sbi, F2FS_DIRTY_DENTS);		res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1);	} else if (type == INO_ENTRIES) {		int i; 		for (i = 0; i <= UPDATE_INO; i++)			mem_size += (sbi->im[i].ino_num *				sizeof(struct ino_entry)) >> PAGE_CACHE_SHIFT;		res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1);	} else if (type == EXTENT_CACHE) {		mem_size = (sbi->total_ext_tree * sizeof(struct extent_tree) +				atomic_read(&sbi->total_ext_node) *				sizeof(struct extent_node)) >> PAGE_CACHE_SHIFT;		res = mem_size < ((avail_ram * nm_i->ram_thresh / 100) >> 1);	} else {		if (sbi->sb->s_bdi->wb.dirty_exceeded)			return false;	}	return res;} static void clear_node_page_dirty(struct page *page){	struct address_space *mapping = page->mapping;	unsigned int long flags; 	if (PageDirty(page)) {		spin_lock_irqsave(&mapping->tree_lock, flags);		radix_tree_tag_clear(&mapping->page_tree,				page_index(page),				PAGECACHE_TAG_DIRTY);		spin_unlock_irqrestore(&mapping->tree_lock, flags); 		clear_page_dirty_for_io(page);		dec_page_count(F2FS_M_SB(mapping), F2FS_DIRTY_NODES);	}	ClearPageUptodate(page);} static struct page *get_current_nat_page(struct f2fs_sb_info *sbi, nid_t nid){	pgoff_t index = current_nat_addr(sbi, nid);	return get_meta_page(sbi, index);} static struct page *get_next_nat_page(struct f2fs_sb_info *sbi, nid_t nid){	struct page *src_page;	struct page *dst_page;	pgoff_t src_off;	pgoff_t dst_off;	void *src_addr;	void *dst_addr;	struct f2fs_nm_info *nm_i = NM_I(sbi); 	src_off = current_nat_addr(sbi, nid);	dst_off = next_nat_addr(sbi, src_off); 	/* get current nat block page with lock */	src_page = get_meta_page(sbi, src_off);	dst_page = grab_meta_page(sbi, dst_off);	f2fs_bug_on(sbi, PageDirty(src_page)); 	src_addr = page_address(src_page);	dst_addr = page_address(dst_page);	memcpy(dst_addr, src_addr, PAGE_CACHE_SIZE);	set_page_dirty(dst_page);	f2fs_put_page(src_page, 1); 	set_to_next_nat(nm_i, nid); 	return dst_page;} static struct nat_entry *__lookup_nat_cache(struct f2fs_nm_info *nm_i, nid_t n){	return radix_tree_lookup(&nm_i->nat_root, n);} static unsigned int __gang_lookup_nat_cache(struct f2fs_nm_info *nm_i,		nid_t start, unsigned int nr, struct nat_entry **ep){	return radix_tree_gang_lookup(&nm_i->nat_root, (void **)ep, start, nr);} static void __del_from_nat_cache(struct f2fs_nm_info *nm_i, struct nat_entry *e){	list_del(&e->list);	radix_tree_delete(&nm_i->nat_root, nat_get_nid(e));	nm_i->nat_cnt--;	kmem_cache_free(nat_entry_slab, e);} static void __set_nat_cache_dirty(struct f2fs_nm_info *nm_i,						struct nat_entry *ne){	nid_t set = NAT_BLOCK_OFFSET(ne->ni.nid);	struct nat_entry_set *head; 	if (get_nat_flag(ne, IS_DIRTY))		return; 	head = radix_tree_lookup(&nm_i->nat_set_root, set);	if (!head) {		head = f2fs_kmem_cache_alloc(nat_entry_set_slab, GFP_ATOMIC); 		INIT_LIST_HEAD(&head->entry_list);		INIT_LIST_HEAD(&head->set_list);		head->set = set;		head->entry_cnt = 0;		f2fs_radix_tree_insert(&nm_i->nat_set_root, set, head);	}	list_move_tail(&ne->list, &head->entry_list);	nm_i->dirty_nat_cnt++;	head->entry_cnt++;	set_nat_flag(ne, IS_DIRTY, true);} static void __clear_nat_cache_dirty(struct f2fs_nm_info *nm_i,						struct nat_entry *ne){	nid_t set = NAT_BLOCK_OFFSET(ne->ni.nid);	struct nat_entry_set *head; 	head = radix_tree_lookup(&nm_i->nat_set_root, set);	if (head) {		list_move_tail(&ne->list, &nm_i->nat_entries);		set_nat_flag(ne, IS_DIRTY, false);		head->entry_cnt--;		nm_i->dirty_nat_cnt--;	}} static unsigned int __gang_lookup_nat_set(struct f2fs_nm_info *nm_i,		nid_t start, unsigned int nr, struct nat_entry_set **ep){	return radix_tree_gang_lookup(&nm_i->nat_set_root, (void **)ep,							start, nr);} int need_dentry_mark(struct f2fs_sb_info *sbi, nid_t nid){	struct f2fs_nm_info *nm_i = NM_I(sbi);	struct nat_entry *e;	bool need = false; 	down_read(&nm_i->nat_tree_lock);	e = __lookup_nat_cache(nm_i, nid);	if (e) {		if (!get_nat_flag(e, IS_CHECKPOINTED) &&				!get_nat_flag(e, HAS_FSYNCED_INODE))			need = true;	}	up_read(&nm_i->nat_tree_lock);	return need;} bool is_checkpointed_node(struct f2fs_sb_info *sbi, nid_t nid){	struct f2fs_nm_info *nm_i = NM_I(sbi);	struct nat_entry *e;	bool is_cp = true; 	down_read(&nm_i->nat_tree_lock);	e = __lookup_nat_cache(nm_i, nid);	if (e && !get_nat_flag(e, IS_CHECKPOINTED))		is_cp = false;	up_read(&nm_i->nat_tree_lock);	return is_cp;} bool need_inode_block_update(struct f2fs_sb_info *sbi, nid_t ino){	struct f2fs_nm_info *nm_i = NM_I(sbi);	struct nat_entry *e;	bool need_update = true; 	down_read(&nm_i->nat_tree_lock);	e = __lookup_nat_cache(nm_i, ino);	if (e && get_nat_flag(e, HAS_LAST_FSYNC) &&			(get_nat_flag(e, IS_CHECKPOINTED) ||			 get_nat_flag(e, HAS_FSYNCED_INODE)))		need_update = false;	up_read(&nm_i->nat_tree_lock);	return need_update;} 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);	f2fs_radix_tree_insert(&nm_i->nat_root, nid, new);	memset(new, 0, sizeof(struct nat_entry));	nat_set_nid(new, nid);	nat_reset_flag(new);	list_add_tail(&new->list, &nm_i->nat_entries);	nm_i->nat_cnt++;	return new;} static void cache_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid,						struct f2fs_nat_entry *ne){	struct nat_entry *e; 	down_write(&nm_i->nat_tree_lock);	e = __lookup_nat_cache(nm_i, nid);	if (!e) {		e = grab_nat_entry(nm_i, nid);		node_info_from_raw_nat(&e->ni, ne);	}	up_write(&nm_i->nat_tree_lock);} static void set_node_addr(struct f2fs_sb_info *sbi, struct node_info *ni,			block_t new_blkaddr, bool fsync_done){	struct f2fs_nm_info *nm_i = NM_I(sbi);	struct nat_entry *e; 	down_write(&nm_i->nat_tree_lock);	e = __lookup_nat_cache(nm_i, ni->nid);	if (!e) {		e = grab_nat_entry(nm_i, ni->nid);		copy_node_info(&e->ni, ni);		f2fs_bug_on(sbi, ni->blk_addr == NEW_ADDR);	} else if (new_blkaddr == NEW_ADDR) {		/*		 * when nid is reallocated,		 * previous nat entry can be remained in nat cache.		 * So, reinitialize it with new information.		 */		copy_node_info(&e->ni, ni);		f2fs_bug_on(sbi, ni->blk_addr != NULL_ADDR);	} 	/* sanity check */	f2fs_bug_on(sbi, nat_get_blkaddr(e) != ni->blk_addr);	f2fs_bug_on(sbi, nat_get_blkaddr(e) == NULL_ADDR &&			new_blkaddr == NULL_ADDR);	f2fs_bug_on(sbi, nat_get_blkaddr(e) == NEW_ADDR &&			new_blkaddr == NEW_ADDR);	f2fs_bug_on(sbi, nat_get_blkaddr(e) != NEW_ADDR &&			nat_get_blkaddr(e) != NULL_ADDR &&			new_blkaddr == NEW_ADDR); 	/* 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));	} 	/* change address */	nat_set_blkaddr(e, new_blkaddr);	if (new_blkaddr == NEW_ADDR || new_blkaddr == NULL_ADDR)		set_nat_flag(e, IS_CHECKPOINTED, false);	__set_nat_cache_dirty(nm_i, e); 	/* update fsync_mark if its inode nat entry is still alive */	if (ni->nid != ni->ino)		e = __lookup_nat_cache(nm_i, ni->ino);	if (e) {		if (fsync_done && ni->nid == ni->ino)			set_nat_flag(e, HAS_FSYNCED_INODE, true);		set_nat_flag(e, HAS_LAST_FSYNC, fsync_done);	}	up_write(&nm_i->nat_tree_lock);} int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink){	struct f2fs_nm_info *nm_i = NM_I(sbi); 	if (available_free_memory(sbi, NAT_ENTRIES))		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,					struct nat_entry, list);		__del_from_nat_cache(nm_i, ne);		nr_shrink--;	}	up_write(&nm_i->nat_tree_lock);	return nr_shrink;} /* * This function always returns success */void get_node_info(struct f2fs_sb_info *sbi, nid_t nid, struct node_info *ni){	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;	nid_t start_nid = START_NID(nid);	struct f2fs_nat_block *nat_blk;	struct page *page = NULL;	struct f2fs_nat_entry ne;	struct nat_entry *e;	int i; 	ni->nid = nid; 	/* Check nat cache */	down_read(&nm_i->nat_tree_lock);	e = __lookup_nat_cache(nm_i, nid);	if (e) {		ni->ino = nat_get_ino(e);		ni->blk_addr = nat_get_blkaddr(e);		ni->version = nat_get_version(e);	}	up_read(&nm_i->nat_tree_lock);	if (e)		return; 	memset(&ne, 0, sizeof(struct f2fs_nat_entry)); 	/* Check current segment summary */	mutex_lock(&curseg->curseg_mutex);	i = lookup_journal_in_cursum(sum, NAT_JOURNAL, nid, 0);	if (i >= 0) {		ne = nat_in_journal(sum, i);		node_info_from_raw_nat(ni, &ne);	}	mutex_unlock(&curseg->curseg_mutex);	if (i >= 0)		goto cache; 	/* Fill node_info from nat page */	page = get_current_nat_page(sbi, start_nid);	nat_blk = (struct f2fs_nat_block *)page_address(page);	ne = nat_blk->entries[nid - start_nid];	node_info_from_raw_nat(ni, &ne);	f2fs_put_page(page, 1);cache:	/* cache nat entry */	cache_nat_entry(NM_I(sbi), nid, &ne);} /* * The maximum depth is four. * Offset[0] will have raw inode offset. */static int get_node_path(struct f2fs_inode_info *fi, long block,				int offset[4], unsigned int noffset[4]){	const long direct_index = ADDRS_PER_INODE(fi);	const long direct_blks = ADDRS_PER_BLOCK;	const long dptrs_per_blk = NIDS_PER_BLOCK;	const long indirect_blks = ADDRS_PER_BLOCK * NIDS_PER_BLOCK;	const long dindirect_blks = indirect_blks * NIDS_PER_BLOCK;	int n = 0;	int level = 0; 	noffset[0] = 0; 	if (block < direct_index) {		offset[n] = block;		goto got;	}	block -= direct_index;	if (block < direct_blks) {		offset[n++] = NODE_DIR1_BLOCK;		noffset[n] = 1;		offset[n] = block;		level = 1;		goto got;	}	block -= direct_blks;	if (block < direct_blks) {		offset[n++] = NODE_DIR2_BLOCK;		noffset[n] = 2;		offset[n] = block;		level = 1;		goto got;	}	block -= direct_blks;	if (block < indirect_blks) {		offset[n++] = NODE_IND1_BLOCK;		noffset[n] = 3;		offset[n++] = block / direct_blks;		noffset[n] = 4 + offset[n - 1];		offset[n] = block % direct_blks;		level = 2;		goto got;	}	block -= indirect_blks;	if (block < indirect_blks) {		offset[n++] = NODE_IND2_BLOCK;		noffset[n] = 4 + dptrs_per_blk;		offset[n++] = block / direct_blks;		noffset[n] = 5 + dptrs_per_blk + offset[n - 1];		offset[n] = block % direct_blks;		level = 2;		goto got;	}	block -= indirect_blks;	if (block < dindirect_blks) {		offset[n++] = NODE_DIND_BLOCK;		noffset[n] = 5 + (dptrs_per_blk * 2);		offset[n++] = block / indirect_blks;		noffset[n] = 6 + (dptrs_per_blk * 2) +			      offset[n - 1] * (dptrs_per_blk + 1);		offset[n++] = (block / direct_blks) % dptrs_per_blk;		noffset[n] = 7 + (dptrs_per_blk * 2) +			      offset[n - 2] * (dptrs_per_blk + 1) +			      offset[n - 1];		offset[n] = block % direct_blks;		level = 3;		goto got;	} else {		BUG();	}got:	return level;} /* * Caller should call f2fs_put_dnode(dn). * Also, it should grab and release a rwsem by calling f2fs_lock_op() and * f2fs_unlock_op() only if ro is not set RDONLY_NODE. * In the case of RDONLY_NODE, we don't need to care about mutex. */int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int mode){	struct f2fs_sb_info *sbi = F2FS_I_SB(dn->inode);	struct page *npage[4];	struct page *parent = NULL;	int offset[4];	unsigned int noffset[4];	nid_t nids[4];	int level, i;	int err = 0; 	level = get_node_path(F2FS_I(dn->inode), index, offset, noffset); 	nids[0] = dn->inode->i_ino;	npage[0] = dn->inode_page; 	if (!npage[0]) {

Lines 1–500 of 2090.