disk-io.c 11.0 KB
Newer Older
C
Chris Mason 已提交
1 2
#include <linux/module.h>
#include <linux/fs.h>
3
#include <linux/blkdev.h>
C
Chris Mason 已提交
4 5
#include <linux/crypto.h>
#include <linux/scatterlist.h>
6 7
#include "ctree.h"
#include "disk-io.h"
8
#include "transaction.h"
9

10

C
Chris Mason 已提交
11
static int check_tree_block(struct btrfs_root *root, struct buffer_head *buf)
12
{
C
Chris Mason 已提交
13
	struct btrfs_node *node = btrfs_buffer_node(buf);
14
	if (buf->b_blocknr != btrfs_header_blocknr(&node->header)) {
15
		BUG();
16
	}
C
Chris Mason 已提交
17
	if (root->node && btrfs_header_parentid(&node->header) !=
18
	    btrfs_header_parentid(btrfs_buffer_header(root->node))) {
19
		BUG();
20
	}
21
	return 0;
22 23
}

24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129
struct buffer_head *btrfs_find_tree_block(struct btrfs_root *root, u64 blocknr)
{
	struct address_space *mapping = root->fs_info->btree_inode->i_mapping;
	int blockbits = root->fs_info->sb->s_blocksize_bits;
	unsigned long index = blocknr >> (PAGE_CACHE_SHIFT - blockbits);
	struct page *page;
	struct buffer_head *bh;
	struct buffer_head *head;
	struct buffer_head *ret = NULL;

	page = find_lock_page(mapping, index);
	if (!page)
		return NULL;

	if (!page_has_buffers(page))
		goto out_unlock;

	head = page_buffers(page);
	bh = head;
	do {
		if (buffer_mapped(bh) && bh->b_blocknr == blocknr) {
			ret = bh;
			get_bh(bh);
			goto out_unlock;
		}
		bh = bh->b_this_page;
	} while (bh != head);
out_unlock:
	unlock_page(page);
	page_cache_release(page);
	return ret;
}

struct buffer_head *btrfs_find_create_tree_block(struct btrfs_root *root,
						 u64 blocknr)
{
	struct address_space *mapping = root->fs_info->btree_inode->i_mapping;
	int blockbits = root->fs_info->sb->s_blocksize_bits;
	unsigned long index = blocknr >> (PAGE_CACHE_SHIFT - blockbits);
	struct page *page;
	struct buffer_head *bh;
	struct buffer_head *head;
	struct buffer_head *ret = NULL;
	u64 first_block = index << (PAGE_CACHE_SHIFT - blockbits);
	page = grab_cache_page(mapping, index);
	if (!page)
		return NULL;

	if (!page_has_buffers(page))
		create_empty_buffers(page, root->fs_info->sb->s_blocksize, 0);
	head = page_buffers(page);
	bh = head;
	do {
		if (!buffer_mapped(bh)) {
			bh->b_bdev = root->fs_info->sb->s_bdev;
			bh->b_blocknr = first_block;
			set_buffer_mapped(bh);
		}
		if (bh->b_blocknr == blocknr) {
			ret = bh;
			get_bh(bh);
			goto out_unlock;
		}
		bh = bh->b_this_page;
		first_block++;
	} while (bh != head);
out_unlock:
	unlock_page(page);
	page_cache_release(page);
	return ret;
}

static sector_t max_block(struct block_device *bdev)
{
	sector_t retval = ~((sector_t)0);
	loff_t sz = i_size_read(bdev->bd_inode);

	if (sz) {
		unsigned int size = block_size(bdev);
		unsigned int sizebits = blksize_bits(size);
		retval = (sz >> sizebits);
	}
	return retval;
}

static int btree_get_block(struct inode *inode, sector_t iblock,
			   struct buffer_head *bh, int create)
{
	if (iblock >= max_block(inode->i_sb->s_bdev)) {
		if (create)
			return -EIO;

		/*
		 * for reads, we're just trying to fill a partial page.
		 * return a hole, they will have to call get_block again
		 * before they can fill it, and they will get -EIO at that
		 * time
		 */
		return 0;
	}
	bh->b_bdev = inode->i_sb->s_bdev;
	bh->b_blocknr = iblock;
	set_buffer_mapped(bh);
	return 0;
}

C
Chris Mason 已提交
130 131
int btrfs_csum_data(struct btrfs_root * root, char *data, size_t len,
		    char *result)
