aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTheodore Ts'o <tytso@mit.edu>2011-09-09 18:54:51 -0400
committerTheodore Ts'o <tytso@mit.edu>2011-09-09 18:54:51 -0400
commit0aa060000e83ca3d09ddc446a7174fb0820d99bc (patch)
treec80077aed7b1df852d556965e2b36d3c956cdabe
parent4d33b1ef10995d7ba6191d67456202c697a92a32 (diff)
ext4: teach ext4_ext_truncate() about the bigalloc feature
When we are truncating (as opposed unlinking) a file, we need to worry about partial truncates of a file, especially in the light of sparse files. The changes here make sure that arbitrary truncates of sparse files works correctly. Yeah, it's messy. Note that these functions will need to be revisted when the punch ioctl is integrated --- in fact this commit will probably have merge conflicts with the punch changes which Allison Henders and the IBM LTC have been working on. I will need to fix this up when either patch hits mainline. Signed-off-by: "Theodore Ts'o" <tytso@mit.edu>
-rw-r--r--fs/ext4/extents.c88
1 files changed, 76 insertions, 12 deletions
diff --git a/fs/ext4/extents.c b/fs/ext4/extents.c
index bd42ab29efec..cd4479c08031 100644
--- a/fs/ext4/extents.c
+++ b/fs/ext4/extents.c
@@ -2202,14 +2202,39 @@ int ext4_ext_index_trans_blocks(struct inode *inode, int nrblocks, int chunk)
2202} 2202}
2203 2203
2204static int ext4_remove_blocks(handle_t *handle, struct inode *inode, 2204static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
2205 struct ext4_extent *ex, 2205 struct ext4_extent *ex,
2206 ext4_lblk_t from, ext4_lblk_t to) 2206 ext4_fsblk_t *partial_cluster,
2207 ext4_lblk_t from, ext4_lblk_t to)
2207{ 2208{
2209 struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
2208 unsigned short ee_len = ext4_ext_get_actual_len(ex); 2210 unsigned short ee_len = ext4_ext_get_actual_len(ex);
2211 ext4_fsblk_t pblk;
2209 int flags = EXT4_FREE_BLOCKS_FORGET; 2212 int flags = EXT4_FREE_BLOCKS_FORGET;
2210 2213
2211 if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode)) 2214 if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
2212 flags |= EXT4_FREE_BLOCKS_METADATA; 2215 flags |= EXT4_FREE_BLOCKS_METADATA;
2216 /*
2217 * For bigalloc file systems, we never free a partial cluster
2218 * at the beginning of the extent. Instead, we make a note
2219 * that we tried freeing the cluster, and check to see if we
2220 * need to free it on a subsequent call to ext4_remove_blocks,
2221 * or at the end of the ext4_truncate() operation.
2222 */
2223 flags |= EXT4_FREE_BLOCKS_NOFREE_FIRST_CLUSTER;
2224
2225 /*
2226 * If we have a partial cluster, and it's different from the
2227 * cluster of the last block, we need to explicitly free the
2228 * partial cluster here.
2229 */
2230 pblk = ext4_ext_pblock(ex) + ee_len - 1;
2231 if (*partial_cluster && (EXT4_B2C(sbi, pblk) != *partial_cluster)) {
2232 ext4_free_blocks(handle, inode, NULL,
2233 EXT4_C2B(sbi, *partial_cluster),
2234 sbi->s_cluster_ratio, flags);
2235 *partial_cluster = 0;
2236 }
2237
2213#ifdef EXTENTS_STATS 2238#ifdef EXTENTS_STATS
2214 { 2239 {
2215 struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); 2240 struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
@@ -2229,12 +2254,24 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
2229 && to == le32_to_cpu(ex->ee_block) + ee_len - 1) { 2254 && to == le32_to_cpu(ex->ee_block) + ee_len - 1) {
2230 /* tail removal */ 2255 /* tail removal */
2231 ext4_lblk_t num; 2256 ext4_lblk_t num;
2232 ext4_fsblk_t start;
2233 2257
2234 num = le32_to_cpu(ex->ee_block) + ee_len - from; 2258 num = le32_to_cpu(ex->ee_block) + ee_len - from;
2235 start = ext4_ext_pblock(ex) + ee_len - num; 2259 pblk = ext4_ext_pblock(ex) + ee_len - num;
2236 ext_debug("free last %u blocks starting %llu\n", num, start); 2260 ext_debug("free last %u blocks starting %llu\n", num, pblk);
2237 ext4_free_blocks(handle, inode, NULL, start, num, flags); 2261 ext4_free_blocks(handle, inode, NULL, pblk, num, flags);
2262 /*
2263 * If the block range to be freed didn't start at the
2264 * beginning of a cluster, and we removed the entire
2265 * extent, save the partial cluster here, since we
2266 * might need to delete if we determine that the
2267 * truncate operation has removed all of the blocks in
2268 * the cluster.
2269 */
2270 if (pblk & (sbi->s_cluster_ratio - 1) &&
2271 (ee_len == num))
2272 *partial_cluster = EXT4_B2C(sbi, pblk);
2273 else
2274 *partial_cluster = 0;
2238 } else if (from == le32_to_cpu(ex->ee_block) 2275 } else if (from == le32_to_cpu(ex->ee_block)
2239 && to <= le32_to_cpu(ex->ee_block) + ee_len - 1) { 2276 && to <= le32_to_cpu(ex->ee_block) + ee_len - 1) {
2240 /* head removal */ 2277 /* head removal */
@@ -2269,9 +2306,10 @@ static int ext4_remove_blocks(handle_t *handle, struct inode *inode,
2269 */ 2306 */
2270static int 2307static int
2271ext4_ext_rm_leaf(handle_t *handle, struct inode *inode, 2308ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
2272 struct ext4_ext_path *path, ext4_lblk_t start, 2309 struct ext4_ext_path *path, ext4_fsblk_t *partial_cluster,
2273 ext4_lblk_t end) 2310 ext4_lblk_t start, ext4_lblk_t end)
2274{ 2311{
2312 struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb);
2275 int err = 0, correct_index = 0; 2313 int err = 0, correct_index = 0;
2276 int depth = ext_depth(inode), credits; 2314 int depth = ext_depth(inode), credits;
2277 struct ext4_extent_header *eh; 2315 struct ext4_extent_header *eh;
@@ -2423,7 +2461,8 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
2423 if (err) 2461 if (err)
2424 goto out; 2462 goto out;
2425 2463
2426 err = ext4_remove_blocks(handle, inode, ex, a, b); 2464 err = ext4_remove_blocks(handle, inode, ex, partial_cluster,
2465 a, b);
2427 if (err) 2466 if (err)
2428 goto out; 2467 goto out;
2429 2468
@@ -2471,7 +2510,8 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
2471 sizeof(struct ext4_extent)); 2510 sizeof(struct ext4_extent));
2472 } 2511 }
2473 le16_add_cpu(&eh->eh_entries, -1); 2512 le16_add_cpu(&eh->eh_entries, -1);
2474 } 2513 } else
2514 *partial_cluster = 0;
2475 2515
2476 ext_debug("new extent: %u:%u:%llu\n", block, num, 2516 ext_debug("new extent: %u:%u:%llu\n", block, num,
2477 ext4_ext_pblock(ex)); 2517 ext4_ext_pblock(ex));
@@ -2483,6 +2523,25 @@ ext4_ext_rm_leaf(handle_t *handle, struct inode *inode,
2483 if (correct_index && eh->eh_entries) 2523 if (correct_index && eh->eh_entries)
2484 err = ext4_ext_correct_indexes(handle, inode, path); 2524 err = ext4_ext_correct_indexes(handle, inode, path);
2485 2525
2526 /*
2527 * If there is still a entry in the leaf node, check to see if
2528 * it references the partial cluster. This is the only place
2529 * where it could; if it doesn't, we can free the cluster.
2530 */
2531 if (*partial_cluster && ex >= EXT_FIRST_EXTENT(eh) &&
2532 (EXT4_B2C(sbi, ext4_ext_pblock(ex) + ex_ee_len - 1) !=
2533 *partial_cluster)) {
2534 int flags = EXT4_FREE_BLOCKS_FORGET;
2535
2536 if (S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))
2537 flags |= EXT4_FREE_BLOCKS_METADATA;
2538
2539 ext4_free_blocks(handle, inode, NULL,
2540 EXT4_C2B(sbi, *partial_cluster),
2541 sbi->s_cluster_ratio, flags);
2542 *partial_cluster = 0;
2543 }
2544
2486 /* if this leaf is free, then we should 2545 /* if this leaf is free, then we should
2487 * remove it from index block above */ 2546 * remove it from index block above */
2488 if (err == 0 && eh->eh_entries == 0 && path[depth].p_bh != NULL) 2547 if (err == 0 && eh->eh_entries == 0 && path[depth].p_bh != NULL)
@@ -2518,6 +2577,7 @@ static int ext4_ext_remove_space(struct inode *inode, ext4_lblk_t start)
2518 struct super_block *sb = inode->i_sb; 2577 struct super_block *sb = inode->i_sb;
2519 int depth = ext_depth(inode); 2578 int depth = ext_depth(inode);
2520 struct ext4_ext_path *path; 2579 struct ext4_ext_path *path;
2580 ext4_fsblk_t partial_cluster = 0;
2521 handle_t *handle; 2581 handle_t *handle;
2522 int i, err; 2582 int i, err;
2523 2583
@@ -2553,7 +2613,8 @@ again:
2553 if (i == depth) { 2613 if (i == depth) {
2554 /* this is leaf block */ 2614 /* this is leaf block */
2555 err = ext4_ext_rm_leaf(handle, inode, path, 2615 err = ext4_ext_rm_leaf(handle, inode, path,
2556 start, EXT_MAX_BLOCKS - 1); 2616 &partial_cluster, start,
2617 EXT_MAX_BLOCKS - 1);
2557 /* root level has p_bh == NULL, brelse() eats this */ 2618 /* root level has p_bh == NULL, brelse() eats this */
2558 brelse(path[i].p_bh); 2619 brelse(path[i].p_bh);
2559 path[i].p_bh = NULL; 2620 path[i].p_bh = NULL;
@@ -3495,6 +3556,8 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
3495 ee_len = ext4_ext_get_actual_len(ex); 3556 ee_len = ext4_ext_get_actual_len(ex);
3496 /* if found extent covers block, simply return it */ 3557 /* if found extent covers block, simply return it */
3497 if (in_range(map->m_lblk, ee_block, ee_len)) { 3558 if (in_range(map->m_lblk, ee_block, ee_len)) {
3559 ext4_fsblk_t partial_cluster = 0;
3560
3498 newblock = map->m_lblk - ee_block + ee_start; 3561 newblock = map->m_lblk - ee_block + ee_start;
3499 /* number of remaining blocks in the extent */ 3562 /* number of remaining blocks in the extent */
3500 allocated = ee_len - (map->m_lblk - ee_block); 3563 allocated = ee_len - (map->m_lblk - ee_block);
@@ -3578,7 +3641,8 @@ int ext4_ext_map_blocks(handle_t *handle, struct inode *inode,
3578 ext4_ext_invalidate_cache(inode); 3641 ext4_ext_invalidate_cache(inode);
3579 3642
3580 err = ext4_ext_rm_leaf(handle, inode, path, 3643 err = ext4_ext_rm_leaf(handle, inode, path,
3581 map->m_lblk, map->m_lblk + punched_out); 3644 &partial_cluster, map->m_lblk,
3645 map->m_lblk + punched_out);
3582 3646
3583 if (!err && path->p_hdr->eh_entries == 0) { 3647 if (!err && path->p_hdr->eh_entries == 0) {
3584 /* 3648 /*