Move ext4-use-percpu-data-for-lg_prealloc_list to stable series
[ext4-patch-queue.git] / ext4-online-defrag-exchange-blocks-between-two-inodes.patch
blob287a2371aab34f277093473c68778da3027ddf08
1 ext4: online defrag-- Exchange the blocks between two inodes
3 From: Akira Fujita <a-fujita@rs.jp.nec.com>
5 Exchange the data blocks between the temporary inode and
6 the original inode.
8 Signed-off-by: Akira Fujita <a-fujita@rs.jp.nec.com>
9 Signed-off-by: Takashi Sato <t-sato@yk.jp.nec.com>
10 ---
11 fs/ext4/defrag.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
12 1 file changed, 240 insertions(+)
14 Index: linux-2.6.26-rc9/fs/ext4/defrag.c
15 ===================================================================
16 --- linux-2.6.26-rc9.orig/fs/ext4/defrag.c 2008-07-11 16:05:18.000000000 -0700
17 +++ linux-2.6.26-rc9/fs/ext4/defrag.c 2008-07-11 16:05:19.000000000 -0700
18 @@ -134,6 +134,188 @@ int ext4_defrag_ioctl(struct inode *inod
21 /**
22 + * ext4_defrag_merge_across_blocks - Merge extents across leaf block
23 + *
24 + * @handle: journal handle
25 + * @org_inode: original inode
26 + * @o_start: first original extent to be defraged
27 + * @o_end: last original extent to be defraged
28 + * @start_ext: first new extent to be merged
29 + * @new_ext: middle of new extent to be merged
30 + * @end_ext: last new extent to be merged
31 + *
32 + * This function returns 0 if succeed, otherwise returns error value.
33 + */
34 +static int
35 +ext4_defrag_merge_across_blocks(handle_t *handle, struct inode *org_inode,
36 + struct ext4_extent *o_start, struct ext4_extent *o_end,
37 + struct ext4_extent *start_ext, struct ext4_extent *new_ext,
38 + struct ext4_extent *end_ext)
40 + struct ext4_ext_path *org_path = NULL;
41 + ext4_lblk_t eblock = 0;
42 + int new_flag = 0;
43 + int end_flag = 0;
44 + int err;
46 + if (le16_to_cpu(start_ext->ee_len) &&
47 + le16_to_cpu(new_ext->ee_len) &&
48 + le16_to_cpu(end_ext->ee_len)) {
50 + if (o_start == o_end) {
52 + /* start_ext new_ext end_ext
53 + * dest |---------|-----------|--------|
54 + * org |------------------------------|
55 + */
57 + end_flag = 1;
58 + } else {
60 + /* start_ext new_ext end_ext
61 + * dest |---------|----------|---------|
62 + * org |---------------|--------------|
63 + */
65 + o_end->ee_block = end_ext->ee_block;
66 + o_end->ee_len = end_ext->ee_len;
67 + ext4_ext_store_pblock(o_end, ext_pblock(end_ext));
68 + }
70 + o_start->ee_len = start_ext->ee_len;
71 + new_flag = 1;
73 + } else if (le16_to_cpu(start_ext->ee_len) &&
74 + le16_to_cpu(new_ext->ee_len) &&
75 + !le16_to_cpu(end_ext->ee_len) &&
76 + o_start == o_end) {
78 + /* start_ext new_ext
79 + * dest |--------------|---------------|
80 + * org |------------------------------|
81 + */
83 + o_start->ee_len = start_ext->ee_len;
84 + new_flag = 1;
86 + } else if (!le16_to_cpu(start_ext->ee_len) &&
87 + le16_to_cpu(new_ext->ee_len) &&
88 + le16_to_cpu(end_ext->ee_len) &&
89 + o_start == o_end) {
91 + /* new_ext end_ext
92 + * dest |--------------|---------------|
93 + * org |------------------------------|
94 + */
96 + o_end->ee_block = end_ext->ee_block;
97 + o_end->ee_len = end_ext->ee_len;
98 + ext4_ext_store_pblock(o_end, ext_pblock(end_ext));
100 + /*
101 + * Set 0 to the extent block if new_ext was
102 + * the first block.
103 + */
104 + if (!new_ext->ee_block)
105 + eblock = 0;
106 + else
107 + eblock = le32_to_cpu(new_ext->ee_block);
109 + new_flag = 1;
110 + } else {
111 + printk(KERN_ERR "ext4 defrag: Unexpected merge case\n");
112 + return -EIO;
115 + if (new_flag) {
116 + org_path = ext4_ext_find_extent(org_inode, eblock, NULL);
117 + if (IS_ERR(org_path)) {
118 + err = PTR_ERR(org_path);
119 + org_path = NULL;
120 + goto out;
122 + err = ext4_ext_insert_extent(handle, org_inode,
123 + org_path, new_ext);
124 + if (err)
125 + goto out;
128 + if (end_flag) {
129 + org_path = ext4_ext_find_extent(org_inode,
130 + le32_to_cpu(end_ext->ee_block) - 1, org_path);
131 + if (IS_ERR(org_path)) {
132 + err = PTR_ERR(org_path);
133 + org_path = NULL;
134 + goto out;
136 + err = ext4_ext_insert_extent(handle, org_inode,
137 + org_path, end_ext);
138 + if (err)
139 + goto out;
141 +out:
142 + if (org_path) {
143 + ext4_ext_drop_refs(org_path);
144 + kfree(org_path);
147 + return err;
151 +/**
152 + * ext4_defrag_merge_inside_block - Merge new extent to the extent block
154 + * @o_start: first original extent to be merged
155 + * @o_end: last original extent to be merged
156 + * @start_ext: first new extent to be merged
157 + * @new_ext: middle of new extent to be merged
158 + * @end_ext: last new extent to be merged
159 + * @eh: extent header of target leaf block
160 + * @replaced: the number of blocks which will be replaced with new_ext
161 + * @range_to_move: used to decide how to merge
163 + * This function always returns 0.
164 + */
165 +static int
166 +ext4_defrag_merge_inside_block(struct ext4_extent *o_start,
167 + struct ext4_extent *o_end, struct ext4_extent *start_ext,
168 + struct ext4_extent *new_ext, struct ext4_extent *end_ext,
169 + struct ext4_extent_header *eh, ext4_fsblk_t replaced,
170 + int range_to_move)
172 + int i = 0;
173 + unsigned len;
175 + /* Move the existing extents */
176 + if (range_to_move && o_end < EXT_LAST_EXTENT(eh)) {
177 + len = (unsigned long)(EXT_LAST_EXTENT(eh) + 1) -
178 + (unsigned long)(o_end + 1);
179 + memmove(o_end + 1 + range_to_move, o_end + 1, len);
182 + /* Insert start entry */
183 + if (le16_to_cpu(start_ext->ee_len))
184 + o_start[i++].ee_len = start_ext->ee_len;
186 + /* Insert new entry */
187 + if (le16_to_cpu(new_ext->ee_len)) {
188 + o_start[i].ee_block = new_ext->ee_block;
189 + o_start[i].ee_len = cpu_to_le16(replaced);
190 + ext4_ext_store_pblock(&o_start[i++], ext_pblock(new_ext));
193 + /* Insert end entry */
194 + if (end_ext->ee_len)
195 + o_start[i] = *end_ext;
197 + /* Increment the total entries counter on the extent block */
198 + le16_add_cpu(&eh->eh_entries, range_to_move);
200 + return 0;
203 +/**
204 * ext4_defrag_merge_extents - Merge new extent
206 * @handle: journal handle
207 @@ -155,7 +337,65 @@ ext4_defrag_merge_extents(handle_t *hand
208 struct ext4_extent *start_ext, struct ext4_extent *new_ext,
209 struct ext4_extent *end_ext, ext4_fsblk_t replaced)
211 + struct ext4_extent_header *eh;
212 + unsigned need_slots, slots_range;
213 + int range_to_move, depth, ret;
215 + /*
216 + * The extents need to be inserted
217 + * start_extent + new_extent + end_extent.
218 + */
219 + need_slots = (le16_to_cpu(start_ext->ee_len) ? 1 : 0) +
220 + (le16_to_cpu(end_ext->ee_len) ? 1 : 0) +
221 + (le16_to_cpu(new_ext->ee_len) ? 1 : 0);
223 + /* The number of slots between start and end */
224 + slots_range = ((unsigned long)(o_end + 1) - (unsigned long)o_start + 1)
225 + / sizeof(struct ext4_extent);
227 + /* Range to move the end of extent */
228 + range_to_move = need_slots - slots_range;
229 + depth = org_path->p_depth;
230 + org_path += depth;
231 + eh = org_path->p_hdr;
233 + if (depth) {
234 + /* Register to journal */
235 + ret = ext4_journal_get_write_access(handle, org_path->p_bh);
236 + if (ret)
237 + return ret;
240 + /* Expansion */
241 + if (range_to_move > 0 &&
242 + (range_to_move > le16_to_cpu(eh->eh_max)
243 + - le16_to_cpu(eh->eh_entries))) {
245 + ret = ext4_defrag_merge_across_blocks(handle, org_inode,
246 + o_start, o_end, start_ext, new_ext,
247 + end_ext);
248 + if (ret < 0)
249 + return ret;
250 + } else {
251 + ret = ext4_defrag_merge_inside_block(o_start, o_end,
252 + start_ext, new_ext, end_ext, eh,
253 + replaced, range_to_move);
254 + if (ret < 0)
255 + return ret;
258 + if (depth) {
259 + ret = ext4_journal_dirty_metadata(handle, org_path->p_bh);
260 + if (ret)
261 + return ret;
262 + } else {
263 + ret = ext4_mark_inode_dirty(handle, org_inode);
264 + if (ret < 0)
265 + return ret;
268 return 0;