diff options
| -rw-r--r-- | fs/ext4/ext4.h | 2 | ||||
| -rw-r--r-- | fs/ext4/ext4_extents.h | 15 | ||||
| -rw-r--r-- | fs/ext4/extents.c | 248 | ||||
| -rw-r--r-- | fs/ext4/file.c | 4 | ||||
| -rw-r--r-- | fs/ext4/inode.c | 4 |
5 files changed, 271 insertions, 2 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index c50c04cc6d7b..f46a513a5157 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
| @@ -1067,6 +1067,8 @@ struct buffer_head *ext4_getblk(handle_t *, struct inode *, | |||
| 1067 | ext4_lblk_t, int, int *); | 1067 | ext4_lblk_t, int, int *); |
| 1068 | struct buffer_head *ext4_bread(handle_t *, struct inode *, | 1068 | struct buffer_head *ext4_bread(handle_t *, struct inode *, |
| 1069 | ext4_lblk_t, int, int *); | 1069 | ext4_lblk_t, int, int *); |
| 1070 | int ext4_get_block(struct inode *inode, sector_t iblock, | ||
| 1071 | struct buffer_head *bh_result, int create); | ||
| 1070 | int ext4_get_blocks_handle(handle_t *handle, struct inode *inode, | 1072 | int ext4_get_blocks_handle(handle_t *handle, struct inode *inode, |
| 1071 | ext4_lblk_t iblock, unsigned long maxblocks, | 1073 | ext4_lblk_t iblock, unsigned long maxblocks, |
| 1072 | struct buffer_head *bh_result, | 1074 | struct buffer_head *bh_result, |
diff --git a/fs/ext4/ext4_extents.h b/fs/ext4/ext4_extents.h index d33dc56d6986..bec7ce59fc0d 100644 --- a/fs/ext4/ext4_extents.h +++ b/fs/ext4/ext4_extents.h | |||
| @@ -124,6 +124,19 @@ struct ext4_ext_path { | |||
| 124 | #define EXT4_EXT_CACHE_GAP 1 | 124 | #define EXT4_EXT_CACHE_GAP 1 |
| 125 | #define EXT4_EXT_CACHE_EXTENT 2 | 125 | #define EXT4_EXT_CACHE_EXTENT 2 |
| 126 | 126 | ||
| 127 | /* | ||
| 128 | * to be called by ext4_ext_walk_space() | ||
| 129 | * negative retcode - error | ||
| 130 | * positive retcode - signal for ext4_ext_walk_space(), see below | ||
| 131 | * callback must return valid extent (passed or newly created) | ||
| 132 | */ | ||
| 133 | typedef int (*ext_prepare_callback)(struct inode *, struct ext4_ext_path *, | ||
| 134 | struct ext4_ext_cache *, | ||
| 135 | struct ext4_extent *, void *); | ||
| 136 | |||
| 137 | #define EXT_CONTINUE 0 | ||
| 138 | #define EXT_BREAK 1 | ||
| 139 | #define EXT_REPEAT 2 | ||
| 127 | 140 | ||
| 128 | #define EXT_MAX_BLOCK 0xffffffff | 141 | #define EXT_MAX_BLOCK 0xffffffff |
| 129 | 142 | ||
| @@ -224,6 +237,8 @@ extern int ext4_ext_try_to_merge(struct inode *inode, | |||
| 224 | struct ext4_extent *); | 237 | struct ext4_extent *); |
| 225 | extern unsigned int ext4_ext_check_overlap(struct inode *, struct ext4_extent *, struct ext4_ext_path *); | 238 | extern unsigned int ext4_ext_check_overlap(struct inode *, struct ext4_extent *, struct ext4_ext_path *); |
| 226 | extern int ext4_ext_insert_extent(handle_t *, struct inode *, struct ext4_ext_path *, struct ext4_extent *); | 239 | extern int ext4_ext_insert_extent(handle_t *, struct inode *, struct ext4_ext_path *, struct ext4_extent *); |
| 240 | extern int ext4_ext_walk_space(struct inode *, ext4_lblk_t, ext4_lblk_t, | ||
| 241 | ext_prepare_callback, void *); | ||
| 227 | extern struct ext4_ext_path *ext4_ext_find_extent(struct inode *, ext4_lblk_t, | 242 | extern struct ext4_ext_path *ext4_ext_find_extent(struct inode *, ext4_lblk_t, |
| 228 | struct ext4_ext_path *); | 243 | struct ext4_ext_path *); |
| 229 | extern int ext4_ext_search_left(struct inode *, struct ext4_ext_path *, | 244 | extern int ext4_ext_search_left(struct inode *, struct ext4_ext_path *, |
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index c8f81f2fb28e..ea2ce3c0ae66 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c | |||
| @@ -40,6 +40,7 @@ | |||
| 40 | #include <linux/slab.h> | 40 | #include <linux/slab.h> |
| 41 | #include <linux/falloc.h> | 41 | #include <linux/falloc.h> |
| 42 | #include <asm/uaccess.h> | 42 | #include <asm/uaccess.h> |
| 43 | #include <linux/fiemap.h> | ||
| 43 | #include "ext4_jbd2.h" | 44 | #include "ext4_jbd2.h" |
| 44 | #include "ext4_extents.h" | 45 | #include "ext4_extents.h" |
| 45 | 46 | ||
| @@ -1626,6 +1627,113 @@ cleanup: | |||
| 1626 | return err; | 1627 | return err; |
| 1627 | } | 1628 | } |
| 1628 | 1629 | ||
| 1630 | int ext4_ext_walk_space(struct inode *inode, ext4_lblk_t block, | ||
| 1631 | ext4_lblk_t num, ext_prepare_callback func, | ||
| 1632 | void *cbdata) | ||
| 1633 | { | ||
| 1634 | struct ext4_ext_path *path = NULL; | ||
| 1635 | struct ext4_ext_cache cbex; | ||
| 1636 | struct ext4_extent *ex; | ||
| 1637 | ext4_lblk_t next, start = 0, end = 0; | ||
| 1638 | ext4_lblk_t last = block + num; | ||
| 1639 | int depth, exists, err = 0; | ||
| 1640 | |||
| 1641 | BUG_ON(func == NULL); | ||
| 1642 | BUG_ON(inode == NULL); | ||
| 1643 | |||
| 1644 | while (block < last && block != EXT_MAX_BLOCK) { | ||
| 1645 | num = last - block; | ||
| 1646 | /* find extent for this block */ | ||
| 1647 | path = ext4_ext_find_extent(inode, block, path); | ||
| 1648 | if (IS_ERR(path)) { | ||
| 1649 | err = PTR_ERR(path); | ||
| 1650 | path = NULL; | ||
| 1651 | break; | ||
| 1652 | } | ||
| 1653 | |||
| 1654 | depth = ext_depth(inode); | ||
| 1655 | BUG_ON(path[depth].p_hdr == NULL); | ||
| 1656 | ex = path[depth].p_ext; | ||
| 1657 | next = ext4_ext_next_allocated_block(path); | ||
| 1658 | |||
| 1659 | exists = 0; | ||
| 1660 | if (!ex) { | ||
| 1661 | /* there is no extent yet, so try to allocate | ||
| 1662 | * all requested space */ | ||
| 1663 | start = block; | ||
| 1664 | end = block + num; | ||
| 1665 | } else if (le32_to_cpu(ex->ee_block) > block) { | ||
| 1666 | /* need to allocate space before found extent */ | ||
| 1667 | start = block; | ||
| 1668 | end = le32_to_cpu(ex->ee_block); | ||
| 1669 | if (block + num < end) | ||
| 1670 | end = block + num; | ||
| 1671 | } else if (block >= le32_to_cpu(ex->ee_block) | ||
| 1672 | + ext4_ext_get_actual_len(ex)) { | ||
| 1673 | /* need to allocate space after found extent */ | ||
| 1674 | start = block; | ||
| 1675 | end = block + num; | ||
| 1676 | if (end >= next) | ||
| 1677 | end = next; | ||
| 1678 | } else if (block >= le32_to_cpu(ex->ee_block)) { | ||
| 1679 | /* | ||
| 1680 | * some part of requested space is covered | ||
| 1681 | * by found extent | ||
| 1682 | */ | ||
| 1683 | start = block; | ||
| 1684 | end = le32_to_cpu(ex->ee_block) | ||
| 1685 | + ext4_ext_get_actual_len(ex); | ||
| 1686 | if (block + num < end) | ||
| 1687 | end = block + num; | ||
| 1688 | exists = 1; | ||
| 1689 | } else { | ||
| 1690 | BUG(); | ||
| 1691 | } | ||
| 1692 | BUG_ON(end <= start); | ||
| 1693 | |||
| 1694 | if (!exists) { | ||
| 1695 | cbex.ec_block = start; | ||
| 1696 | cbex.ec_len = end - start; | ||
| 1697 | cbex.ec_start = 0; | ||
| 1698 | cbex.ec_type = EXT4_EXT_CACHE_GAP; | ||
| 1699 | } else { | ||
| 1700 | cbex.ec_block = le32_to_cpu(ex->ee_block); | ||
| 1701 | cbex.ec_len = ext4_ext_get_actual_len(ex); | ||
| 1702 | cbex.ec_start = ext_pblock(ex); | ||
| 1703 | cbex.ec_type = EXT4_EXT_CACHE_EXTENT; | ||
| 1704 | } | ||
| 1705 | |||
| 1706 | BUG_ON(cbex.ec_len == 0); | ||
| 1707 | err = func(inode, path, &cbex, ex, cbdata); | ||
| 1708 | ext4_ext_drop_refs(path); | ||
| 1709 | |||
| 1710 | if (err < 0) | ||
| 1711 | break; | ||
| 1712 | |||
| 1713 | if (err == EXT_REPEAT) | ||
| 1714 | continue; | ||
| 1715 | else if (err == EXT_BREAK) { | ||
| 1716 | err = 0; | ||
| 1717 | break; | ||
| 1718 | } | ||
| 1719 | |||
| 1720 | if (ext_depth(inode) != depth) { | ||
| 1721 | /* depth was changed. we have to realloc path */ | ||
| 1722 | kfree(path); | ||
| 1723 | path = NULL; | ||
| 1724 | } | ||
| 1725 | |||
| 1726 | block = cbex.ec_block + cbex.ec_len; | ||
| 1727 | } | ||
| 1728 | |||
| 1729 | if (path) { | ||
| 1730 | ext4_ext_drop_refs(path); | ||
| 1731 | kfree(path); | ||
| 1732 | } | ||
| 1733 | |||
| 1734 | return err; | ||
| 1735 | } | ||
| 1736 | |||
| 1629 | static void | 1737 | static void |
| 1630 | ext4_ext_put_in_cache(struct inode *inode, ext4_lblk_t block, | 1738 | ext4_ext_put_in_cache(struct inode *inode, ext4_lblk_t block, |
| 1631 | __u32 len, ext4_fsblk_t start, int type) | 1739 | __u32 len, ext4_fsblk_t start, int type) |
| @@ -2971,3 +3079,143 @@ retry: | |||
| 2971 | mutex_unlock(&inode->i_mutex); | 3079 | mutex_unlock(&inode->i_mutex); |
| 2972 | return ret > 0 ? ret2 : ret; | 3080 | return ret > 0 ? ret2 : ret; |
| 2973 | } | 3081 | } |
| 3082 | |||
| 3083 | /* | ||
| 3084 | * Callback function called for each extent to gather FIEMAP information. | ||
| 3085 | */ | ||
| 3086 | int ext4_ext_fiemap_cb(struct inode *inode, struct ext4_ext_path *path, | ||
| 3087 | struct ext4_ext_cache *newex, struct ext4_extent *ex, | ||
| 3088 | void *data) | ||
| 3089 | { | ||
| 3090 | struct fiemap_extent_info *fieinfo = data; | ||
| 3091 | unsigned long blksize_bits = inode->i_sb->s_blocksize_bits; | ||
| 3092 | __u64 logical; | ||
| 3093 | __u64 physical; | ||
| 3094 | __u64 length; | ||
| 3095 | __u32 flags = 0; | ||
| 3096 | int error; | ||
| 3097 | |||
| 3098 | logical = (__u64)newex->ec_block << blksize_bits; | ||
| 3099 | |||
| 3100 | if (newex->ec_type == EXT4_EXT_CACHE_GAP) { | ||
| 3101 | pgoff_t offset; | ||
| 3102 | struct page *page; | ||
| 3103 | struct buffer_head *bh = NULL; | ||
| 3104 | |||
| 3105 | offset = logical >> PAGE_SHIFT; | ||
| 3106 | page = find_get_page(inode->i_mapping, offset); | ||
| 3107 | if (!page || !page_has_buffers(page)) | ||
| 3108 | return EXT_CONTINUE; | ||
| 3109 | |||
| 3110 | bh = page_buffers(page); | ||
| 3111 | |||
| 3112 | if (!bh) | ||
| 3113 | return EXT_CONTINUE; | ||
| 3114 | |||
| 3115 | if (buffer_delay(bh)) { | ||
| 3116 | flags |= FIEMAP_EXTENT_DELALLOC; | ||
| 3117 | page_cache_release(page); | ||
| 3118 | } else { | ||
| 3119 | page_cache_release(page); | ||
| 3120 | return EXT_CONTINUE; | ||
| 3121 | } | ||
| 3122 | } | ||
| 3123 | |||
| 3124 | physical = (__u64)newex->ec_start << blksize_bits; | ||
| 3125 | length = (__u64)newex->ec_len << blksize_bits; | ||
| 3126 | |||
| 3127 | if (ex && ext4_ext_is_uninitialized(ex)) | ||
| 3128 | flags |= FIEMAP_EXTENT_UNWRITTEN; | ||
| 3129 | |||
| 3130 | /* | ||
| 3131 | * If this extent reaches EXT_MAX_BLOCK, it must be last. | ||
| 3132 | * | ||
| 3133 | * Or if ext4_ext_next_allocated_block is EXT_MAX_BLOCK, | ||
| 3134 | * this also indicates no more allocated blocks. | ||
| 3135 | * | ||
| 3136 | * XXX this might miss a single-block extent at EXT_MAX_BLOCK | ||
| 3137 | */ | ||
| 3138 | if (logical + length - 1 == EXT_MAX_BLOCK || | ||
| 3139 | ext4_ext_next_allocated_block(path) == EXT_MAX_BLOCK) | ||
| 3140 | flags |= FIEMAP_EXTENT_LAST; | ||
| 3141 | |||
| 3142 | error = fiemap_fill_next_extent(fieinfo, logical, physical, | ||
| 3143 | length, flags); | ||
| 3144 | if (error < 0) | ||
| 3145 | return error; | ||
| 3146 | if (error == 1) | ||
| 3147 | return EXT_BREAK; | ||
| 3148 | |||
| 3149 | return EXT_CONTINUE; | ||
| 3150 | } | ||
| 3151 | |||
| 3152 | /* fiemap flags we can handle specified here */ | ||
| 3153 | #define EXT4_FIEMAP_FLAGS (FIEMAP_FLAG_SYNC|FIEMAP_FLAG_XATTR) | ||
| 3154 | |||
| 3155 | int ext4_xattr_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo) | ||
| 3156 | { | ||
| 3157 | __u64 physical = 0; | ||
| 3158 | __u64 length; | ||
| 3159 | __u32 flags = FIEMAP_EXTENT_LAST; | ||
| 3160 | int blockbits = inode->i_sb->s_blocksize_bits; | ||
| 3161 | int error = 0; | ||
| 3162 | |||
| 3163 | /* in-inode? */ | ||
| 3164 | if (EXT4_I(inode)->i_state & EXT4_STATE_XATTR) { | ||
| 3165 | struct ext4_iloc iloc; | ||
| 3166 | int offset; /* offset of xattr in inode */ | ||
| 3167 | |||
| 3168 | error = ext4_get_inode_loc(inode, &iloc); | ||
| 3169 | if (error) | ||
| 3170 | return error; | ||
| 3171 | physical = iloc.bh->b_blocknr << blockbits; | ||
| 3172 | offset = EXT4_GOOD_OLD_INODE_SIZE + | ||
| 3173 | EXT4_I(inode)->i_extra_isize; | ||
| 3174 | physical += offset; | ||
| 3175 | length = EXT4_SB(inode->i_sb)->s_inode_size - offset; | ||
| 3176 | flags |= FIEMAP_EXTENT_DATA_INLINE; | ||
| 3177 | } else { /* external block */ | ||
| 3178 | physical = EXT4_I(inode)->i_file_acl << blockbits; | ||
| 3179 | length = inode->i_sb->s_blocksize; | ||
| 3180 | } | ||
| 3181 | |||
| 3182 | if (physical) | ||
| 3183 | error = fiemap_fill_next_extent(fieinfo, 0, physical, | ||
| 3184 | length, flags); | ||
| 3185 | return (error < 0 ? error : 0); | ||
| 3186 | } | ||
| 3187 | |||
| 3188 | int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, | ||
| 3189 | __u64 start, __u64 len) | ||
| 3190 | { | ||
| 3191 | ext4_lblk_t start_blk; | ||
| 3192 | ext4_lblk_t len_blks; | ||
| 3193 | int error = 0; | ||
| 3194 | |||
| 3195 | /* fallback to generic here if not in extents fmt */ | ||
| 3196 | if (!(EXT4_I(inode)->i_flags & EXT4_EXTENTS_FL)) | ||
| 3197 | return generic_block_fiemap(inode, fieinfo, start, len, | ||
| 3198 | ext4_get_block); | ||
| 3199 | |||
| 3200 | if (fiemap_check_flags(fieinfo, EXT4_FIEMAP_FLAGS)) | ||
| 3201 | return -EBADR; | ||
| 3202 | |||
| 3203 | if (fieinfo->fi_flags & FIEMAP_FLAG_XATTR) { | ||
| 3204 | error = ext4_xattr_fiemap(inode, fieinfo); | ||
| 3205 | } else { | ||
| 3206 | start_blk = start >> inode->i_sb->s_blocksize_bits; | ||
| 3207 | len_blks = len >> inode->i_sb->s_blocksize_bits; | ||
| 3208 | |||
| 3209 | /* | ||
| 3210 | * Walk the extent tree gathering extent information. | ||
| 3211 | * ext4_ext_fiemap_cb will push extents back to user. | ||
| 3212 | */ | ||
| 3213 | down_write(&EXT4_I(inode)->i_data_sem); | ||
| 3214 | error = ext4_ext_walk_space(inode, start_blk, len_blks, | ||
| 3215 | ext4_ext_fiemap_cb, fieinfo); | ||
| 3216 | up_write(&EXT4_I(inode)->i_data_sem); | ||
| 3217 | } | ||
| 3218 | |||
| 3219 | return error; | ||
| 3220 | } | ||
| 3221 | |||
diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 62796b7e1d1b..6d5be156202a 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c | |||
| @@ -140,6 +140,9 @@ static int ext4_file_mmap(struct file *file, struct vm_area_struct *vma) | |||
| 140 | return 0; | 140 | return 0; |
| 141 | } | 141 | } |
| 142 | 142 | ||
| 143 | extern int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, | ||
| 144 | __u64 start, __u64 len); | ||
| 145 | |||
| 143 | const struct file_operations ext4_file_operations = { | 146 | const struct file_operations ext4_file_operations = { |
| 144 | .llseek = generic_file_llseek, | 147 | .llseek = generic_file_llseek, |
| 145 | .read = do_sync_read, | 148 | .read = do_sync_read, |
| @@ -170,5 +173,6 @@ const struct inode_operations ext4_file_inode_operations = { | |||
| 170 | #endif | 173 | #endif |
| 171 | .permission = ext4_permission, | 174 | .permission = ext4_permission, |
| 172 | .fallocate = ext4_fallocate, | 175 | .fallocate = ext4_fallocate, |
| 176 | .fiemap = ext4_fiemap, | ||
| 173 | }; | 177 | }; |
| 174 | 178 | ||
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index bd770c360c14..a4747867411f 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
| @@ -1135,8 +1135,8 @@ int ext4_get_blocks_wrap(handle_t *handle, struct inode *inode, sector_t block, | |||
| 1135 | /* Maximum number of blocks we map for direct IO at once. */ | 1135 | /* Maximum number of blocks we map for direct IO at once. */ |
| 1136 | #define DIO_MAX_BLOCKS 4096 | 1136 | #define DIO_MAX_BLOCKS 4096 |
| 1137 | 1137 | ||
| 1138 | static int ext4_get_block(struct inode *inode, sector_t iblock, | 1138 | int ext4_get_block(struct inode *inode, sector_t iblock, |
| 1139 | struct buffer_head *bh_result, int create) | 1139 | struct buffer_head *bh_result, int create) |
| 1140 | { | 1140 | { |
| 1141 | handle_t *handle = ext4_journal_current_handle(); | 1141 | handle_t *handle = ext4_journal_current_handle(); |
| 1142 | int ret = 0, started = 0; | 1142 | int ret = 0, started = 0; |
