Skip to content

Commit d583fb8

Browse files
achendertytso
authored andcommitted
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 <[email protected]> Signed-off-by: "Theodore Ts'o" <[email protected]>
1 parent 3084885 commit d583fb8

File tree

1 file changed

+141
-19
lines changed

1 file changed

+141
-19
lines changed

fs/ext4/extents.c

Lines changed: 141 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,13 @@
4646

4747
#include <trace/events/ext4.h>
4848

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+
4956
static int ext4_ext_truncate_extend_restart(handle_t *handle,
5057
struct inode *inode,
5158
int needed)
@@ -2203,8 +2210,16 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
22032210
ext4_free_blocks(handle, inode, NULL, start, num, flags);
22042211
} else if (from == le32_to_cpu(ex->ee_block)
22052212
&& to <= le32_to_cpu(ex->ee_block) + ee_len - 1) {
2206-
printk(KERN_INFO "strange request: removal %u-%u from %u:%u\n",
2207-
from, to, le32_to_cpu(ex->ee_block), ee_len);
2213+
/* head removal */
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+
22082223
} else {
22092224
printk(KERN_INFO "strange request: removal(2) "
22102225
"%u-%u from %u:%u\n",
@@ -2213,9 +2228,22 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
22132228
return 0;
22142229
}
22152230

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+
*/
22162243
static int
22172244
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)
22192247
{
22202248
int err = 0, correct_index = 0;
22212249
int depth = ext_depth(inode), credits;
@@ -2226,6 +2254,7 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
22262254
unsigned short ex_ee_len;
22272255
unsigned uninitialized = 0;
22282256
struct ext4_extent *ex;
2257+
struct ext4_map_blocks map;
22292258

22302259
/* the header must be checked already in ext4_ext_remove_space() */
22312260
ext_debug("truncate since %u in leaf\n", start);
@@ -2255,31 +2284,95 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
22552284
path[depth].p_ext = ex;
22562285

22572286
a = ex_ee_block > start ? ex_ee_block : start;
2258-
b = ex_ee_block + ex_ee_len - 1 < EXT_MAX_BLOCK ?
2259-
ex_ee_block + ex_ee_len - 1 : EXT_MAX_BLOCK;
2287+
b = ex_ee_block+ex_ee_len - 1 < end ?
2288+
ex_ee_block+ex_ee_len - 1 : end;
22602289

22612290
ext_debug(" border %u:%u\n", a, b);
22622291

2263-
if (a != ex_ee_block && b != ex_ee_block + ex_ee_len - 1) {
2264-
block = 0;
2265-
num = 0;
2266-
BUG();
2292+
/* If this extent is beyond the end of the hole, skip it */
2293+
if (end <= ex_ee_block) {
2294+
ex--;
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+
}
22672340
} else if (a != ex_ee_block) {
22682341
/* remove tail of the extent */
22692342
block = ex_ee_block;
22702343
num = a - block;
22712344
} else if (b != ex_ee_block + ex_ee_len - 1) {
22722345
/* remove head of the extent */
2273-
block = a;
2274-
num = b - a;
2275-
/* there is no "make a hole" API yet */
2276-
BUG();
2346+
block = b;
2347+
num = ex_ee_block + ex_ee_len - b;
2348+
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+
}
22772359
} else {
22782360
/* remove whole extent: excellent! */
22792361
block = ex_ee_block;
22802362
num = 0;
2281-
BUG_ON(a != ex_ee_block);
2282-
BUG_ON(b != ex_ee_block + ex_ee_len - 1);
2363+
if (a != ex_ee_block) {
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+
}
22832376
}
22842377

22852378
/*
@@ -2310,7 +2403,13 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
23102403
if (num == 0) {
23112404
/* this extent is removed; mark slot entirely unused */
23122405
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));
23142413
}
23152414

23162415
ex->ee_block = cpu_to_le32(block);
@@ -2326,6 +2425,27 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
23262425
if (err)
23272426
goto out;
23282427

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+
23292449
ext_debug("new extent: %u:%u:%llu\n", block, num,
23302450
ext4_ext_pblock(ex));
23312451
ex--;
@@ -2366,7 +2486,8 @@ ext4_ext_more_to_rm(struct ext4_ext_path *path)
23662486
return 1;
23672487
}
23682488

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)
23702491
{
23712492
struct super_block *sb = inode->i_sb;
23722493
int depth = ext_depth(inode);
@@ -2405,7 +2526,8 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
24052526
while (i >= 0 && err == 0) {
24062527
if (i == depth) {
24072528
/* 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);
24092531
/* root level has p_bh == NULL, brelse() eats this */
24102532
brelse(path[i].p_bh);
24112533
path[i].p_bh = NULL;
@@ -3451,7 +3573,7 @@ void ext4_ext_truncate(struct inode *inode)
34513573

34523574
last_block = (inode->i_size + sb->s_blocksize - 1)
34533575
>> 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);
34553577

34563578
/* In a multi-transaction truncate, we only make the final
34573579
* transaction synchronous.

0 commit comments

Comments
 (0)