C
Chris Mason 已提交
132 133 134 135 136 137 138 139
{
	struct scatterlist sg;
	struct crypto_hash *tfm = root->fs_info->hash_tfm;
	struct hash_desc desc;
	int ret;

	desc.tfm = tfm;
	desc.flags = 0;
C
Chris Mason 已提交
140
	sg_init_one(&sg, data, len);
C
Chris Mason 已提交
141
	spin_lock(&root->fs_info->hash_lock);
C
Chris Mason 已提交
142
	ret = crypto_hash_digest(&desc, &sg, len, result);
C
Chris Mason 已提交
143 144 145 146
	spin_unlock(&root->fs_info->hash_lock);
	if (ret) {
		printk("sha256 digest failed\n");
	}
C
Chris Mason 已提交
147 148 149 150 151 152 153 154 155 156 157 158 159
	return ret;
}
static int csum_tree_block(struct btrfs_root *root, struct buffer_head *bh,
			   int verify)
{
	char result[BTRFS_CSUM_SIZE];
	int ret;
	struct btrfs_node *node;

	ret = btrfs_csum_data(root, bh->b_data + BTRFS_CSUM_SIZE,
			      bh->b_size - BTRFS_CSUM_SIZE, result);
	if (ret)
		return ret;
C
Chris Mason 已提交
160
	if (verify) {
C
Chris Mason 已提交
161 162 163 164 165 166 167 168 169
		if (memcmp(bh->b_data, result, BTRFS_CSUM_SIZE)) {
			printk("checksum verify failed on %lu\n",
			       bh->b_blocknr);
			return 1;
		}
	} else {
		node = btrfs_buffer_node(bh);
		memcpy(&node->header.csum, result, BTRFS_CSUM_SIZE);
	}
C
Chris Mason 已提交
170 171 172
	return 0;
}

173
static int btree_writepage(struct page *page, struct writeback_control *wbc)
174
{
C
Chris Mason 已提交
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
	struct buffer_head *bh;
	struct btrfs_root *root = btrfs_sb(page->mapping->host->i_sb);
	struct buffer_head *head;

	if (!page_has_buffers(page)) {
		create_empty_buffers(page, root->fs_info->sb->s_blocksize,
					(1 << BH_Dirty)|(1 << BH_Uptodate));
	}
	head = page_buffers(page);
	bh = head;
	do {
		if (buffer_dirty(bh))
			csum_tree_block(root, bh, 0);
		bh = bh->b_this_page;
	} while (bh != head);
190
	return block_write_full_page(page, btree_get_block, wbc);
191 192
}

193
static int btree_readpage(struct file * file, struct page * page)
194
{
195
	return block_read_full_page(page, btree_get_block);
196 197
}

198 199 200 201 202 203
static struct address_space_operations btree_aops = {
	.readpage	= btree_readpage,
	.writepage	= btree_writepage,
	.sync_page	= block_sync_page,
};

C
Chris Mason 已提交
204
struct buffer_head *read_tree_block(struct btrfs_root *root, u64 blocknr)
205
{
206
	struct buffer_head *bh = NULL;
207

208 209 210 211 212 213 214 215 216 217 218
	bh = btrfs_find_create_tree_block(root, blocknr);
	if (!bh)
		return bh;
	lock_buffer(bh);
	if (!buffer_uptodate(bh)) {
		get_bh(bh);
		bh->b_end_io = end_buffer_read_sync;
		submit_bh(READ, bh);
		wait_on_buffer(bh);
		if (!buffer_uptodate(bh))
			goto fail;
C
Chris Mason 已提交
219
		csum_tree_block(root, bh, 1);
220 221 222 223
	} else {
		unlock_buffer(bh);
	}
	if (check_tree_block(root, bh))
C
Chris Mason 已提交
224
		BUG();
225 226 227 228 229
	return bh;
fail:
	brelse(bh);
	return NULL;

230 231
}

232
int dirty_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,
C
Chris Mason 已提交
233
		     struct buffer_head *buf)
234
{
C
Chris Mason 已提交
235
	mark_buffer_dirty(buf);
236 237 238
	return 0;
}

239
int clean_tree_block(struct btrfs_trans_handle *trans, struct btrfs_root *root,
C
Chris Mason 已提交
240
		     struct buffer_head *buf)
241
{
C
Chris Mason 已提交
242
	clear_buffer_dirty(buf);
243 244 245
	return 0;
}

C
Chris Mason 已提交
246
static int __setup_root(struct btrfs_super_block *super,
247 248
			struct btrfs_root *root,
			struct btrfs_fs_info *fs_info,
C
Chris Mason 已提交
249
			u64 objectid)
