📄 extents.c
字号:
/* * Copyright (c) 2003-2006, Cluster File Systems, Inc, info@clusterfs.com * Written by Alex Tomas <alex@clusterfs.com> * * Architecture independence: * Copyright (c) 2005, Bull S.A. * Written by Pierre Peiffer <pierre.peiffer@bull.net> * * 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. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public Licens * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111- *//* * Extents support for EXT4 * * TODO: * - ext4*_error() should be used in some situations * - analyze all BUG()/BUG_ON(), use -EIO where appropriate * - smart tree reduction */#include <linux/module.h>#include <linux/fs.h>#include <linux/time.h>#include <linux/ext4_jbd2.h>#include <linux/jbd2.h>#include <linux/highuid.h>#include <linux/pagemap.h>#include <linux/quotaops.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/falloc.h>#include <linux/ext4_fs_extents.h>#include <asm/uaccess.h>/* * ext_pblock: * combine low and high parts of physical block number into ext4_fsblk_t */static ext4_fsblk_t ext_pblock(struct ext4_extent *ex){ ext4_fsblk_t block; block = le32_to_cpu(ex->ee_start_lo); block |= ((ext4_fsblk_t) le16_to_cpu(ex->ee_start_hi) << 31) << 1; return block;}/* * idx_pblock: * combine low and high parts of a leaf physical block number into ext4_fsblk_t */static ext4_fsblk_t idx_pblock(struct ext4_extent_idx *ix){ ext4_fsblk_t block; block = le32_to_cpu(ix->ei_leaf_lo); block |= ((ext4_fsblk_t) le16_to_cpu(ix->ei_leaf_hi) << 31) << 1; return block;}/* * ext4_ext_store_pblock: * stores a large physical block number into an extent struct, * breaking it into parts */static void ext4_ext_store_pblock(struct ext4_extent *ex, ext4_fsblk_t pb){ ex->ee_start_lo = cpu_to_le32((unsigned long) (pb & 0xffffffff)); ex->ee_start_hi = cpu_to_le16((unsigned long) ((pb >> 31) >> 1) & 0xffff);}/* * ext4_idx_store_pblock: * stores a large physical block number into an index struct, * breaking it into parts */static void ext4_idx_store_pblock(struct ext4_extent_idx *ix, ext4_fsblk_t pb){ ix->ei_leaf_lo = cpu_to_le32((unsigned long) (pb & 0xffffffff)); ix->ei_leaf_hi = cpu_to_le16((unsigned long) ((pb >> 31) >> 1) & 0xffff);}static handle_t *ext4_ext_journal_restart(handle_t *handle, int needed){ int err; if (handle->h_buffer_credits > needed) return handle; if (!ext4_journal_extend(handle, needed)) return handle; err = ext4_journal_restart(handle, needed); return handle;}/* * could return: * - EROFS * - ENOMEM */static int ext4_ext_get_access(handle_t *handle, struct inode *inode, struct ext4_ext_path *path){ if (path->p_bh) { /* path points to block */ return ext4_journal_get_write_access(handle, path->p_bh); } /* path points to leaf/index in inode body */ /* we use in-core data, no need to protect them */ return 0;}/* * could return: * - EROFS * - ENOMEM * - EIO */static int ext4_ext_dirty(handle_t *handle, struct inode *inode, struct ext4_ext_path *path){ int err; if (path->p_bh) { /* path points to block */ err = ext4_journal_dirty_metadata(handle, path->p_bh); } else { /* path points to leaf/index in inode body */ err = ext4_mark_inode_dirty(handle, inode); } return err;}static ext4_fsblk_t ext4_ext_find_goal(struct inode *inode, struct ext4_ext_path *path, ext4_fsblk_t block){ struct ext4_inode_info *ei = EXT4_I(inode); ext4_fsblk_t bg_start; ext4_grpblk_t colour; int depth; if (path) { struct ext4_extent *ex; depth = path->p_depth; /* try to predict block placement */ ex = path[depth].p_ext; if (ex) return ext_pblock(ex)+(block-le32_to_cpu(ex->ee_block)); /* it looks like index is empty; * try to find starting block from index itself */ if (path[depth].p_bh) return path[depth].p_bh->b_blocknr; } /* OK. use inode's group */ bg_start = (ei->i_block_group * EXT4_BLOCKS_PER_GROUP(inode->i_sb)) + le32_to_cpu(EXT4_SB(inode->i_sb)->s_es->s_first_data_block); colour = (current->pid % 16) * (EXT4_BLOCKS_PER_GROUP(inode->i_sb) / 16); return bg_start + colour + block;}static ext4_fsblk_text4_ext_new_block(handle_t *handle, struct inode *inode, struct ext4_ext_path *path, struct ext4_extent *ex, int *err){ ext4_fsblk_t goal, newblock; goal = ext4_ext_find_goal(inode, path, le32_to_cpu(ex->ee_block)); newblock = ext4_new_block(handle, inode, goal, err); return newblock;}static int ext4_ext_space_block(struct inode *inode){ int size; size = (inode->i_sb->s_blocksize - sizeof(struct ext4_extent_header)) / sizeof(struct ext4_extent);#ifdef AGGRESSIVE_TEST if (size > 6) size = 6;#endif return size;}static int ext4_ext_space_block_idx(struct inode *inode){ int size; size = (inode->i_sb->s_blocksize - sizeof(struct ext4_extent_header)) / sizeof(struct ext4_extent_idx);#ifdef AGGRESSIVE_TEST if (size > 5) size = 5;#endif return size;}static int ext4_ext_space_root(struct inode *inode){ int size; size = sizeof(EXT4_I(inode)->i_data); size -= sizeof(struct ext4_extent_header); size /= sizeof(struct ext4_extent);#ifdef AGGRESSIVE_TEST if (size > 3) size = 3;#endif return size;}static int ext4_ext_space_root_idx(struct inode *inode){ int size; size = sizeof(EXT4_I(inode)->i_data); size -= sizeof(struct ext4_extent_header); size /= sizeof(struct ext4_extent_idx);#ifdef AGGRESSIVE_TEST if (size > 4) size = 4;#endif return size;}static intext4_ext_max_entries(struct inode *inode, int depth){ int max; if (depth == ext_depth(inode)) { if (depth == 0) max = ext4_ext_space_root(inode); else max = ext4_ext_space_root_idx(inode); } else { if (depth == 0) max = ext4_ext_space_block(inode); else max = ext4_ext_space_block_idx(inode); } return max;}static int __ext4_ext_check_header(const char *function, struct inode *inode, struct ext4_extent_header *eh, int depth){ const char *error_msg; int max = 0; if (unlikely(eh->eh_magic != EXT4_EXT_MAGIC)) { error_msg = "invalid magic"; goto corrupted; } if (unlikely(le16_to_cpu(eh->eh_depth) != depth)) { error_msg = "unexpected eh_depth"; goto corrupted; } if (unlikely(eh->eh_max == 0)) { error_msg = "invalid eh_max"; goto corrupted; } max = ext4_ext_max_entries(inode, depth); if (unlikely(le16_to_cpu(eh->eh_max) > max)) { error_msg = "too large eh_max"; goto corrupted; } if (unlikely(le16_to_cpu(eh->eh_entries) > le16_to_cpu(eh->eh_max))) { error_msg = "invalid eh_entries"; goto corrupted; } return 0;corrupted: ext4_error(inode->i_sb, function, "bad header in inode #%lu: %s - magic %x, " "entries %u, max %u(%u), depth %u(%u)", inode->i_ino, error_msg, le16_to_cpu(eh->eh_magic), le16_to_cpu(eh->eh_entries), le16_to_cpu(eh->eh_max), max, le16_to_cpu(eh->eh_depth), depth); return -EIO;}#define ext4_ext_check_header(inode, eh, depth) \ __ext4_ext_check_header(__FUNCTION__, inode, eh, depth)#ifdef EXT_DEBUGstatic void ext4_ext_show_path(struct inode *inode, struct ext4_ext_path *path){ int k, l = path->p_depth; ext_debug("path:"); for (k = 0; k <= l; k++, path++) { if (path->p_idx) { ext_debug(" %d->%llu", le32_to_cpu(path->p_idx->ei_block), idx_pblock(path->p_idx)); } else if (path->p_ext) { ext_debug(" %d:%d:%llu ", le32_to_cpu(path->p_ext->ee_block), ext4_ext_get_actual_len(path->p_ext), ext_pblock(path->p_ext)); } else ext_debug(" []"); } ext_debug("\n");}static void ext4_ext_show_leaf(struct inode *inode, struct ext4_ext_path *path){ int depth = ext_depth(inode); struct ext4_extent_header *eh; struct ext4_extent *ex; int i; if (!path) return; eh = path[depth].p_hdr; ex = EXT_FIRST_EXTENT(eh); for (i = 0; i < le16_to_cpu(eh->eh_entries); i++, ex++) { ext_debug("%d:%d:%llu ", le32_to_cpu(ex->ee_block), ext4_ext_get_actual_len(ex), ext_pblock(ex)); } ext_debug("\n");}#else#define ext4_ext_show_path(inode,path)#define ext4_ext_show_leaf(inode,path)#endifstatic void ext4_ext_drop_refs(struct ext4_ext_path *path){ int depth = path->p_depth; int i; for (i = 0; i <= depth; i++, path++) if (path->p_bh) { brelse(path->p_bh); path->p_bh = NULL; }}/* * ext4_ext_binsearch_idx: * binary search for the closest index of the given block * the header must be checked before calling this */static voidext4_ext_binsearch_idx(struct inode *inode, struct ext4_ext_path *path, int block){ struct ext4_extent_header *eh = path->p_hdr; struct ext4_extent_idx *r, *l, *m; ext_debug("binsearch for %d(idx): ", block); l = EXT_FIRST_INDEX(eh) + 1; r = EXT_LAST_INDEX(eh); while (l <= r) { m = l + (r - l) / 2; if (block < le32_to_cpu(m->ei_block)) r = m - 1; else l = m + 1; ext_debug("%p(%u):%p(%u):%p(%u) ", l, le32_to_cpu(l->ei_block), m, le32_to_cpu(m->ei_block), r, le32_to_cpu(r->ei_block)); } path->p_idx = l - 1; ext_debug(" -> %d->%lld ", le32_to_cpu(path->p_idx->ei_block), idx_pblock(path->p_idx));#ifdef CHECK_BINSEARCH { struct ext4_extent_idx *chix, *ix; int k; chix = ix = EXT_FIRST_INDEX(eh); for (k = 0; k < le16_to_cpu(eh->eh_entries); k++, ix++) { if (k != 0 && le32_to_cpu(ix->ei_block) <= le32_to_cpu(ix[-1].ei_block)) { printk("k=%d, ix=0x%p, first=0x%p\n", k, ix, EXT_FIRST_INDEX(eh)); printk("%u <= %u\n", le32_to_cpu(ix->ei_block), le32_to_cpu(ix[-1].ei_block)); } BUG_ON(k && le32_to_cpu(ix->ei_block) <= le32_to_cpu(ix[-1].ei_block)); if (block < le32_to_cpu(ix->ei_block)) break; chix = ix; } BUG_ON(chix != path->p_idx); }#endif}/* * ext4_ext_binsearch: * binary search for closest extent of the given block * the header must be checked before calling this */static voidext4_ext_binsearch(struct inode *inode, struct ext4_ext_path *path, int block){ struct ext4_extent_header *eh = path->p_hdr; struct ext4_extent *r, *l, *m; if (eh->eh_entries == 0) { /* * this leaf is empty: * we get such a leaf in split/add case */ return; } ext_debug("binsearch for %d: ", block); l = EXT_FIRST_EXTENT(eh) + 1; r = EXT_LAST_EXTENT(eh); while (l <= r) { m = l + (r - l) / 2; if (block < le32_to_cpu(m->ee_block)) r = m - 1; else l = m + 1; ext_debug("%p(%u):%p(%u):%p(%u) ", l, le32_to_cpu(l->ee_block), m, le32_to_cpu(m->ee_block), r, le32_to_cpu(r->ee_block)); } path->p_ext = l - 1; ext_debug(" -> %d:%llu:%d ", le32_to_cpu(path->p_ext->ee_block), ext_pblock(path->p_ext), ext4_ext_get_actual_len(path->p_ext));#ifdef CHECK_BINSEARCH { struct ext4_extent *chex, *ex; int k; chex = ex = EXT_FIRST_EXTENT(eh); for (k = 0; k < le16_to_cpu(eh->eh_entries); k++, ex++) { BUG_ON(k && le32_to_cpu(ex->ee_block) <= le32_to_cpu(ex[-1].ee_block)); if (block < le32_to_cpu(ex->ee_block)) break; chex = ex; } BUG_ON(chex != path->p_ext); }#endif}int ext4_ext_tree_init(handle_t *handle, struct inode *inode){ struct ext4_extent_header *eh; eh = ext_inode_hdr(inode); eh->eh_depth = 0; eh->eh_entries = 0; eh->eh_magic = EXT4_EXT_MAGIC; eh->eh_max = cpu_to_le16(ext4_ext_space_root(inode)); ext4_mark_inode_dirty(handle, inode); ext4_ext_invalidate_cache(inode); return 0;}struct ext4_ext_path *ext4_ext_find_extent(struct inode *inode, int block, struct ext4_ext_path *path){ struct ext4_extent_header *eh; struct buffer_head *bh; short int depth, i, ppos = 0, alloc = 0; eh = ext_inode_hdr(inode); depth = ext_depth(inode);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -