diff options
author | Allison Henderson <achender@linux.vnet.ibm.com> | 2011-05-25 07:41:43 -0400 |
---|---|---|
committer | Theodore Ts'o <tytso@mit.edu> | 2011-05-25 07:41:43 -0400 |
commit | d583fb87a3ff0ca50befd2f73f7a67fade1c8c56 (patch) | |
tree | 4e8cd6c05390d5355906d5657b87d82fcd4ea750 /fs | |
parent | 308488518dfcbe3be250085cd582f5b0c1ce72a9 (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')
-rw-r--r-- | fs/ext4/extents.c | 160 |
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 | ||
49 | static 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 | |||
49 | static int ext4_ext_truncate_extend_restart(handle_t *handle, | 56 | static 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 | */ | ||
2216 | static int | 2243 | static int |
2217 | ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, | 2244 | ext4_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 | ||
2369 | static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start) | 2489 | static 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. |