2081 lines · 49.5 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/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 mem_size = 0;	bool res = false; 	si_meminfo(&val);	/* give 25%, 25%, 50% memory for each components respectively */	if (type == FREE_NIDS) {		mem_size = (nm_i->fcnt * sizeof(struct free_nid)) >> 12;		res = mem_size < ((val.totalram * nm_i->ram_thresh / 100) >> 2);	} else if (type == NAT_ENTRIES) {		mem_size = (nm_i->nat_cnt * sizeof(struct nat_entry)) >> 12;		res = mem_size < ((val.totalram * nm_i->ram_thresh / 100) >> 2);	} else if (type == DIRTY_DENTS) {		if (sbi->sb->s_bdi->dirty_exceeded)			return false;		mem_size = get_pages(sbi, F2FS_DIRTY_DENTS);		res = mem_size < ((val.totalram * nm_i->ram_thresh / 100) >> 1);	}	return res;} static void clear_node_page_dirty(struct page *page){	struct address_space *mapping = page->mapping;	struct f2fs_sb_info *sbi = F2FS_SB(mapping->host->i_sb);	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(sbi, 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(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);} int is_checkpointed_node(struct f2fs_sb_info *sbi, nid_t nid){	struct f2fs_nm_info *nm_i = NM_I(sbi);	struct nat_entry *e;	int is_cp = 1; 	read_lock(&nm_i->nat_tree_lock);	e = __lookup_nat_cache(nm_i, nid);	if (e && !e->checkpointed)		is_cp = 0;	read_unlock(&nm_i->nat_tree_lock);	return is_cp;} bool fsync_mark_done(struct f2fs_sb_info *sbi, nid_t nid){	struct f2fs_nm_info *nm_i = NM_I(sbi);	struct nat_entry *e;	bool fsync_done = false; 	read_lock(&nm_i->nat_tree_lock);	e = __lookup_nat_cache(nm_i, nid);	if (e)		fsync_done = e->fsync_done;	read_unlock(&nm_i->nat_tree_lock);	return fsync_done;} void fsync_mark_clear(struct f2fs_sb_info *sbi, nid_t nid){	struct f2fs_nm_info *nm_i = NM_I(sbi);	struct nat_entry *e; 	write_lock(&nm_i->nat_tree_lock);	e = __lookup_nat_cache(nm_i, nid);	if (e)		e->fsync_done = false;	write_unlock(&nm_i->nat_tree_lock);} static struct nat_entry *grab_nat_entry(struct f2fs_nm_info *nm_i, nid_t nid){	struct nat_entry *new; 	new = kmem_cache_alloc(nat_entry_slab, GFP_ATOMIC);	if (!new)		return NULL;	if (radix_tree_insert(&nm_i->nat_root, nid, new)) {		kmem_cache_free(nat_entry_slab, new);		return NULL;	}	memset(new, 0, sizeof(struct nat_entry));	nat_set_nid(new, nid);	new->checkpointed = true;	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;retry:	write_lock(&nm_i->nat_tree_lock);	e = __lookup_nat_cache(nm_i, nid);	if (!e) {		e = grab_nat_entry(nm_i, nid);		if (!e) {			write_unlock(&nm_i->nat_tree_lock);			goto retry;		}		node_info_from_raw_nat(&e->ni, ne);	}	write_unlock(&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;retry:	write_lock(&nm_i->nat_tree_lock);	e = __lookup_nat_cache(nm_i, ni->nid);	if (!e) {		e = grab_nat_entry(nm_i, ni->nid);		if (!e) {			write_unlock(&nm_i->nat_tree_lock);			goto retry;		}		e->ni = *ni;		f2fs_bug_on(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.		 */		e->ni = *ni;		f2fs_bug_on(ni->blk_addr != NULL_ADDR);	} 	/* sanity check */	f2fs_bug_on(nat_get_blkaddr(e) != ni->blk_addr);	f2fs_bug_on(nat_get_blkaddr(e) == NULL_ADDR &&			new_blkaddr == NULL_ADDR);	f2fs_bug_on(nat_get_blkaddr(e) == NEW_ADDR &&			new_blkaddr == NEW_ADDR);	f2fs_bug_on(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);	__set_nat_cache_dirty(nm_i, e); 	/* update fsync_mark if its inode nat entry is still alive */	e = __lookup_nat_cache(nm_i, ni->ino);	if (e)		e->fsync_done = fsync_done;	write_unlock(&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; 	write_lock(&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--;	}	write_unlock(&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; 	memset(&ne, 0, sizeof(struct f2fs_nat_entry));	ni->nid = nid; 	/* Check nat cache */	read_lock(&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);	}	read_unlock(&nm_i->nat_tree_lock);	if (e)		return; 	/* 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_SB(dn->inode->i_sb);	struct page *npage[4];	struct page *parent;	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]) {		npage[0] = get_node_page(sbi, nids[0]);		if (IS_ERR(npage[0]))			return PTR_ERR(npage[0]);	}	parent = npage[0];	if (level != 0)		nids[1] = get_nid(parent, offset[0], true);	dn->inode_page = npage[0];	dn->inode_page_locked = true; 	/* get indirect or direct nodes */	for (i = 1; i <= level; i++) {		bool done = false; 		if (!nids[i] && mode == ALLOC_NODE) {			/* alloc new node */			if (!alloc_nid(sbi, &(nids[i]))) {				err = -ENOSPC;				goto release_pages;			} 			dn->nid = nids[i];			npage[i] = new_node_page(dn, noffset[i], NULL);			if (IS_ERR(npage[i])) {				alloc_nid_failed(sbi, nids[i]);				err = PTR_ERR(npage[i]);				goto release_pages;			} 			set_nid(parent, offset[i - 1], nids[i], i == 1);			alloc_nid_done(sbi, nids[i]);			done = true;		} else if (mode == LOOKUP_NODE_RA && i == level && level > 1) {			npage[i] = get_node_page_ra(parent, offset[i - 1]);			if (IS_ERR(npage[i])) {				err = PTR_ERR(npage[i]);				goto release_pages;			}			done = true;		}		if (i == 1) {			dn->inode_page_locked = false;			unlock_page(parent);		} else {			f2fs_put_page(parent, 1);		} 		if (!done) {			npage[i] = get_node_page(sbi, nids[i]);			if (IS_ERR(npage[i])) {				err = PTR_ERR(npage[i]);				f2fs_put_page(npage[0], 0);				goto release_out;			}		}		if (i < level) {			parent = npage[i];			nids[i + 1] = get_nid(parent, offset[i], false);		}	}	dn->nid = nids[level];	dn->ofs_in_node = offset[level];	dn->node_page = npage[level];	dn->data_blkaddr = datablock_addr(dn->node_page, dn->ofs_in_node);	return 0; release_pages:	f2fs_put_page(parent, 1);	if (i > 1)		f2fs_put_page(npage[0], 0);release_out:	dn->inode_page = NULL;

Lines 1–500 of 2081.