aboutsummaryrefslogtreecommitdiffstats
path: root/fs
diff options
context:
space:
mode:
Diffstat (limited to 'fs')
-rw-r--r--fs/ext4/ext4.h3
-rw-r--r--fs/ext4/extents.c236
-rw-r--r--fs/ext4/inode.c25
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);
1816extern int ext4_get_inode_loc(struct inode *, struct ext4_iloc *); 1816extern int ext4_get_inode_loc(struct inode *, struct ext4_iloc *);
1817extern int ext4_can_truncate(struct inode *inode); 1817extern int ext4_can_truncate(struct inode *inode);
1818extern void ext4_truncate(struct inode *); 1818extern void ext4_truncate(struct inode *);
1819extern int ext4_punch_hole(struct file *file, loff_t offset, loff_t length);
1819extern int ext4_truncate_restart_trans(handle_t *, struct inode *, int nblocks); 1820extern int ext4_truncate_restart_trans(handle_t *, struct inode *, int nblocks);
1820extern void ext4_set_inode_flags(struct inode *); 1821extern void ext4_set_inode_flags(struct inode *);
1821extern void ext4_get_inode_flags(struct ext4_inode_info *); 1822extern 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,
2157extern int ext4_ext_map_blocks(handle_t *handle, struct inode *inode, 2158extern 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);
2159extern void ext4_ext_truncate(struct inode *); 2160extern void ext4_ext_truncate(struct inode *);
2161extern int ext4_ext_punch_hole(struct file *file, loff_t offset,
2162 loff_t length);
2160extern void ext4_ext_init(struct super_block *); 2163extern void ext4_ext_init(struct super_block *);
2161extern void ext4_ext_release(struct super_block *); 2164extern void ext4_ext_release(struct super_block *);
2162extern long ext4_fallocate(struct file *file, int mode, loff_t offset, 2165extern 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 */
2049static int 2062static int ext4_ext_check_cache(struct inode *inode, ext4_lblk_t block,
2050ext4_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 */
2108static int
2109ext4_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 */
4158int 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
4310out:
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}
4103int ext4_fiemap(struct inode *inode, struct fiemap_extent_info *fieinfo, 4317int 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
4432int 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