/* * 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" static struct kmem_cache *nat_entry_slab;static struct kmem_cache *free_nid_slab; 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); /* 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); 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;} /* * Readahead NAT pages */static void ra_nat_pages(struct f2fs_sb_info *sbi, int nid){ struct address_space *mapping = sbi->meta_inode->i_mapping; struct f2fs_nm_info *nm_i = NM_I(sbi); struct page *page; pgoff_t index; int i; for (i = 0; i < FREE_NID_PAGES; i++, nid += NAT_ENTRY_PER_BLOCK) { if (nid >= nm_i->max_nid) nid = 0; index = current_nat_addr(sbi, nid); page = grab_cache_page(mapping, index); if (!page) continue; if (f2fs_readpage(sbi, page, index, READ)) { f2fs_put_page(page, 1); continue; } page_cache_release(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;} 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); 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; } nat_set_blkaddr(e, le32_to_cpu(ne->block_addr)); nat_set_ino(e, le32_to_cpu(ne->ino)); nat_set_version(e, ne->version); e->checkpointed = true; } 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){ 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; e->checkpointed = true; 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; BUG_ON(ni->blk_addr != NULL_ADDR); } if (new_blkaddr == NEW_ADDR) e->checkpointed = false; /* sanity check */ BUG_ON(nat_get_blkaddr(e) != ni->blk_addr); BUG_ON(nat_get_blkaddr(e) == NULL_ADDR && new_blkaddr == NULL_ADDR); BUG_ON(nat_get_blkaddr(e) == NEW_ADDR && new_blkaddr == NEW_ADDR); BUG_ON(nat_get_blkaddr(e) != NEW_ADDR && nat_get_blkaddr(e) != NULL_ADDR && new_blkaddr == NEW_ADDR); /* increament 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); write_unlock(&nm_i->nat_tree_lock);} static int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink){ struct f2fs_nm_info *nm_i = NM_I(sbi); if (nm_i->nat_cnt < 2 * NM_WOUT_THRESHOLD) 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 returns always 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(long block, int offset[4], unsigned int noffset[4]){ const long direct_index = ADDRS_PER_INODE; 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; level = 0; 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). */int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int ro){ 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(index, offset, noffset); nids[0] = dn->inode->i_ino; npage[0] = get_node_page(sbi, nids[0]); if (IS_ERR(npage[0])) return PTR_ERR(npage[0]); parent = npage[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] && !ro) { mutex_lock_op(sbi, NODE_NEW); /* alloc new node */ if (!alloc_nid(sbi, &(nids[i]))) { mutex_unlock_op(sbi, NODE_NEW); err = -ENOSPC; goto release_pages; } dn->nid = nids[i]; npage[i] = new_node_page(dn, noffset[i]); if (IS_ERR(npage[i])) { alloc_nid_failed(sbi, nids[i]); mutex_unlock_op(sbi, NODE_NEW); err = PTR_ERR(npage[i]); goto release_pages; } set_nid(parent, offset[i - 1], nids[i], i == 1); alloc_nid_done(sbi, nids[i]); mutex_unlock_op(sbi, NODE_NEW); done = true; } else if (ro && 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; dn->node_page = NULL; return err;} static void truncate_node(struct dnode_of_data *dn){ struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb); struct node_info ni; get_node_info(sbi, dn->nid, &ni); if (dn->inode->i_blocks == 0) { BUG_ON(ni.blk_addr != NULL_ADDR); goto invalidate; } BUG_ON(ni.blk_addr == NULL_ADDR); /* Deallocate node address */ invalidate_blocks(sbi, ni.blk_addr); dec_valid_node_count(sbi, dn->inode, 1); set_node_addr(sbi, &ni, NULL_ADDR); if (dn->nid == dn->inode->i_ino) { remove_orphan_inode(sbi, dn->nid); dec_valid_inode_count(sbi);Lines 1–500 of 1760. Load more
/* * 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" static struct kmem_cache *nat_entry_slab; static struct kmem_cache *free_nid_slab; 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); /* 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); 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; } /* * Readahead NAT pages */ static void ra_nat_pages(struct f2fs_sb_info *sbi, int nid) { struct address_space *mapping = sbi->meta_inode->i_mapping; struct f2fs_nm_info *nm_i = NM_I(sbi); struct page *page; pgoff_t index; int i; for (i = 0; i < FREE_NID_PAGES; i++, nid += NAT_ENTRY_PER_BLOCK) { if (nid >= nm_i->max_nid) nid = 0; index = current_nat_addr(sbi, nid); page = grab_cache_page(mapping, index); if (!page) continue; if (f2fs_readpage(sbi, page, index, READ)) { f2fs_put_page(page, 1); continue; } page_cache_release(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; } 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); 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; } nat_set_blkaddr(e, le32_to_cpu(ne->block_addr)); nat_set_ino(e, le32_to_cpu(ne->ino)); nat_set_version(e, ne->version); e->checkpointed = true; } 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) { 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; e->checkpointed = true; 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; BUG_ON(ni->blk_addr != NULL_ADDR); } if (new_blkaddr == NEW_ADDR) e->checkpointed = false; /* sanity check */ BUG_ON(nat_get_blkaddr(e) != ni->blk_addr); BUG_ON(nat_get_blkaddr(e) == NULL_ADDR && new_blkaddr == NULL_ADDR); BUG_ON(nat_get_blkaddr(e) == NEW_ADDR && new_blkaddr == NEW_ADDR); BUG_ON(nat_get_blkaddr(e) != NEW_ADDR && nat_get_blkaddr(e) != NULL_ADDR && new_blkaddr == NEW_ADDR); /* increament 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); write_unlock(&nm_i->nat_tree_lock); } static int try_to_free_nats(struct f2fs_sb_info *sbi, int nr_shrink) { struct f2fs_nm_info *nm_i = NM_I(sbi); if (nm_i->nat_cnt < 2 * NM_WOUT_THRESHOLD) 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 returns always 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(long block, int offset[4], unsigned int noffset[4]) { const long direct_index = ADDRS_PER_INODE; 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; level = 0; 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). */ int get_dnode_of_data(struct dnode_of_data *dn, pgoff_t index, int ro) { 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(index, offset, noffset); nids[0] = dn->inode->i_ino; npage[0] = get_node_page(sbi, nids[0]); if (IS_ERR(npage[0])) return PTR_ERR(npage[0]); parent = npage[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] && !ro) { mutex_lock_op(sbi, NODE_NEW); /* alloc new node */ if (!alloc_nid(sbi, &(nids[i]))) { mutex_unlock_op(sbi, NODE_NEW); err = -ENOSPC; goto release_pages; } dn->nid = nids[i]; npage[i] = new_node_page(dn, noffset[i]); if (IS_ERR(npage[i])) { alloc_nid_failed(sbi, nids[i]); mutex_unlock_op(sbi, NODE_NEW); err = PTR_ERR(npage[i]); goto release_pages; } set_nid(parent, offset[i - 1], nids[i], i == 1); alloc_nid_done(sbi, nids[i]); mutex_unlock_op(sbi, NODE_NEW); done = true; } else if (ro && 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; dn->node_page = NULL; return err; } static void truncate_node(struct dnode_of_data *dn) { struct f2fs_sb_info *sbi = F2FS_SB(dn->inode->i_sb); struct node_info ni; get_node_info(sbi, dn->nid, &ni); if (dn->inode->i_blocks == 0) { BUG_ON(ni.blk_addr != NULL_ADDR); goto invalidate; } BUG_ON(ni.blk_addr == NULL_ADDR); /* Deallocate node address */ invalidate_blocks(sbi, ni.blk_addr); dec_valid_node_count(sbi, dn->inode, 1); set_node_addr(sbi, &ni, NULL_ADDR); if (dn->nid == dn->inode->i_ino) { remove_orphan_inode(sbi, dn->nid); dec_valid_inode_count(sbi);