Btrfs: variable block size support
[deliverable/linux.git] / fs / btrfs / disk-io.c
1 #define _XOPEN_SOURCE 500
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <sys/types.h>
5 #include <sys/stat.h>
6 #include <fcntl.h>
7 #include <unistd.h>
8 #include "kerncompat.h"
9 #include "radix-tree.h"
10 #include "ctree.h"
11 #include "disk-io.h"
12
13 static int allocated_blocks = 0;
14 int cache_max = 10000;
15
16 static int check_tree_block(struct btrfs_root *root, struct btrfs_buffer *buf)
17 {
18 if (buf->blocknr != btrfs_header_blocknr(&buf->node.header))
19 BUG();
20 if (root->node && btrfs_header_parentid(&buf->node.header) !=
21 btrfs_header_parentid(&root->node->node.header))
22 BUG();
23 return 0;
24 }
25
26 static int free_some_buffers(struct btrfs_root *root)
27 {
28 struct list_head *node, *next;
29 struct btrfs_buffer *b;
30 if (root->cache_size < cache_max)
31 return 0;
32 list_for_each_safe(node, next, &root->cache) {
33 b = list_entry(node, struct btrfs_buffer, cache);
34 if (b->count == 1) {
35 BUG_ON(!list_empty(&b->dirty));
36 list_del_init(&b->cache);
37 btrfs_block_release(root, b);
38 if (root->cache_size < cache_max)
39 break;
40 }
41 }
42 return 0;
43 }
44
45 struct btrfs_buffer *alloc_tree_block(struct btrfs_root *root, u64 blocknr)
46 {
47 struct btrfs_buffer *buf;
48 int ret;
49
50 buf = malloc(sizeof(struct btrfs_buffer) + root->blocksize);
51 if (!buf)
52 return buf;
53 allocated_blocks++;
54 buf->blocknr = blocknr;
55 buf->count = 2;
56 INIT_LIST_HEAD(&buf->dirty);
57 free_some_buffers(root);
58 radix_tree_preload(GFP_KERNEL);
59 ret = radix_tree_insert(&root->cache_radix, blocknr, buf);
60 radix_tree_preload_end();
61 list_add_tail(&buf->cache, &root->cache);
62 root->cache_size++;
63 if (ret) {
64 free(buf);
65 return NULL;
66 }
67 return buf;
68 }
69
70 struct btrfs_buffer *find_tree_block(struct btrfs_root *root, u64 blocknr)
71 {
72 struct btrfs_buffer *buf;
73 buf = radix_tree_lookup(&root->cache_radix, blocknr);
74 if (buf) {
75 buf->count++;
76 } else {
77 buf = alloc_tree_block(root, blocknr);
78 if (!buf) {
79 BUG();
80 return NULL;
81 }
82 }
83 return buf;
84 }
85
86 struct btrfs_buffer *read_tree_block(struct btrfs_root *root, u64 blocknr)
87 {
88 loff_t offset = blocknr * root->blocksize;
89 struct btrfs_buffer *buf;
90 int ret;
91
92 buf = radix_tree_lookup(&root->cache_radix, blocknr);
93 if (buf) {
94 buf->count++;
95 } else {
96 buf = alloc_tree_block(root, blocknr);
97 if (!buf)
98 return NULL;
99 ret = pread(root->fp, &buf->node, root->blocksize, offset);
100 if (ret != root->blocksize) {
101 free(buf);
102 return NULL;
103 }
104 }
105 if (check_tree_block(root, buf))
106 BUG();
107 return buf;
108 }
109
110 int dirty_tree_block(struct btrfs_root *root, struct btrfs_buffer *buf)
111 {
112 if (!list_empty(&buf->dirty))
113 return 0;
114 list_add_tail(&buf->dirty, &root->trans);
115 buf->count++;
116 return 0;
117 }
118
119 int clean_tree_block(struct btrfs_root *root, struct btrfs_buffer *buf)
120 {
121 if (!list_empty(&buf->dirty)) {
122 list_del_init(&buf->dirty);
123 btrfs_block_release(root, buf);
124 }
125 return 0;
126 }
127
128 int write_tree_block(struct btrfs_root *root, struct btrfs_buffer *buf)
129 {
130 u64 blocknr = buf->blocknr;
131 loff_t offset = blocknr * root->blocksize;
132 int ret;
133
134 if (buf->blocknr != btrfs_header_blocknr(&buf->node.header))
135 BUG();
136 ret = pwrite(root->fp, &buf->node, root->blocksize, offset);
137 if (ret != root->blocksize)
138 return ret;
139 return 0;
140 }
141
142 static int __commit_transaction(struct btrfs_root *root)
143 {
144 struct btrfs_buffer *b;
145 int ret = 0;
146 int wret;
147 while(!list_empty(&root->trans)) {
148 b = list_entry(root->trans.next, struct btrfs_buffer, dirty);
149 list_del_init(&b->dirty);
150 wret = write_tree_block(root, b);
151 if (wret)
152 ret = wret;
153 btrfs_block_release(root, b);
154 }
155 return ret;
156 }
157
158 static int commit_extent_and_tree_roots(struct btrfs_root *tree_root,
159 struct btrfs_root *extent_root)
160 {
161 int ret;
162 u64 old_extent_block;
163
164 while(1) {
165 old_extent_block = btrfs_root_blocknr(&extent_root->root_item);
166 if (old_extent_block == extent_root->node->blocknr)
167 break;
168 btrfs_set_root_blocknr(&extent_root->root_item,
169 extent_root->node->blocknr);
170 ret = btrfs_update_root(tree_root,
171 &extent_root->root_key,
172 &extent_root->root_item);
173 BUG_ON(ret);
174 }
175 __commit_transaction(extent_root);
176 __commit_transaction(tree_root);
177 return 0;
178 }
179
180 int btrfs_commit_transaction(struct btrfs_root *root,
181 struct btrfs_super_block *s)
182 {
183 int ret = 0;
184 struct btrfs_buffer *snap = root->commit_root;
185 struct btrfs_key snap_key;
186
187 ret = __commit_transaction(root);
188 BUG_ON(ret);
189
190 if (root->commit_root == root->node)
191 return 0;
192
193 memcpy(&snap_key, &root->root_key, sizeof(snap_key));
194 root->root_key.offset++;
195
196 btrfs_set_root_blocknr(&root->root_item, root->node->blocknr);
197 ret = btrfs_insert_root(root->tree_root, &root->root_key,
198 &root->root_item);
199 BUG_ON(ret);
200
201 ret = commit_extent_and_tree_roots(root->tree_root, root->extent_root);
202 BUG_ON(ret);
203
204 write_ctree_super(root, s);
205 btrfs_finish_extent_commit(root->extent_root);
206 btrfs_finish_extent_commit(root->tree_root);
207
208 root->commit_root = root->node;
209 root->node->count++;
210 ret = btrfs_drop_snapshot(root, snap);
211 BUG_ON(ret);
212
213 ret = btrfs_del_root(root->tree_root, &snap_key);
214 BUG_ON(ret);
215
216 return ret;
217 }
218
219 static int __setup_root(struct btrfs_super_block *super,
220 struct btrfs_root *root, u64 objectid, int fp)
221 {
222 INIT_LIST_HEAD(&root->trans);
223 INIT_LIST_HEAD(&root->cache);
224 root->cache_size = 0;
225 root->fp = fp;
226 root->node = NULL;
227 root->commit_root = NULL;
228 root->blocksize = btrfs_super_blocksize(super);
229 root->ref_cows = 0;
230 memset(&root->current_insert, 0, sizeof(root->current_insert));
231 memset(&root->last_insert, 0, sizeof(root->last_insert));
232 memset(&root->root_key, 0, sizeof(root->root_key));
233 memset(&root->root_item, 0, sizeof(root->root_item));
234 return 0;
235 }
236
237 static int find_and_setup_root(struct btrfs_super_block *super,
238 struct btrfs_root *tree_root, u64 objectid,
239 struct btrfs_root *root, int fp)
240 {
241 int ret;
242
243 __setup_root(super, root, objectid, fp);
244 ret = btrfs_find_last_root(tree_root, objectid,
245 &root->root_item, &root->root_key);
246 BUG_ON(ret);
247
248 root->node = read_tree_block(root,
249 btrfs_root_blocknr(&root->root_item));
250 BUG_ON(!root->node);
251 return 0;
252 }
253
254 struct btrfs_root *open_ctree(char *filename, struct btrfs_super_block *super)
255 {
256 struct btrfs_root *root = malloc(sizeof(struct btrfs_root));
257 struct btrfs_root *extent_root = malloc(sizeof(struct btrfs_root));
258 struct btrfs_root *tree_root = malloc(sizeof(struct btrfs_root));
259 int fp;
260 int ret;
261
262 root->extent_root = extent_root;
263 root->tree_root = tree_root;
264
265 extent_root->extent_root = extent_root;
266 extent_root->tree_root = tree_root;
267
268 tree_root->extent_root = extent_root;
269 tree_root->tree_root = tree_root;
270
271 fp = open(filename, O_CREAT | O_RDWR, 0600);
272 if (fp < 0) {
273 free(root);
274 return NULL;
275 }
276 INIT_RADIX_TREE(&root->cache_radix, GFP_KERNEL);
277 INIT_RADIX_TREE(&root->pinned_radix, GFP_KERNEL);
278 INIT_RADIX_TREE(&extent_root->pinned_radix, GFP_KERNEL);
279 INIT_RADIX_TREE(&extent_root->cache_radix, GFP_KERNEL);
280 INIT_RADIX_TREE(&tree_root->pinned_radix, GFP_KERNEL);
281 INIT_RADIX_TREE(&tree_root->cache_radix, GFP_KERNEL);
282
283 ret = pread(fp, super, sizeof(struct btrfs_super_block),
284 BTRFS_SUPER_INFO_OFFSET);
285 if (ret == 0 || btrfs_super_root(super) == 0) {
286 printf("making new FS!\n");
287 ret = mkfs(fp, 0, 1024);
288 if (ret)
289 return NULL;
290 ret = pread(fp, super, sizeof(struct btrfs_super_block),
291 BTRFS_SUPER_INFO_OFFSET);
292 if (ret != sizeof(struct btrfs_super_block))
293 return NULL;
294 }
295 BUG_ON(ret < 0);
296
297 __setup_root(super, tree_root, BTRFS_ROOT_TREE_OBJECTID, fp);
298 tree_root->node = read_tree_block(tree_root, btrfs_super_root(super));
299 BUG_ON(!tree_root->node);
300
301 ret = find_and_setup_root(super, tree_root, BTRFS_EXTENT_TREE_OBJECTID,
302 extent_root, fp);
303 BUG_ON(ret);
304
305 ret = find_and_setup_root(super, tree_root, BTRFS_FS_TREE_OBJECTID,
306 root, fp);
307 BUG_ON(ret);
308
309 root->commit_root = root->node;
310 root->node->count++;
311 root->ref_cows = 1;
312 return root;
313 }
314
315 int write_ctree_super(struct btrfs_root *root, struct btrfs_super_block *s)
316 {
317 int ret;
318 btrfs_set_super_root(s, root->tree_root->node->blocknr);
319 ret = pwrite(root->fp, s, sizeof(*s),
320 BTRFS_SUPER_INFO_OFFSET);
321 if (ret != sizeof(*s)) {
322 fprintf(stderr, "failed to write new super block err %d\n", ret);
323 return ret;
324 }
325 return 0;
326 }
327
328 static int drop_cache(struct btrfs_root *root)
329 {
330 while(!list_empty(&root->cache)) {
331 struct btrfs_buffer *b = list_entry(root->cache.next,
332 struct btrfs_buffer, cache);
333 list_del_init(&b->cache);
334 btrfs_block_release(root, b);
335 }
336 return 0;
337 }
338 int close_ctree(struct btrfs_root *root, struct btrfs_super_block *s)
339 {
340 int ret;
341 btrfs_commit_transaction(root, s);
342 ret = commit_extent_and_tree_roots(root->tree_root, root->extent_root);
343 BUG_ON(ret);
344 write_ctree_super(root, s);
345 drop_cache(root->extent_root);
346 drop_cache(root->tree_root);
347 drop_cache(root);
348 BUG_ON(!list_empty(&root->trans));
349 BUG_ON(!list_empty(&root->extent_root->trans));
350 BUG_ON(!list_empty(&root->tree_root->trans));
351
352 close(root->fp);
353 if (root->node)
354 btrfs_block_release(root, root->node);
355 if (root->extent_root->node)
356 btrfs_block_release(root->extent_root, root->extent_root->node);
357 if (root->tree_root->node)
358 btrfs_block_release(root->tree_root, root->tree_root->node);
359 btrfs_block_release(root, root->commit_root);
360 free(root);
361 printf("on close %d blocks are allocated\n", allocated_blocks);
362 return 0;
363 }
364
365 void btrfs_block_release(struct btrfs_root *root, struct btrfs_buffer *buf)
366 {
367 buf->count--;
368 if (buf->count < 0)
369 BUG();
370 if (buf->count == 0) {
371 BUG_ON(!list_empty(&buf->cache));
372 BUG_ON(!list_empty(&buf->dirty));
373 if (!radix_tree_lookup(&root->cache_radix, buf->blocknr))
374 BUG();
375 radix_tree_delete(&root->cache_radix, buf->blocknr);
376 memset(buf, 0, sizeof(*buf));
377 free(buf);
378 BUG_ON(allocated_blocks == 0);
379 allocated_blocks--;
380 BUG_ON(root->cache_size == 0);
381 root->cache_size--;
382 }
383 }
384
This page took 0.041099 seconds and 6 git commands to generate.