2007-06-21 01:55:11

by Takashi Sato

[permalink] [raw]
Subject: [RFC][PATCH 7/10] Reserve freed blocks

- Reserve the free blocks in the target area, not to be
used by other process.

Signed-off-by: Takashi Sato <[email protected]>
Signed-off-by: Akira Fujita <[email protected]>
---
diff -X Online-Defrag_linux-2.6.19-rc6-git/Documentation/dontdiff -upNr Online-Defrag_linux-2.6.19-rc6-git-RESERVE_BLOCK/fs/ext4/extents.c Online-Defrag_linux-2.6.19-rc6-git-BLOCK_RELEASE/fs/ext4/extents.c
--- Online-Defrag_linux-2.6.19-rc6-git-RESERVE_BLOCK/fs/ext4/extents.c 2007-06-19 21:40:55.000000000 +0900
+++ Online-Defrag_linux-2.6.19-rc6-git-BLOCK_RELEASE/fs/ext4/extents.c 2007-06-19 20:19:14.000000000 +0900
@@ -2619,6 +2619,182 @@ out:
}

/**
+ * ext4_ext_defrag_reserve - reserve blocks for defrag
+ * @inode target inode
+ * @goal block reservation goal
+ * @len blocks count to reserve
+ *
+ * This function returns 0 if succeeded, otherwise
+ * returns error value
+ */
+
+int ext4_ext_defrag_reserve(struct inode * inode, ext4_fsblk_t goal, int len)
+{
+ struct super_block *sb = NULL;
+ handle_t *handle = NULL;
+ struct buffer_head *bitmap_bh = NULL;
+ struct ext4_block_alloc_info *block_i;
+ struct ext4_reserve_window_node * my_rsv = NULL;
+ unsigned short windowsz = 0;
+ unsigned long group_no;
+ ext4_grpblk_t grp_target_blk;
+ int err = 0;
+
+ mutex_lock(&EXT4_I(inode)->truncate_mutex);
+
+ handle = ext4_journal_start(inode, EXT4_RESERVE_TRANS_BLOCKS);
+ if (IS_ERR(handle)) {
+ err = PTR_ERR(handle);
+ handle = NULL;
+ goto out;
+ }
+
+ if (S_ISREG(inode->i_mode) && (!EXT4_I(inode)->i_block_alloc_info)) {
+ ext4_init_block_alloc_info(inode);
+ } else if (!S_ISREG(inode->i_mode)) {
+ printk(KERN_ERR "ext4_ext_defrag_reserve:"
+ " incorrect file type\n");
+ err = -1;
+ goto out;
+ }
+
+ sb = inode->i_sb;
+ if (!sb) {
+ printk("ext4_ext_defrag_reserve: nonexistent device\n");
+ err = -ENXIO;
+ goto out;
+ }
+ ext4_get_group_no_and_offset(sb, goal, &group_no,
+ &grp_target_blk);
+
+ block_i = EXT4_I(inode)->i_block_alloc_info;
+
+ if (!block_i || ((windowsz =
+ block_i->rsv_window_node.rsv_goal_size) == 0)) {
+ printk("ex4_ext_defrag_reserve: unable to reserve\n");
+ err = -1;
+ goto out;
+ }
+
+ my_rsv = &block_i->rsv_window_node;
+
+ bitmap_bh = read_block_bitmap(sb, group_no);
+ if (!bitmap_bh) {
+ err = -ENOSPC;
+ goto out;
+ }
+
+ BUFFER_TRACE(bitmap_bh, "get undo access for new block");
+ err = ext4_journal_get_undo_access(handle, bitmap_bh);
+ if (err)
+ goto out;
+
+ err = alloc_new_reservation(my_rsv, grp_target_blk, sb,
+ group_no, bitmap_bh);
+ if (err < 0) {
+ printk(KERN_ERR "defrag: reservation faild\n");
+ ext4_discard_reservation(inode);
+ goto out;
+ } else {
+ if (len > EXT4_DEFAULT_RESERVE_BLOCKS) {
+ try_to_extend_reservation(my_rsv, sb,
+ len - EXT4_DEFAULT_RESERVE_BLOCKS);
+ }
+ }
+
+out:
+ mutex_unlock(&EXT4_I(inode)->truncate_mutex);
+ ext4_journal_release_buffer(handle, bitmap_bh);
+ brelse(bitmap_bh);
+
+ if (handle)
+ ext4_journal_stop(handle);
+
+ return err;
+}
+
+int goal_in_my_reservation(struct ext4_reserve_window *, ext4_grpblk_t,
+ unsigned int, struct super_block *);
+int rsv_is_empty(struct ext4_reserve_window *);
+
+/**
+ * ext4_ext_block_within_rsv - Is target extent reserved ?
+ * @ inode inode of target file
+ * @ ex_start start physical block number of the extent
+ * which already moved
+ * @ ex_len block length of the extent which already moved
+ *
+ * This function returns 0 if succeeded, otherwise
+ * returns error value
+ */
+static int ext4_ext_block_within_rsv(struct inode *inode,
+ ext4_fsblk_t ex_start, int ex_len)
+{
+ struct super_block *sb = inode->i_sb;
+ struct ext4_block_alloc_info *block_i;
+ unsigned long group_no;
+ ext4_grpblk_t grp_blk;
+ struct ext4_reserve_window_node *rsv;
+
+ block_i = EXT4_I(inode)->i_block_alloc_info;
+ if (block_i && block_i->rsv_window_node.rsv_goal_size > 0) {
+ rsv = &block_i->rsv_window_node;
+ if (rsv_is_empty(&rsv->rsv_window)) {
+ printk("defrag: Can't defrag due to"
+ " the empty reservation\n");
+ return -1;
+ }
+ } else {
+ printk("defrag: No i_block_alloc_info\n");
+ return -1;
+ }
+
+ ext4_get_group_no_and_offset(sb, ex_start, &group_no, &grp_blk);
+
+ if (goal_in_my_reservation(&rsv->rsv_window, grp_blk, group_no, sb)
+ && goal_in_my_reservation(&rsv->rsv_window, grp_blk + ex_len -1,
+ group_no, sb))
+ return 0;
+ return -1;
+}
+
+/*
+ * ext4_ext_fblocks_reserve() -
+ * reserve free blocks by ext4_ext_defrag_reserve()
+ * @inode: To get a block group number
+ * @ext_info: freeblocks distribution which stored extent-like style
+ * @ext_info->ext[] an array of struct ext4_extents_data
+ */
+int ext4_ext_fblocks_reserve(struct inode *inode,
+ struct ext4_extents_info *ext_info)
+{
+ ext4_fsblk_t ex_start = 0;
+ int i;
+ int ret = 0;
+ int len = 0;
+
+ for (i = 0; i < ext_info->entries; i++) {
+ ex_start = ext_info->ext[i].start;
+ len = ext_info->ext[i].len;
+
+ ret = ext4_ext_defrag_reserve(inode, ex_start, len);
+ if (ret < 0)
+ goto ERR;
+
+ ret = ext4_ext_block_within_rsv(inode, ex_start, len);
+ if (ret < 0)
+ goto ERR;
+ }
+ return ret;
+
+ERR:
+ mutex_lock(&EXT4_I(inode)->truncate_mutex);
+ ext4_discard_reservation(inode);
+ mutex_unlock(&EXT4_I(inode)->truncate_mutex);
+ return ret;
+}
+
+/**
* ext4_ext_defrag_victim - Create free space for defrag
* @filp target file
* @ex_info target extents array to move
@@ -2871,6 +3047,15 @@ int ext4_ext_ioctl(struct inode *inode,
&ext_info, sizeof(ext_info)))
return -EFAULT;
}
+ } else if (cmd == EXT4_IOC_RESERVE_BLOCK) {
+ struct ext4_extents_info ext_info;
+
+ if (copy_from_user(&ext_info,
+ (struct ext4_extents_info __user *)arg,
+ sizeof(ext_info)))
+ return -EFAULT;
+
+ err = ext4_ext_fblocks_reserve(inode, &ext_info);
} else if (cmd == EXT4_IOC_MOVE_VICTIM) {
struct ext4_extents_info ext_info;