]> git.karo-electronics.de Git - mv-sheeva.git/blob - fs/ocfs2/move_extents.c
Ocfs2/move_extents: move a range of extent.
[mv-sheeva.git] / fs / ocfs2 / move_extents.c
1 /* -*- mode: c; c-basic-offset: 8; -*-
2  * vim: noexpandtab sw=8 ts=8 sts=0:
3  *
4  * move_extents.c
5  *
6  * Copyright (C) 2011 Oracle.  All rights reserved.
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public
10  * License version 2 as published by the Free Software Foundation.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * General Public License for more details.
16  */
17 #include <linux/fs.h>
18 #include <linux/types.h>
19 #include <linux/mount.h>
20 #include <linux/swap.h>
21
22 #include <cluster/masklog.h>
23
24 #include "ocfs2.h"
25 #include "ocfs2_ioctl.h"
26
27 #include "alloc.h"
28 #include "aops.h"
29 #include "dlmglue.h"
30 #include "extent_map.h"
31 #include "inode.h"
32 #include "journal.h"
33 #include "suballoc.h"
34 #include "uptodate.h"
35 #include "super.h"
36 #include "dir.h"
37 #include "buffer_head_io.h"
38 #include "sysfile.h"
39 #include "suballoc.h"
40 #include "refcounttree.h"
41 #include "move_extents.h"
42
43 struct ocfs2_move_extents_context {
44         struct inode *inode;
45         struct file *file;
46         int auto_defrag;
47         int credits;
48         u32 new_phys_cpos;
49         u32 clusters_moved;
50         u64 refcount_loc;
51         struct ocfs2_move_extents *range;
52         struct ocfs2_extent_tree et;
53         struct ocfs2_alloc_context *meta_ac;
54         struct ocfs2_alloc_context *data_ac;
55         struct ocfs2_cached_dealloc_ctxt dealloc;
56 };
57
58 static int __ocfs2_move_extent(handle_t *handle,
59                                struct ocfs2_move_extents_context *context,
60                                u32 cpos, u32 len, u32 p_cpos, u32 new_p_cpos,
61                                int ext_flags)
62 {
63         int ret = 0, index;
64         struct inode *inode = context->inode;
65         struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
66         struct ocfs2_extent_rec *rec, replace_rec;
67         struct ocfs2_path *path = NULL;
68         struct ocfs2_extent_list *el;
69         u64 ino = ocfs2_metadata_cache_owner(context->et.et_ci);
70         u64 old_blkno = ocfs2_clusters_to_blocks(inode->i_sb, p_cpos);
71
72         ret = ocfs2_duplicate_clusters_by_page(handle, context->file, cpos,
73                                                p_cpos, new_p_cpos, len);
74         if (ret) {
75                 mlog_errno(ret);
76                 goto out;
77         }
78
79         memset(&replace_rec, 0, sizeof(replace_rec));
80         replace_rec.e_cpos = cpu_to_le32(cpos);
81         replace_rec.e_leaf_clusters = cpu_to_le16(len);
82         replace_rec.e_blkno = cpu_to_le64(ocfs2_clusters_to_blocks(inode->i_sb,
83                                                                    new_p_cpos));
84
85         path = ocfs2_new_path_from_et(&context->et);
86         if (!path) {
87                 ret = -ENOMEM;
88                 mlog_errno(ret);
89                 goto out;
90         }
91
92         ret = ocfs2_find_path(INODE_CACHE(inode), path, cpos);
93         if (ret) {
94                 mlog_errno(ret);
95                 goto out;
96         }
97
98         el = path_leaf_el(path);
99
100         index = ocfs2_search_extent_list(el, cpos);
101         if (index == -1 || index >= le16_to_cpu(el->l_next_free_rec)) {
102                 ocfs2_error(inode->i_sb,
103                             "Inode %llu has an extent at cpos %u which can no "
104                             "longer be found.\n",
105                             (unsigned long long)ino, cpos);
106                 ret = -EROFS;
107                 goto out;
108         }
109
110         rec = &el->l_recs[index];
111
112         BUG_ON(ext_flags != rec->e_flags);
113         /*
114          * after moving/defraging to new location, the extent is not going
115          * to be refcounted anymore.
116          */
117         replace_rec.e_flags = ext_flags & ~OCFS2_EXT_REFCOUNTED;
118
119         ret = ocfs2_journal_access_di(handle, INODE_CACHE(inode),
120                                       context->et.et_root_bh,
121                                       OCFS2_JOURNAL_ACCESS_WRITE);
122         if (ret) {
123                 mlog_errno(ret);
124                 goto out;
125         }
126
127         ret = ocfs2_split_extent(handle, &context->et, path, index,
128                                  &replace_rec, context->meta_ac,
129                                  &context->dealloc);
130         if (ret) {
131                 mlog_errno(ret);
132                 goto out;
133         }
134
135         ocfs2_journal_dirty(handle, context->et.et_root_bh);
136
137         context->new_phys_cpos = new_p_cpos;
138
139         /*
140          * need I to append truncate log for old clusters?
141          */
142         if (old_blkno) {
143                 if (ext_flags & OCFS2_EXT_REFCOUNTED)
144                         ret = ocfs2_decrease_refcount(inode, handle,
145                                         ocfs2_blocks_to_clusters(osb->sb,
146                                                                  old_blkno),
147                                         len, context->meta_ac,
148                                         &context->dealloc, 1);
149                 else
150                         ret = ocfs2_truncate_log_append(osb, handle,
151                                                         old_blkno, len);
152         }
153
154 out:
155         return ret;
156 }
157
158 /*
159  * lock allocators, and reserving appropriate number of bits for
160  * meta blocks and data clusters.
161  *
162  * in some cases, we don't need to reserve clusters, just let data_ac
163  * be NULL.
164  */
165 static int ocfs2_lock_allocators_move_extents(struct inode *inode,
166                                         struct ocfs2_extent_tree *et,
167                                         u32 clusters_to_move,
168                                         u32 extents_to_split,
169                                         struct ocfs2_alloc_context **meta_ac,
170                                         struct ocfs2_alloc_context **data_ac,
171                                         int extra_blocks,
172                                         int *credits)
173 {
174         int ret, num_free_extents;
175         unsigned int max_recs_needed = 2 * extents_to_split + clusters_to_move;
176         struct ocfs2_super *osb = OCFS2_SB(inode->i_sb);
177
178         num_free_extents = ocfs2_num_free_extents(osb, et);
179         if (num_free_extents < 0) {
180                 ret = num_free_extents;
181                 mlog_errno(ret);
182                 goto out;
183         }
184
185         if (!num_free_extents ||
186             (ocfs2_sparse_alloc(osb) && num_free_extents < max_recs_needed))
187                 extra_blocks += ocfs2_extend_meta_needed(et->et_root_el);
188
189         ret = ocfs2_reserve_new_metadata_blocks(osb, extra_blocks, meta_ac);
190         if (ret) {
191                 mlog_errno(ret);
192                 goto out;
193         }
194
195         if (data_ac) {
196                 ret = ocfs2_reserve_clusters(osb, clusters_to_move, data_ac);
197                 if (ret) {
198                         mlog_errno(ret);
199                         goto out;
200                 }
201         }
202
203         *credits += ocfs2_calc_extend_credits(osb->sb, et->et_root_el,
204                                               clusters_to_move + 2);
205
206         mlog(0, "reserve metadata_blocks: %d, data_clusters: %u, credits: %d\n",
207              extra_blocks, clusters_to_move, *credits);
208 out:
209         if (ret) {
210                 if (*meta_ac) {
211                         ocfs2_free_alloc_context(*meta_ac);
212                         *meta_ac = NULL;
213                 }
214         }
215
216         return ret;
217 }