diff options
-rw-r--r-- | fs/ext4/ext4.h | 3 | ||||
-rw-r--r-- | fs/ext4/extents.c | 236 | ||||
-rw-r--r-- | fs/ext4/inode.c | 25 |
3 files changed, 253 insertions, 11 deletions
diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index c48f9db5b96b..7bc7c724805d 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h | |||
@@ -1816,6 +1816,7 @@ extern int ext4_change_inode_journal_flag(struct inode *, int); | |||
1816 | extern int ext4_get_inode_loc(struct inode *, struct ext4_iloc *); | 1816 | extern int ext4_get_inode_loc(struct inode *, struct ext4_iloc *); |
1817 | extern int ext4_can_truncate(struct inode *inode); | 1817 | extern int ext4_can_truncate(struct inode *inode); |
1818 | extern void ext4_truncate(struct inode *); | 1818 | extern void ext4_truncate(struct inode *); |
1819 | extern int ext4_punch_hole(struct file *file, loff_t offset, loff_t length); | ||
1819 | extern int ext4_truncate_restart_trans(handle_t *, struct inode *, int nblocks); | 1820 | extern int ext4_truncate_restart_trans(handle_t *, struct inode *, int nblocks); |
1820 | extern void ext4_set_inode_flags(struct inode *); | 1821 | extern void ext4_set_inode_flags(struct inode *); |
1821 | extern void ext4_get_inode_flags(struct ext4_inode_info *); | 1822 | extern void ext4_get_inode_flags(struct ext4_inode_info *); |
@@ -2157,6 +2158,8 @@ extern int ext4_ext_index_trans_blocks(struct inode *inode, int nrblocks, | |||
2157 | extern int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, | 2158 | extern int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, |
2158 | struct ext4_map_blocks *map, int flags); | 2159 | struct ext4_map_blocks *map, int flags); |
2159 | extern void ext4_ext_truncate(struct inode *); | 2160 | extern void ext4_ext_truncate(struct inode *); |
2161 | extern int ext4_ext_punch_hole(struct file *file, loff_t offset, | ||
2162 | loff_t length); | ||
2160 | extern void ext4_ext_init(struct super_block *); | 2163 | extern void ext4_ext_init(struct super_block *); |
2161 | extern void ext4_ext_release(struct super_block *); | 2164 | extern void ext4_ext_release(struct super_block *); |
2162 | extern long ext4_fallocate(struct file *file, int mode, loff_t offset, | 2165 | extern long ext4_fallocate(struct file *file, int mode, loff_t offset, |
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c index 1d456b2ac377..88ff3a74787b 100644 --- a/fs/ext4/extents.c +++ b/fs/ext4/extents.c | |||
@@ -2044,12 +2044,23 @@ ext4_ext_put_gap_in_cache(struct inode *inode, struct ext4_ext_path *path, | |||
2044 | } | 2044 | } |
2045 | 2045 | ||
2046 | /* | 2046 | /* |
2047 | * ext4_ext_in_cache() | ||
2048 | * Checks to see if the given block is in the cache. | ||
2049 | * If it is, the cached extent is stored in the given | ||
2050 | * cache extent pointer. If the cached extent is a hole, | ||
2051 | * this routine should be used instead of | ||
2052 | * ext4_ext_in_cache if the calling function needs to | ||
2053 | * know the size of the hole. | ||
2054 | * | ||
2055 | * @inode: The files inode | ||
2056 | * @block: The block to look for in the cache | ||
2057 | * @ex: Pointer where the cached extent will be stored | ||
2058 | * if it contains block | ||
2059 | * | ||
2047 | * Return 0 if cache is invalid; 1 if the cache is valid | 2060 | * Return 0 if cache is invalid; 1 if the cache is valid |
2048 | */ | 2061 | */ |
2049 | static int | 2062 | static int ext4_ext_check_cache(struct inode *inode, ext4_lblk_t block, |
2050 | ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block, | 2063 | struct ext4_ext_cache *ex){ |
2051 | struct ext4_extent *ex) | ||
2052 | { | ||
2053 | struct ext4_ext_cache *cex; | 2064 | struct ext4_ext_cache *cex; |
2054 | struct ext4_sb_info *sbi; | 2065 | struct ext4_sb_info *sbi; |
2055 | int ret = 0; | 2066 | int ret = 0; |
@@ -2066,9 +2077,7 @@ ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block, | |||
2066 | goto errout; | 2077 | goto errout; |
2067 | 2078 | ||
2068 | if (in_range(block, cex->ec_block, cex->ec_len)) { | 2079 | if (in_range(block, cex->ec_block, cex->ec_len)) { |
2069 | ex->ee_block = cpu_to_le32(cex->ec_block); | 2080 | memcpy(ex, cex, sizeof(struct ext4_ext_cache)); |
2070 | ext4_ext_store_pblock(ex, cex->ec_start); | ||
2071 | ex->ee_len = cpu_to_le16(cex->ec_len); | ||
2072 | ext_debug("%u cached by %u:%u:%llu\n", | 2081 | ext_debug("%u cached by %u:%u:%llu\n", |
2073 | block, | 2082 | block, |
2074 | cex->ec_block, cex->ec_len, cex->ec_start); | 2083 | cex->ec_block, cex->ec_len, cex->ec_start); |
@@ -2084,6 +2093,37 @@ errout: | |||
2084 | } | 2093 | } |
2085 | 2094 | ||
2086 | /* | 2095 | /* |
2096 | * ext4_ext_in_cache() | ||
2097 | * Checks to see if the given block is in the cache. | ||
2098 | * If it is, the cached extent is stored in the given | ||
2099 | * extent pointer. | ||
2100 | * | ||
2101 | * @inode: The files inode | ||
2102 | * @block: The block to look for in the cache | ||
2103 | * @ex: Pointer where the cached extent will be stored | ||
2104 | * if it contains block | ||
2105 | * | ||
2106 | * Return 0 if cache is invalid; 1 if the cache is valid | ||
2107 | */ | ||
2108 | static int | ||
2109 | ext4_ext_in_cache(struct inode *inode, ext4_lblk_t block, | ||
2110 | struct ext4_extent *ex) | ||
2111 | { | ||
2112 | struct ext4_ext_cache cex; | ||
2113 | int ret = 0; | ||
2114 | |||
2115 | if (ext4_ext_check_cache(inode, block, &cex)) { | ||
2116 | ex->ee_block = cpu_to_le32(cex.ec_block); | ||
2117 | ext4_ext_store_pblock(ex, cex.ec_start); | ||
2118 | ex->ee_len = cpu_to_le16(cex.ec_len); | ||
2119 | ret = 1; | ||
2120 | } | ||
2121 | |||
2122 | return ret; | ||
2123 | } | ||
2124 | |||
2125 | |||
2126 | /* | ||
2087 | * ext4_ext_rm_idx: | 2127 | * ext4_ext_rm_idx: |
2088 | * removes index from the index block. | 2128 | * removes index from the index block. |
2089 | * It's used in truncate case only, thus all requests are for | 2129 | * It's used in truncate case only, thus all requests are for |
@@ -3724,10 +3764,6 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len) | |||
3724 | struct ext4_map_blocks map; | 3764 | struct ext4_map_blocks map; |
3725 | unsigned int credits, blkbits = inode->i_blkbits; | 3765 | unsigned int credits, blkbits = inode->i_blkbits; |
3726 | 3766 | ||
3727 | /* We only support the FALLOC_FL_KEEP_SIZE mode */ | ||
3728 | if (mode & ~FALLOC_FL_KEEP_SIZE) | ||
3729 | return -EOPNOTSUPP; | ||
3730 | |||
3731 | /* | 3767 | /* |
3732 | * currently supporting (pre)allocate mode for extent-based | 3768 | * currently supporting (pre)allocate mode for extent-based |
3733 | * files _only_ | 3769 | * files _only_ |
@@ -3735,6 +3771,13 @@ long ext4_fallocate(struct file *file, int mode, loff_t offset, loff_t len) | |||
3735 | if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) | 3771 | if (!(ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS))) |
3736 | return -EOPNOTSUPP; | 3772 | return -EOPNOTSUPP; |
3737 | 3773 | ||
3774 | /* Return error if mode is not supported */ | ||
3775 | if (mode & ~(FALLOC_FL_KEEP_SIZE | FALLOC_FL_PUNCH_HOLE)) | ||
3776 | return -EOPNOTSUPP; | ||
3777 | |||
3778 | if (mode & FALLOC_FL_PUNCH_HOLE) | ||
3779 | return ext4_punch_hole(file, offset, len); | ||
3780 | |||
3738 | trace_ext4_fallocate_enter(inode, offset, len, mode); | 3781 | trace_ext4_fallocate_enter(inode, offset, len, mode); |
3739 | map.m_lblk = offset >> blkbits; | 3782 | map.m_lblk = offset >> blkbits; |
3740 | /* | 3783 | /* |
@@ -4100,6 +4143,177 @@ static int ext4_xattr_fiemap(struct inode *inode, | |||
4100 | return (error < 0 ? error : 0); | 4143 | return (error < 0 ? error : 0); |
4101 | } | 4144 | } |
4102 | 4145 | ||
4146 | /* | ||
4147 | * ext4_ext_punch_hole | ||
4148 | * | ||
4149 | * Punches a hole of "length" bytes in a file starting | ||
4150 | * at byte "offset" | ||
4151 | * | ||
4152 | * @inode: The inode of the file to punch a hole in | ||
4153 | * @offset: The starting byte offset of the hole | ||
4154 | * @length: The length of the hole | ||
4155 | * | ||
4156 | * Returns the number of blocks removed or negative on err | ||
4157 | */ | ||
4158 | int ext4_ext_punch_hole(struct file *file, loff_t offset, loff_t length) | ||
4159 | { | ||
4160 | struct inode *inode = file->f_path.dentry->d_inode; | ||
4161 | struct super_block *sb = inode->i_sb; | ||
4162 | struct ext4_ext_cache cache_ex; | ||
4163 | ext4_lblk_t first_block, last_block, num_blocks, iblock, max_blocks; | ||
4164 | struct address_space *mapping = inode->i_mapping; | ||
4165 | struct ext4_map_blocks map; | ||
4166 | handle_t *handle; | ||
4167 | loff_t first_block_offset, last_block_offset, block_len; | ||
4168 | loff_t first_page, last_page, first_page_offset, last_page_offset; | ||
4169 | int ret, credits, blocks_released, err = 0; | ||
4170 | |||
4171 | first_block = (offset + sb->s_blocksize - 1) >> | ||
4172 | EXT4_BLOCK_SIZE_BITS(sb); | ||
4173 | last_block = (offset + length) >> EXT4_BLOCK_SIZE_BITS(sb); | ||
4174 | |||
4175 | first_block_offset = first_block << EXT4_BLOCK_SIZE_BITS(sb); | ||
4176 | last_block_offset = last_block << EXT4_BLOCK_SIZE_BITS(sb); | ||
4177 | |||
4178 | first_page = (offset + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT; | ||
4179 | last_page = (offset + length) >> PAGE_CACHE_SHIFT; | ||
4180 | |||
4181 | first_page_offset = first_page << PAGE_CACHE_SHIFT; | ||
4182 | last_page_offset = last_page << PAGE_CACHE_SHIFT; | ||
4183 | |||
4184 | /* | ||
4185 | * Write out all dirty pages to avoid race conditions | ||
4186 | * Then release them. | ||
4187 | */ | ||
4188 | if (mapping->nrpages && mapping_tagged(mapping, PAGECACHE_TAG_DIRTY)) { | ||
4189 | err = filemap_write_and_wait_range(mapping, | ||
4190 | first_page_offset == 0 ? 0 : first_page_offset-1, | ||
4191 | last_page_offset); | ||
4192 | |||
4193 | if (err) | ||
4194 | return err; | ||
4195 | } | ||
4196 | |||
4197 | /* Now release the pages */ | ||
4198 | if (last_page_offset > first_page_offset) { | ||
4199 | truncate_inode_pages_range(mapping, first_page_offset, | ||
4200 | last_page_offset-1); | ||
4201 | } | ||
4202 | |||
4203 | /* finish any pending end_io work */ | ||
4204 | ext4_flush_completed_IO(inode); | ||
4205 | |||
4206 | credits = ext4_writepage_trans_blocks(inode); | ||
4207 | handle = ext4_journal_start(inode, credits); | ||
4208 | if (IS_ERR(handle)) | ||
4209 | return PTR_ERR(handle); | ||
4210 | |||
4211 | err = ext4_orphan_add(handle, inode); | ||
4212 | if (err) | ||
4213 | goto out; | ||
4214 | |||
4215 | /* | ||
4216 | * Now we need to zero out the un block aligned data. | ||
4217 | * If the file is smaller than a block, just | ||
4218 | * zero out the middle | ||
4219 | */ | ||
4220 | if (first_block > last_block) | ||
4221 | ext4_block_zero_page_range(handle, mapping, offset, length); | ||
4222 | else { | ||
4223 | /* zero out the head of the hole before the first block */ | ||
4224 | block_len = first_block_offset - offset; | ||
4225 | if (block_len > 0) | ||
4226 | ext4_block_zero_page_range(handle, mapping, | ||
4227 | offset, block_len); | ||
4228 | |||
4229 | /* zero out the tail of the hole after the last block */ | ||
4230 | block_len = offset + length - last_block_offset; | ||
4231 | if (block_len > 0) { | ||
4232 | ext4_block_zero_page_range(handle, mapping, | ||
4233 | last_block_offset, block_len); | ||
4234 | } | ||
4235 | } | ||
4236 | |||
4237 | /* If there are no blocks to remove, return now */ | ||
4238 | if (first_block >= last_block) | ||
4239 | goto out; | ||
4240 | |||
4241 | down_write(&EXT4_I(inode)->i_data_sem); | ||
4242 | ext4_ext_invalidate_cache(inode); | ||
4243 | ext4_discard_preallocations(inode); | ||
4244 | |||
4245 | /* | ||
4246 | * Loop over all the blocks and identify blocks | ||
4247 | * that need to be punched out | ||
4248 | */ | ||
4249 | iblock = first_block; | ||
4250 | blocks_released = 0; | ||
4251 | while (iblock < last_block) { | ||
4252 | max_blocks = last_block - iblock; | ||
4253 | num_blocks = 1; | ||
4254 | memset(&map, 0, sizeof(map)); | ||
4255 | map.m_lblk = iblock; | ||
4256 | map.m_len = max_blocks; | ||
4257 | ret = ext4_ext_map_blocks(handle, inode, &map, | ||
4258 | EXT4_GET_BLOCKS_PUNCH_OUT_EXT); | ||
4259 | |||
4260 | if (ret > 0) { | ||
4261 | blocks_released += ret; | ||
4262 | num_blocks = ret; | ||
4263 | } else if (ret == 0) { | ||
4264 | /* | ||
4265 | * If map blocks could not find the block, | ||
4266 | * then it is in a hole. If the hole was | ||
4267 | * not already cached, then map blocks should | ||
4268 | * put it in the cache. So we can get the hole | ||
4269 | * out of the cache | ||
4270 | */ | ||
4271 | memset(&cache_ex, 0, sizeof(cache_ex)); | ||
4272 | if ((ext4_ext_check_cache(inode, iblock, &cache_ex)) && | ||
4273 | !cache_ex.ec_start) { | ||
4274 | |||
4275 | /* The hole is cached */ | ||
4276 | num_blocks = cache_ex.ec_block + | ||
4277 | cache_ex.ec_len - iblock; | ||
4278 | |||
4279 | } else { | ||
4280 | /* The block could not be identified */ | ||
4281 | err = -EIO; | ||
4282 | break; | ||
4283 | } | ||
4284 | } else { | ||
4285 | /* Map blocks error */ | ||
4286 | err = ret; | ||
4287 | break; | ||
4288 | } | ||
4289 | |||
4290 | if (num_blocks == 0) { | ||
4291 | /* This condition should never happen */ | ||
4292 | ext_debug("Block lookup failed"); | ||
4293 | err = -EIO; | ||
4294 | break; | ||
4295 | } | ||
4296 | |||
4297 | iblock += num_blocks; | ||
4298 | } | ||
4299 | |||
4300 | if (blocks_released > 0) { | ||
4301 | ext4_ext_invalidate_cache(inode); | ||
4302 | ext4_discard_preallocations(inode); | ||
4303 | } | ||
4304 | |||
4305 | if (IS_SYNC(inode)) | ||
4306 | ext4_handle_sync(handle); | ||
4307 | |||
4308 | up_write(&EXT4_I(inode)->i_data_sem); | ||
4309 | |||
4310 | out: | ||
4311 | ext4_orphan_del(handle, inode); | ||
4312 | inode->i_mtime = inode->i_ctime = ext4_current_time(inode); | ||
4313 | ext4_mark_inode_dirty(handle, inode); | ||
4314 | ext4_journal_stop(handle); | ||
4315 | return err; | ||
4316 | } | ||
4103 | int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, | 4317 | int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, |
4104 | __u64 start, __u64 len) | 4318 | __u64 start, __u64 len) |
4105 | { | 4319 | { |
diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index fbe11e676624..65576c02c6f5 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c | |||
@@ -4419,6 +4419,31 @@ int ext4_can_truncate(struct inode *inode) | |||
4419 | } | 4419 | } |
4420 | 4420 | ||
4421 | /* | 4421 | /* |
4422 | * ext4_punch_hole: punches a hole in a file by releaseing the blocks | ||
4423 | * associated with the given offset and length | ||
4424 | * | ||
4425 | * @inode: File inode | ||
4426 | * @offset: The offset where the hole will begin | ||
4427 | * @len: The length of the hole | ||
4428 | * | ||
4429 | * Returns: 0 on sucess or negative on failure | ||
4430 | */ | ||
4431 | |||
4432 | int ext4_punch_hole(struct file *file, loff_t offset, loff_t length) | ||
4433 | { | ||
4434 | struct inode *inode = file->f_path.dentry->d_inode; | ||
4435 | if (!S_ISREG(inode->i_mode)) | ||
4436 | return -ENOTSUPP; | ||
4437 | |||
4438 | if (!ext4_test_inode_flag(inode, EXT4_INODE_EXTENTS)) { | ||
4439 | /* TODO: Add support for non extent hole punching */ | ||
4440 | return -ENOTSUPP; | ||
4441 | } | ||
4442 | |||
4443 | return ext4_ext_punch_hole(file, offset, length); | ||
4444 | } | ||
4445 | |||
4446 | /* | ||
4422 | * ext4_truncate() | 4447 | * ext4_truncate() |
4423 | * | 4448 | * |
4424 | * We block out ext4_get_block() block instantiations across the entire | 4449 | * We block out ext4_get_block() block instantiations across the entire |