250
{
C
Chris Mason 已提交
251
	root->node = NULL;
252
	root->commit_root = NULL;
C
Chris Mason 已提交
253 254
	root->blocksize = btrfs_super_blocksize(super);
	root->ref_cows = 0;
255
	root->fs_info = fs_info;
256 257 258 259 260
	memset(&root->root_key, 0, sizeof(root->root_key));
	memset(&root->root_item, 0, sizeof(root->root_item));
	return 0;
}

C
Chris Mason 已提交
261
static int find_and_setup_root(struct btrfs_super_block *super,
262 263 264
			       struct btrfs_root *tree_root,
			       struct btrfs_fs_info *fs_info,
			       u64 objectid,
C
Chris Mason 已提交
265
			       struct btrfs_root *root)
266 267 268
{
	int ret;

C
Chris Mason 已提交
269
	__setup_root(super, root, fs_info, objectid);
270 271 272 273 274 275 276
	ret = btrfs_find_last_root(tree_root, objectid,
				   &root->root_item, &root->root_key);
	BUG_ON(ret);

	root->node = read_tree_block(root,
				     btrfs_root_blocknr(&root->root_item));
	BUG_ON(!root->node);
277 278 279
	return 0;
}

C
Chris Mason 已提交
280 281 282
struct btrfs_root *open_ctree(struct super_block *sb,
			      struct buffer_head *sb_buffer,
			      struct btrfs_super_block *disk_super)
283
{
C
Chris Mason 已提交
284 285 286 287 288 289 290 291 292 293
	struct btrfs_root *root = kmalloc(sizeof(struct btrfs_root),
					  GFP_NOFS);
	struct btrfs_root *extent_root = kmalloc(sizeof(struct btrfs_root),
						 GFP_NOFS);
	struct btrfs_root *tree_root = kmalloc(sizeof(struct btrfs_root),
					       GFP_NOFS);
	struct btrfs_root *inode_root = kmalloc(sizeof(struct btrfs_root),
						GFP_NOFS);
	struct btrfs_fs_info *fs_info = kmalloc(sizeof(*fs_info),
						GFP_NOFS);
294 295
	int ret;

C
Chris Mason 已提交
296
	if (!btrfs_super_root(disk_super)) {
C
Chris Mason 已提交
297
		return NULL;
C
Chris Mason 已提交
298
	}
C
Chris Mason 已提交
299 300
	init_bit_radix(&fs_info->pinned_radix);
	init_bit_radix(&fs_info->pending_del_radix);
301
	sb_set_blocksize(sb, sb_buffer->b_size);
302 303 304 305 306 307 308
	fs_info->running_transaction = NULL;
	fs_info->fs_root = root;
	fs_info->tree_root = tree_root;
	fs_info->extent_root = extent_root;
	fs_info->inode_root = inode_root;
	fs_info->last_inode_alloc = 0;
	fs_info->last_inode_alloc_dirid = 0;
C
Chris Mason 已提交
309 310
	fs_info->disk_super = disk_super;
	fs_info->sb = sb;
311 312 313 314 315
	fs_info->btree_inode = new_inode(sb);
	fs_info->btree_inode->i_ino = 1;
	fs_info->btree_inode->i_size = sb->s_bdev->bd_inode->i_size;
	fs_info->btree_inode->i_mapping->a_ops = &btree_aops;
	mapping_set_gfp_mask(fs_info->btree_inode->i_mapping, GFP_NOFS);
C
Chris Mason 已提交
316
	fs_info->hash_tfm = crypto_alloc_hash("sha256", 0, CRYPTO_ALG_ASYNC);
C
Chris Mason 已提交
317 318 319
	spin_lock_init(&fs_info->hash_lock);

	if (!fs_info->hash_tfm || IS_ERR(fs_info->hash_tfm)) {
C
Chris Mason 已提交
320 321 322
		printk("failed to allocate sha256 hash\n");
		return NULL;
	}
323

C
Chris Mason 已提交
324
	mutex_init(&fs_info->trans_mutex);
C
Chris Mason 已提交
325
	mutex_init(&fs_info->fs_mutex);
326 327
	memset(&fs_info->current_insert, 0, sizeof(fs_info->current_insert));
	memset(&fs_info->last_insert, 0, sizeof(fs_info->last_insert));
328

C
Chris Mason 已提交
329
	__setup_root(disk_super, tree_root, fs_info, BTRFS_ROOT_TREE_OBJECTID);
330 331 332

	fs_info->sb_buffer = read_tree_block(tree_root, sb_buffer->b_blocknr);

C
Chris Mason 已提交
333 334
	if (!fs_info->sb_buffer) {
printk("failed2\n");
335
		return NULL;
C
Chris Mason 已提交
336
	}
337 338 339 340 341
	brelse(sb_buffer);
	sb_buffer = NULL;
	disk_super = (struct btrfs_super_block *)fs_info->sb_buffer->b_data;
	fs_info->disk_super = disk_super;

C
Chris Mason 已提交
342 343
	tree_root->node = read_tree_block(tree_root,
					  btrfs_super_root(disk_super));
344 345
	BUG_ON(!tree_root->node);

C
Chris Mason 已提交
346 347
	ret = find_and_setup_root(disk_super, tree_root, fs_info,
				  BTRFS_EXTENT_TREE_OBJECTID, extent_root);
348 349
	BUG_ON(ret);

C
Chris Mason 已提交
350 351
	ret = find_and_setup_root(disk_super, tree_root, fs_info,
				  BTRFS_INODE_MAP_OBJECTID, inode_root);
352 353
	BUG_ON(ret);

C
Chris Mason 已提交
354 355
	ret = find_and_setup_root(disk_super, tree_root, fs_info,
				  BTRFS_FS_TREE_OBJECTID, root);
356
	BUG_ON(ret);
357
	root->commit_root = root->node;
C
Chris Mason 已提交
358
	get_bh(root->node);
359
	root->ref_cows = 1;
360
	root->fs_info->generation = root->root_key.offset + 1;
361 362 363
	return root;
}

