aboutsummaryrefslogtreecommitdiffstats
path: root/fs/ext4
diff options
context:
space:
mode:
authorAllison Henderson <achender@linux.vnet.ibm.com>2011-05-25 07:41:43 -0400
committerTheodore Ts'o <tytso@mit.edu>2011-05-25 07:41:43 -0400
commitd583fb87a3ff0ca50befd2f73f7a67fade1c8c56 (patch)
tree4e8cd6c05390d5355906d5657b87d82fcd4ea750 /fs/ext4
parent308488518dfcbe3be250085cd582f5b0c1ce72a9 (diff)
ext4: punch out extents
This patch modifies the truncate routines to support hole punching Below is a brief summary of the patches changes: - Added end param to ext_ext4_rm_leaf This function has been modified to accept an end parameter which enables it to punch holes in leafs instead of just truncating them. - Implemented the "remove head" case in the ext_remove_blocks routine This routine is used by ext_ext4_rm_leaf to remove the tail of an extent during a truncate. The new ext_ext4_rm_leaf routine will now also use it to remove the head of an extent in the case that the hole covers a region of blocks at the beginning of an extent. - Added "end" param to ext4_ext_remove_space routine This function has been modified to accept a stop parameter, which is passed through to ext4_ext_rm_leaf. [ext4 punch hole patch series 3/5 v6] Signed-off-by: Allison Henderson <achender@us.ibm.com> Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
Diffstat (limited to 'fs/ext4')
-rw-r--r--fs/ext4/extents.c160
1 files changed, 141 insertions, 19 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index aa3a2601bada..c8c687b5d9a8 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -46,6 +46,13 @@
46 46
47#include <trace/events/ext4.h> 47#include <trace/events/ext4.h>
48 48
49static int ext4_split_extent(handle_t *handle,
50 struct inode *inode,
51 struct ext4_ext_path *path,
52 struct ext4_map_blocks *map,
53 int split_flag,
54 int flags);
55
49static int ext4_ext_truncate_extend_restart(handle_t *handle, 56static int ext4_ext_truncate_extend_restart(handle_t *handle,
50 struct inode *inode, 57 struct inode *inode,
51 int needed) 58 int needed)
@@ -2203,8 +2210,16 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
2203 ext4_free_blocks(handle, inode, NULL, start, num, flags); 2210 ext4_free_blocks(handle, inode, NULL, start, num, flags);
2204 } else if (from == le32_to_cpu(ex->ee_block) 2211 } else if (from == le32_to_cpu(ex->ee_block)
2205 && to <= le32_to_cpu(ex->ee_block) + ee_len - 1) { 2212 && to <= le32_to_cpu(ex->ee_block) + ee_len - 1) {
2206 printk(KERN_INFO "strange request: removal %u-%u from %u:%u\n", 2213 /* head removal */
2207 from, to, le32_to_cpu(ex->ee_block), ee_len); 2214 ext4_lblk_t num;
2215 ext4_fsblk_t start;
2216
2217 num = to - from;
2218 start = ext4_ext_pblock(ex);
2219
2220 ext_debug("free first %u blocks starting %llu\n", num, start);
2221 ext4_free_blocks(handle, inode, 0, start, num, flags);
2222
2208 } else { 2223 } else {
2209 printk(KERN_INFO "strange request: removal(2) " 2224 printk(KERN_INFO "strange request: removal(2) "
2210 "%u-%u from %u:%u\n", 2225 "%u-%u from %u:%u\n",
@@ -2213,9 +2228,22 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
2213 return 0; 2228 return 0;
2214} 2229}
2215 2230
2231
2232/*
2233 * ext4_ext_rm_leaf() Removes the extents associated with the
2234 * blocks appearing between "start" and "end", and splits the extents
2235 * if "start" and "end" appear in the same extent
2236 *
2237 * @handle: The journal handle
2238 * @inode: The files inode
2239 * @path: The path to the leaf
2240 * @start: The first block to remove
2241 * @end: The last block to remove
2242 */
2216static int 2243static int
2217ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, 2244ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
2218 struct ext4_ext_path *path, ext4_lblk_t start) 2245 struct ext4_ext_path *path, ext4_lblk_t start,
2246 ext4_lblk_t end)
2219{ 2247{
2220 int err = 0, correct_index = 0; 2248 int err = 0, correct_index = 0;
2221 int depth = ext_depth(inode), credits; 2249 int depth = ext_depth(inode), credits;
@@ -2226,6 +2254,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
2226 unsigned short ex_ee_len; 2254 unsigned short ex_ee_len;
2227 unsigned uninitialized = 0; 2255 unsigned uninitialized = 0;
2228 struct ext4_extent *ex; 2256 struct ext4_extent *ex;
2257 struct ext4_map_blocks map;
2229 2258
2230 /* the header must be checked already in ext4_ext_remove_space() */ 2259 /* the header must be checked already in ext4_ext_remove_space() */
2231 ext_debug("truncate since %u in leaf\n", start); 2260 ext_debug("truncate since %u in leaf\n", start);
@@ -2255,31 +2284,95 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
2255 path[depth].p_ext = ex; 2284 path[depth].p_ext = ex;
2256 2285
2257 a = ex_ee_block > start ? ex_ee_block : start; 2286 a = ex_ee_block > start ? ex_ee_block : start;
2258 b = ex_ee_block + ex_ee_len - 1 < EXT_MAX_BLOCK ? 2287 b = ex_ee_block+ex_ee_len - 1 < end ?
2259 ex_ee_block + ex_ee_len - 1 : EXT_MAX_BLOCK; 2288 ex_ee_block+ex_ee_len - 1 : end;
2260 2289
2261 ext_debug(" border %u:%u\n", a, b); 2290 ext_debug(" border %u:%u\n", a, b);
2262 2291
2263 if (a != ex_ee_block && b != ex_ee_block + ex_ee_len - 1) { 2292 /* If this extent is beyond the end of the hole, skip it */
2264 block = 0; 2293 if (end <= ex_ee_block) {
2265 num = 0; 2294 ex--;
2266 BUG(); 2295 ex_ee_block = le32_to_cpu(ex->ee_block);
2296 ex_ee_len = ext4_ext_get_actual_len(ex);
2297 continue;
2298 } else if (a != ex_ee_block &&
2299 b != ex_ee_block + ex_ee_len - 1) {
2300 /*
2301 * If this is a truncate, then this condition should
2302 * never happen because at least one of the end points
2303 * needs to be on the edge of the extent.
2304 */
2305 if (end == EXT_MAX_BLOCK) {
2306 ext_debug(" bad truncate %u:%u\n",
2307 start, end);
2308 block = 0;
2309 num = 0;
2310 err = -EIO;
2311 goto out;
2312 }
2313 /*
2314 * else this is a hole punch, so the extent needs to
2315 * be split since neither edge of the hole is on the
2316 * extent edge
2317 */
2318 else{
2319 map.m_pblk = ext4_ext_pblock(ex);
2320 map.m_lblk = ex_ee_block;
2321 map.m_len = b - ex_ee_block;
2322
2323 err = ext4_split_extent(handle,
2324 inode, path, &map, 0,
2325 EXT4_GET_BLOCKS_PUNCH_OUT_EXT |
2326 EXT4_GET_BLOCKS_PRE_IO);
2327
2328 if (err < 0)
2329 goto out;
2330
2331 ex_ee_len = ext4_ext_get_actual_len(ex);
2332
2333 b = ex_ee_block+ex_ee_len - 1 < end ?
2334 ex_ee_block+ex_ee_len - 1 : end;
2335
2336 /* Then remove tail of this extent */
2337 block = ex_ee_block;
2338 num = a - block;
2339 }
2267 } else if (a != ex_ee_block) { 2340 } else if (a != ex_ee_block) {
2268 /* remove tail of the extent */ 2341 /* remove tail of the extent */
2269 block = ex_ee_block; 2342 block = ex_ee_block;
2270 num = a - block; 2343 num = a - block;
2271 } else if (b != ex_ee_block + ex_ee_len - 1) { 2344 } else if (b != ex_ee_block + ex_ee_len - 1) {
2272 /* remove head of the extent */ 2345 /* remove head of the extent */
2273 block = a; 2346 block = b;
2274 num = b - a; 2347 num = ex_ee_block + ex_ee_len - b;
2275 /* there is no "make a hole" API yet */ 2348
2276 BUG(); 2349 /*
2350 * If this is a truncate, this condition
2351 * should never happen
2352 */
2353 if (end == EXT_MAX_BLOCK) {
2354 ext_debug(" bad truncate %u:%u\n",
2355 start, end);
2356 err = -EIO;
2357 goto out;
2358 }
2277 } else { 2359 } else {
2278 /* remove whole extent: excellent! */ 2360 /* remove whole extent: excellent! */
2279 block = ex_ee_block; 2361 block = ex_ee_block;
2280 num = 0; 2362 num = 0;
2281 BUG_ON(a != ex_ee_block); 2363 if (a != ex_ee_block) {
2282 BUG_ON(b != ex_ee_block + ex_ee_len - 1); 2364 ext_debug(" bad truncate %u:%u\n",
2365 start, end);
2366 err = -EIO;
2367 goto out;
2368 }
2369
2370 if (b != ex_ee_block + ex_ee_len - 1) {
2371 ext_debug(" bad truncate %u:%u\n",
2372 start, end);
2373 err = -EIO;
2374 goto out;
2375 }
2283 } 2376 }
2284 2377
2285 /* 2378 /*
@@ -2310,7 +2403,13 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
2310 if (num == 0) { 2403 if (num == 0) {
2311 /* this extent is removed; mark slot entirely unused */ 2404 /* this extent is removed; mark slot entirely unused */
2312 ext4_ext_store_pblock(ex, 0); 2405 ext4_ext_store_pblock(ex, 0);
2313 le16_add_cpu(&eh->eh_entries, -1); 2406 } else if (block != ex_ee_block) {
2407 /*
2408 * If this was a head removal, then we need to update
2409 * the physical block since it is now at a different
2410 * location
2411 */
2412 ext4_ext_store_pblock(ex, ext4_ext_pblock(ex) + (b-a));
2314 } 2413 }
2315 2414
2316 ex->ee_block = cpu_to_le32(block); 2415 ex->ee_block = cpu_to_le32(block);
@@ -2326,6 +2425,27 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
2326 if (err) 2425 if (err)
2327 goto out; 2426 goto out;
2328 2427
2428 /*
2429 * If the extent was completely released,
2430 * we need to remove it from the leaf
2431 */
2432 if (num == 0) {
2433 if (end != EXT_MAX_BLOCK) {
2434 /*
2435 * For hole punching, we need to scoot all the
2436 * extents up when an extent is removed so that
2437 * we dont have blank extents in the middle
2438 */
2439 memmove(ex, ex+1, (EXT_LAST_EXTENT(eh) - ex) *
2440 sizeof(struct ext4_extent));
2441
2442 /* Now get rid of the one at the end */
2443 memset(EXT_LAST_EXTENT(eh), 0,
2444 sizeof(struct ext4_extent));
2445 }
2446 le16_add_cpu(&eh->eh_entries, -1);
2447 }
2448
2329 ext_debug("new extent: %u:%u:%llu\n", block, num, 2449 ext_debug("new extent: %u:%u:%llu\n", block, num,
2330 ext4_ext_pblock(ex)); 2450 ext4_ext_pblock(ex));
2331 ex--; 2451 ex--;
@@ -2366,7 +2486,8 @@ ext4_ext_more_to_rm(struct ext4_ext_path *path)
2366 return 1; 2486 return 1;
2367} 2487}
2368 2488
2369static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start) 2489static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start,
2490 ext4_lblk_t end)
2370{ 2491{
2371 struct super_block *sb = inode->i_sb; 2492 struct super_block *sb = inode->i_sb;
2372 int depth = ext_depth(inode); 2493 int depth = ext_depth(inode);
@@ -2405,7 +2526,8 @@ again:
2405 while (i >= 0 && err == 0) { 2526 while (i >= 0 && err == 0) {
2406 if (i == depth) { 2527 if (i == depth) {
2407 /* this is leaf block */ 2528 /* this is leaf block */
2408 err = ext4_ext_rm_leaf(handle, inode, path, start); 2529 err = ext4_ext_rm_leaf(handle, inode, path,
2530 start, end);
2409 /* root level has p_bh == NULL, brelse() eats this */ 2531 /* root level has p_bh == NULL, brelse() eats this */
2410 brelse(path[i].p_bh); 2532 brelse(path[i].p_bh);
2411 path[i].p_bh = NULL; 2533 path[i].p_bh = NULL;
@@ -3451,7 +3573,7 @@ void ext4_ext_truncate(struct inode *inode)
3451 3573
3452 last_block = (inode->i_size + sb->s_blocksize - 1) 3574 last_block = (inode->i_size + sb->s_blocksize - 1)
3453 >> EXT4_BLOCK_SIZE_BITS(sb); 3575 >> EXT4_BLOCK_SIZE_BITS(sb);
3454 err = ext4_ext_remove_space(inode, last_block); 3576 err = ext4_ext_remove_space(inode, last_block, EXT_MAX_BLOCK);
3455 3577
3456 /* In a multi-transaction truncate, we only make the final 3578 /* In a multi-transaction truncate, we only make the final
3457 * transaction synchronous. 3579 * transaction synchronous.