@@ -1731,9 +1731,13 @@ extern int ext4_mb_reserve_blocks(struct super_block *, int);
extern void ext4_discard_preallocations(struct inode *);
extern int __init ext4_init_mballoc(void);
extern void ext4_exit_mballoc(void);
-extern void ext4_free_blocks(handle_t *handle, struct inode *inode,
- struct buffer_head *bh, ext4_fsblk_t block,
- unsigned long count, int flags);
+extern void __ext4_free_blocks(const char *where, unsigned int line,
+ handle_t *handle, struct inode *inode,
+ struct buffer_head *bh, ext4_fsblk_t block,
+ unsigned long count, int flags);
+#define ext4_free_blocks(handle, inode, bh, block, count, flags) \
+ __ext4_free_blocks(__func__, __LINE__ , (handle), (inode), (bh), \
+ (block), (count), (flags))
extern int ext4_mb_add_groupinfo(struct super_block *sb,
ext4_group_t i, struct ext4_group_desc *desc);
extern int ext4_trim_fs(struct super_block *, struct fstrim_range *);
@@ -4451,9 +4451,9 @@ ext4_mb_free_metadata(handle_t *handle, struct ext4_buddy *e4b,
* @count: number of blocks to count
* @metadata: Are these metadata blocks
*/
-void ext4_free_blocks(handle_t *handle, struct inode *inode,
- struct buffer_head *bh, ext4_fsblk_t block,
- unsigned long count, int flags)
+void __ext4_free_blocks(const char *where, unsigned int line, handle_t *handle,
+ struct inode *inode, struct buffer_head *bh,
+ ext4_fsblk_t block, unsigned long count, int flags)
{
struct buffer_head *bitmap_bh = NULL;
struct super_block *sb = inode->i_sb;
@@ -4467,6 +4467,7 @@ void ext4_free_blocks(handle_t *handle, struct inode *inode,
struct ext4_buddy e4b;
int err = 0;
int ret;
+ int maxblocks;
if (bh) {
if (block)
@@ -4549,6 +4550,29 @@ do_more:
goto error_return;
}
+ maxblocks = count;
+ ret = ext4_snapshot_get_delete_access(handle, inode,
+ block, &maxblocks);
+ if (ret < 0) {
+ ext4_journal_abort_handle(where, line, __func__,
+ NULL, handle, ret);
+ err = ret;
+ goto error_return;
+ }
+ if (ret > 0) {
+ /* 'ret' blocks were moved to snapshot - skip them */
+ block += maxblocks;
+ count -= maxblocks;
+ count += overflow;
+ cond_resched();
+ if (count > 0)
+ goto do_more;
+ /* no more blocks to free/move to snapshot */
+ ext4_mark_super_dirty(sb);
+ goto error_return;
+ }
+ overflow += count - maxblocks;
+ count = maxblocks;
BUFFER_TRACE(bitmap_bh, "getting write access");
err = ext4_handle_get_bitmap_access(handle, sb, block_group, bitmap_bh);
if (err)
@@ -227,6 +227,32 @@ static inline int ext4_snapshot_get_bitmap_access(handle_t *handle,
return ext4_snapshot_cow(handle, NULL, bh->b_blocknr, bh, 1);
}
+/*
+ * get_delete_access() - move blocks to snapshot or approve to free them
+ * @handle: JBD handle
+ * @inode: owner of blocks if known (or NULL otherwise)
+ * @block: address of start @block
+ * @pcount: pointer to no. of blocks about to move or approve
+ *
+ * Called from ext4_free_blocks() before deleting blocks with
+ * i_data_sem held
+ *
+ * Return values:
+ * > 0 - blocks were moved to snapshot and may not be freed
+ * = 0 - blocks may be freed
+ * < 0 - error
+ */
+static inline int ext4_snapshot_get_delete_access(handle_t *handle,
+ struct inode *inode, ext4_fsblk_t block, int *pcount)
+{
+ struct super_block *sb;
+
+ sb = handle->h_transaction->t_journal->j_private;
+ if (!EXT4_SNAPSHOTS(sb))
+ return 0;
+
+ return ext4_snapshot_move(handle, inode, block, pcount, 1);
+}
/* snapshot_ctl.c */