364
int write_ctree_super(struct btrfs_trans_handle *trans, struct btrfs_root
C
Chris Mason 已提交
365
		      *root)
366
{
367 368 369 370 371
	struct buffer_head *bh = root->fs_info->sb_buffer;
	btrfs_set_super_root(root->fs_info->disk_super,
			     root->fs_info->tree_root->node->b_blocknr);
	lock_buffer(bh);
	clear_buffer_dirty(bh);
C
Chris Mason 已提交
372
	csum_tree_block(root, bh, 0);
373 374 375 376 377 378 379
	bh->b_end_io = end_buffer_write_sync;
	get_bh(bh);
	submit_bh(WRITE, bh);
	wait_on_buffer(bh);
	if (!buffer_uptodate(bh)) {
		WARN_ON(1);
		return -EIO;
C
Chris Mason 已提交
380 381 382 383
	}
	return 0;
}

C
Chris Mason 已提交
384
int close_ctree(struct btrfs_root *root)
C
Chris Mason 已提交
385
{
386
	int ret;
387 388
	struct btrfs_trans_handle *trans;

C
Chris Mason 已提交
389 390 391 392 393 394
	trans = btrfs_start_transaction(root, 1);
	btrfs_commit_transaction(trans, root);
	/* run commit again to  drop the original snapshot */
	trans = btrfs_start_transaction(root, 1);
	btrfs_commit_transaction(trans, root);
	ret = btrfs_write_and_wait_transaction(NULL, root);
395
	BUG_ON(ret);
C
Chris Mason 已提交
396
	write_ctree_super(NULL, root);
397

C
Chris Mason 已提交
398
	if (root->node)
C
Chris Mason 已提交
399
		btrfs_block_release(root, root->node);
400 401 402 403 404 405 406 407 408
	if (root->fs_info->extent_root->node)
		btrfs_block_release(root->fs_info->extent_root,
				    root->fs_info->extent_root->node);
	if (root->fs_info->inode_root->node)
		btrfs_block_release(root->fs_info->inode_root,
				    root->fs_info->inode_root->node);
	if (root->fs_info->tree_root->node)
		btrfs_block_release(root->fs_info->tree_root,
				    root->fs_info->tree_root->node);
C
Chris Mason 已提交
409
	btrfs_block_release(root, root->commit_root);
C
Chris Mason 已提交
410
	btrfs_block_release(root, root->fs_info->sb_buffer);
C
Chris Mason 已提交
411
	crypto_free_hash(root->fs_info->hash_tfm);
C
Chris Mason 已提交
412
	truncate_inode_pages(root->fs_info->btree_inode->i_mapping, 0);
413
	iput(root->fs_info->btree_inode);
C
Chris Mason 已提交
414 415 416 417 418
	kfree(root->fs_info->extent_root);
	kfree(root->fs_info->inode_root);
	kfree(root->fs_info->tree_root);
	kfree(root->fs_info);
	kfree(root);
419 420 421
	return 0;
}

C
Chris Mason 已提交
422
void btrfs_block_release(struct btrfs_root *root, struct buffer_head *buf)
423
{
C
Chris Mason 已提交
424
	brelse(buf);
425 426
}