From: Amir Goldstein <amir73il@xxxxxxxxxxxx> Free blocks of deleted snapshots, which are not in use by an older non-deleted snapshot. Shrinking helps reclaiming disk space while older snapshots are currently in use (enabled). We modify the indirect inode truncate helper functions so that they can be used by the snapshot cleanup functions to free blocks selectively according to a COW bitmap buffer. Signed-off-by: Amir Goldstein <amir73il@xxxxxxxxxxxx> Signed-off-by: Yongqiang Yang <xiaoqiangnk@xxxxxxxxx> --- fs/ext4/ext4.h | 10 ++++++++++ fs/ext4/inode.c | 51 +++++++++++++++++++++++++++++++++++++-------------- 2 files changed, 47 insertions(+), 14 deletions(-) diff --git a/fs/ext4/ext4.h b/fs/ext4/ext4.h index 7650515..07629ce 100644 --- a/fs/ext4/ext4.h +++ b/fs/ext4/ext4.h @@ -1905,6 +1905,16 @@ extern void ext4_free_branches(handle_t *handle, struct inode *inode, struct buffer_head *parent_bh, __le32 *first, __le32 *last, int depth); +extern void ext4_free_data_cow(handle_t *handle, struct inode *inode, + struct buffer_head *this_bh, + __le32 *first, __le32 *last, + const char *bitmap, int bit, + int *pfreed_blocks); + +#define ext4_free_data(handle, inode, bh, first, last) \ + ext4_free_data_cow(handle, inode, bh, first, last, \ + NULL, 0, NULL) + /* ioctl.c */ extern long ext4_ioctl(struct file *, unsigned int, unsigned long); extern long ext4_compat_ioctl(struct file *, unsigned int, unsigned long); diff --git a/fs/ext4/inode.c b/fs/ext4/inode.c index d46da6a..e3bfee2 100644 --- a/fs/ext4/inode.c +++ b/fs/ext4/inode.c @@ -4546,11 +4546,15 @@ no_top: * Return 0 on success, 1 on invalid block range * and < 0 on fatal error. */ -static int ext4_clear_blocks(handle_t *handle, struct inode *inode, - struct buffer_head *bh, - ext4_fsblk_t block_to_free, - unsigned long count, __le32 *first, - __le32 *last) +/* + * ext4_clear_blocks_cow - Zero a number of block pointers (consult COW bitmap) + * @bitmap: COW bitmap to consult when shrinking deleted snapshot + * @bit: bit number representing the @first block + */ +static int ext4_clear_blocks_cow(handle_t *handle, struct inode *inode, + struct buffer_head *bh, ext4_fsblk_t block_to_free, + unsigned long count, __le32 *first, __le32 *last, + const char *bitmap, int bit) { __le32 *p; int flags = EXT4_FREE_BLOCKS_FORGET | EXT4_FREE_BLOCKS_VALIDATED; @@ -4590,8 +4594,12 @@ static int ext4_clear_blocks(handle_t *handle, struct inode *inode, } } - for (p = first; p < last; p++) + for (p = first; p < last; p++) { + if (*p && bitmap && ext4_test_bit(bit + (p - first), bitmap)) + /* don't free block used by older snapshot */ + continue; *p = 0; + } ext4_free_blocks(handle, inode, NULL, block_to_free, count, flags); return 0; @@ -4619,9 +4627,17 @@ out_err: * @this_bh will be %NULL if @first and @last point into the inode's direct * block pointers. */ -static void ext4_free_data(handle_t *handle, struct inode *inode, +/* + * ext4_free_data_cow - free a list of data blocks (consult COW bitmap) + * @bitmap: COW bitmap to consult when shrinking deleted snapshot + * @bit: bit number representing the @first block + * @pfreed_blocks: return number of freed blocks + */ +void ext4_free_data_cow(handle_t *handle, struct inode *inode, struct buffer_head *this_bh, - __le32 *first, __le32 *last) + __le32 *first, __le32 *last, + const char *bitmap, int bit, + int *pfreed_blocks) { ext4_fsblk_t block_to_free = 0; /* Starting block # of a run */ unsigned long count = 0; /* Number of blocks in the run */ @@ -4645,6 +4661,11 @@ static void ext4_free_data(handle_t *handle, struct inode *inode, for (p = first; p < last; p++) { nr = le32_to_cpu(*p); + if (nr && bitmap && ext4_test_bit(bit + (p - first), bitmap)) + /* don't free block used by older snapshot */ + nr = 0; + if (nr && pfreed_blocks) + ++(*pfreed_blocks); if (nr) { /* accumulate blocks to free if they're contiguous */ if (count == 0) { @@ -4654,9 +4675,10 @@ static void ext4_free_data(handle_t *handle, struct inode *inode, } else if (nr == block_to_free + count) { count++; } else { - err = ext4_clear_blocks(handle, inode, this_bh, - block_to_free, count, - block_to_free_p, p); + err = ext4_clear_blocks_cow(handle, inode, + this_bh, block_to_free, count, + block_to_free_p, p, bitmap, + bit + (block_to_free_p - first)); if (err) break; block_to_free = nr; @@ -4666,9 +4688,10 @@ static void ext4_free_data(handle_t *handle, struct inode *inode, } } - if (!err && count > 0) - err = ext4_clear_blocks(handle, inode, this_bh, block_to_free, - count, block_to_free_p, p); + if (count > 0) + err = ext4_clear_blocks_cow(handle, inode, this_bh, + block_to_free, count, block_to_free_p, p, + bitmap, bit + (block_to_free_p - first)); if (err < 0) /* fatal error */ return; -- 1.7.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-ext4" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html