#include <linux/module.h>
#include <linux/fs.h>
#include <linux/blkdev.h>
#include <linux/crypto.h>
#include <linux/scatterlist.h>
#include <linux/swap.h>
#include <linux/radix-tree.h>
#include <linux/writeback.h>
#include "ctree.h"
#include "disk-io.h"
#include "transaction.h"
#include "btrfs_inode.h"
struct dev_lookup {
u64 block_start;
u64 num_blocks;
u64 device_id;
struct block_device *bdev;
};
int btrfs_insert_dev_radix(struct btrfs_root *root,
struct block_device *bdev,
u64 device_id,
u64 block_start,
u64 num_blocks)
{
struct dev_lookup *lookup;
int ret;
lookup = kmalloc(sizeof(*lookup), GFP_NOFS);
if (!lookup)
return -ENOMEM;
lookup->block_start = block_start;
lookup->num_blocks = num_blocks;
lookup->bdev = bdev;
lookup->device_id = device_id;
ret = radix_tree_insert(&root->fs_info->dev_radix, block_start +
num_blocks - 1, lookup);
return ret;
}
u64 bh_blocknr(struct buffer_head *bh)
{
int blkbits = bh->b_page->mapping->host->i_blkbits;
u64 blocknr = bh->b_page->index << (PAGE_CACHE_SHIFT - blkbits);
unsigned long offset;
if (PageHighMem(bh->b_page))
offset = (unsigned long)bh->b_data;
else
offset = bh->b_data - (char *)page_address(bh->b_page);
blocknr += offset >> (PAGE_CACHE_SHIFT - blkbits);
return blocknr;
}
static int check_tree_block(struct btrfs_root *root, struct buffer_head *buf)
{
struct btrfs_node *node = btrfs_buffer_node(buf);
if (bh_blocknr(buf) != btrfs_header_blocknr(&node->header)) {
printk(KERN_CRIT "bh_blocknr(buf) is %Lu, header is %Lu\n",
bh_blocknr(buf), btrfs_header_blocknr(&node->header));
BUG();
}
return 0;
}
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_blocknr(bh) == 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;
}
int btrfs_map_bh_to_logical(struct btrfs_root *root, struct buffer_head *bh,
u64 logical)
{
struct dev_lookup *lookup[2];
int ret;
if (logical == 0) {
bh->b_bdev = NULL;
bh->b_blocknr = 0;
set_buffer_mapped(bh);
return 0;
}
root = root->fs_info->dev_root;
ret = radix_tree_gang_lookup(&root->fs_info->dev_radix,
(void **)lookup,
(unsigned long)logical,
ARRAY_SIZE(lookup));
if (ret == 0 || lookup[0]->block_start > logical ||
lookup[0]->block_start + lookup[0]->num_blocks <= logical) {
ret = -ENOENT;
goto out;
}
bh->b_bdev = lookup[0]->bdev;
bh->b_blocknr = logical - lookup[0]->block_start;
set_buffer_mapped(bh);
ret = 0;
out:
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;
int err;
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)) {